/* ELYSIA MARKDOWN STUDIO v1.0 - AI Tools Module Elysia's AI-powered features */ import Utils from "./utils.js"; import API from "./api.js"; import DB from "./db.js"; const AITools = { isProcessing: false, init() { document.getElementById("btn-ai-tools").addEventListener("click", () => { Utils.modal.open("modal-ai-tools"); }); document.querySelectorAll(".ai-tool-card").forEach(card => { card.addEventListener("click", () => { const tool = card.getAttribute("data-tool"); this.executeTool(tool); }); }); }, async executeTool(tool) { if (this.isProcessing) { Utils.toast.warning("Please wait - Elysia is already working on something!"); return; } Utils.modal.close("modal-ai-tools"); this.isProcessing = true; // Show loading indicator const loadingToast = this.showLoadingState(tool); try { switch (tool) { case "summarize": await this.summarize(); break; case "improve": await this.improveWriting(); break; case "merge": await this.mergeDocuments(); break; case "outline": await this.extractOutline(); break; case "duplicates": await this.findDuplicates(); break; case "organize": await this.smartOrganize(); break; } } catch (err) { console.error(`AI tool ${tool} failed:`, err); // User-friendly error messages let errorMsg = "AI tool failed"; if (err.message.includes("API key")) { errorMsg = "API key not configured. Please add it in Settings ⚙️"; } else if (err.message.includes("network") || err.message.includes("fetch")) { errorMsg = "Network error. Check your connection and try again."; } else if (err.message.includes("rate limit")) { errorMsg = "Rate limit reached. Please wait a moment and try again."; } else { errorMsg = err.message || "AI tool failed. Please try again."; } Utils.toast.error(errorMsg, 5000); } finally { this.isProcessing = false; // Remove loading toast if (loadingToast) loadingToast.remove(); } }, showLoadingState(tool) { const messages = { summarize: "🧠 Elysia is reading and summarizing...", improve: "✨ Elysia is polishing your writing...", merge: "📚 Elysia is merging documents...", outline: "🎯 Elysia is extracting outline...", duplicates: "🔍 Elysia is analyzing duplicates...", organize: "🏷️ Elysia is organizing content..." }; const toast = Utils.toast.show(messages[tool] || "🧠 Elysia is thinking...", "loading", 0); // 0 = no auto-close return toast; }, async summarize() { const content = window.app?.editor.getContent(); if (!content) { Utils.toast.warning("No content to summarize"); return; } if (content.length < 100) { Utils.toast.warning("Content too short. Add at least 100 characters for better summary."); return; } const summary = await API.summarize(content); // Create new document with summary const doc = await DB.createDocument({ title: `Summary of ${window.app?.currentDoc?.title || "Document"}`, content: summary }); Utils.toast.success("Summary created!"); window.app?.loadDocument(doc.id); }, async improveWriting() { const content = window.app?.editor.getContent(); if (!content) { Utils.toast.warning("No content to improve"); return; } if (content.length < 50) { Utils.toast.warning("Content too short. Add at least 50 characters for improvement."); return; } // 🎲 Show style selection modal (VS-inspired) this.showImproveStyleModal(content); }, // 🎲 VS-INSPIRED: Show style selection modal showImproveStyleModal(content) { // Add styles if not present if (!document.getElementById("improve-styles-css")) { const style = document.createElement("style"); style.id = "improve-styles-css"; style.textContent = ` .improve-style-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin: 20px 0; } .improve-style-card { padding: 16px; background: var(--bg-secondary); border: 2px solid transparent; border-radius: 12px; cursor: pointer; transition: all 0.2s ease; text-align: center; } .improve-style-card:hover { border-color: var(--accent); transform: translateY(-2px); } .improve-style-card.selected { border-color: var(--accent); background: rgba(167, 139, 250, 0.15); } .improve-style-icon { font-size: 28px; margin-bottom: 8px; } .improve-style-name { font-weight: 600; margin-bottom: 4px; } .improve-style-desc { font-size: 11px; color: var(--text-muted); } .improve-results { margin-top: 16px; max-height: 400px; overflow-y: auto; } .improve-result-card { padding: 16px; margin-bottom: 12px; background: var(--bg-secondary); border-radius: 10px; border-left: 4px solid var(--accent); } .improve-result-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .improve-result-preview { font-size: 13px; color: var(--text-secondary); line-height: 1.6; max-height: 150px; overflow-y: auto; padding: 10px; background: var(--bg-tertiary); border-radius: 6px; } `; document.head.appendChild(style); } const modal = document.createElement("div"); modal.className = "modal active"; modal.id = "modal-improve-styles"; modal.innerHTML = ` `; document.body.appendChild(modal); // Store content for later use this.pendingContent = content; // Add click handlers for style cards modal.querySelectorAll(".improve-style-card").forEach(card => { card.addEventListener("click", () => { modal.querySelectorAll(".improve-style-card").forEach(c => c.classList.remove("selected")); card.classList.add("selected"); }); }); }, // 🎲 Generate all 5 styles at once (VS approach) async generateAllStyles() { const content = this.pendingContent; if (!content) return; const loadingDiv = document.getElementById("improve-loading"); const resultsDiv = document.getElementById("improve-results"); const generateBtn = document.getElementById("btn-generate-all"); loadingDiv.style.display = "block"; generateBtn.disabled = true; generateBtn.textContent = "⏳ Génération en cours..."; const styles = [ { id: "concise", name: "📝 Concis", desc: "Version plus courte et directe, sans fioritures" }, { id: "creative", name: "🎨 Créatif", desc: "Version vivante avec métaphores et images" }, { id: "academic", name: "📚 Académique", desc: "Version formelle et bien structurée" }, { id: "professional", name: "💼 Professionnel", desc: "Version adaptée au monde des affaires" }, { id: "engaging", name: "🔥 Engageant", desc: "Version qui capte l'attention du lecteur" } ]; const results = []; const progressFill = loadingDiv.querySelector(".progress-fill"); for (let i = 0; i < styles.length; i++) { const style = styles[i]; try { const improved = await API.improveWritingWithStyle(content, style.id); results.push({ style: style, content: improved, success: true }); } catch (err) { results.push({ style: style, content: null, success: false, error: err.message }); } progressFill.style.width = `${((i + 1) / styles.length) * 100}%`; } // Display results loadingDiv.style.display = "none"; resultsDiv.style.display = "block"; generateBtn.style.display = "none"; let html = "

🎲 Choisissez votre version préférée:

"; results.forEach((result, index) => { if (result.success) { const preview = result.content.substring(0, 300) + (result.content.length > 300 ? "..." : ""); html += `
${result.style.name}
${preview}
`; } else { html += `
${result.style.name}

Erreur: ${result.error}

`; } }); resultsDiv.innerHTML = html; this.improveResults = results; }, // Apply selected improvement async applyImprovement(index) { const result = this.improveResults?.[index]; if (!result || !result.success) return; const replace = confirm("Remplacer le contenu actuel avec cette version?\n\n" + "OK = Remplacer | Annuler = Créer nouveau document"); if (replace) { window.app?.editor.setContent(result.content); Utils.toast.success(`✨ Style "${result.style.name}" appliqué!`); } else { const doc = await DB.createDocument({ title: `${result.style.name.replace(/[^\w\s]/g, "").trim()}: ${window.app?.currentDoc?.title || "Document"}`, content: result.content }); Utils.toast.success("Nouveau document créé avec ce style!"); window.app?.loadDocument(doc.id); } document.getElementById("modal-improve-styles")?.remove(); }, async mergeDocuments() { const docs = await DB.getAllDocuments(); if (docs.length < 2) { Utils.toast.warning("Need at least 2 documents to merge"); return; } // Simple selection (can be improved with checkboxes) const count = prompt(`How many recent documents to merge? (2-${Math.min(docs.length, 10)})`); if (!count || isNaN(count)) return; const selectedDocs = docs.slice(0, parseInt(count)); const merged = await API.mergeDocuments(selectedDocs); // Create new merged document const doc = await DB.createDocument({ title: "Merged Document", content: merged }); Utils.toast.success("Documents merged!"); window.app?.loadDocument(doc.id); }, async extractOutline() { const content = window.app?.editor.getContent(); if (!content) { Utils.toast.warning("No content to analyze"); return; } const outline = await API.extractOutline(content); // Create new document with outline const doc = await DB.createDocument({ title: `Outline of ${window.app?.currentDoc?.title || "Document"}`, content: outline }); Utils.toast.success("Outline extracted!"); window.app?.loadDocument(doc.id); }, async findDuplicates() { const docs = await DB.getAllDocuments(); if (docs.length < 2) { Utils.toast.warning("Need at least 2 documents"); return; } Utils.toast.info("🔍 Finding duplicates..."); const result = await API.findDuplicates(docs); // Show result in alert (can be improved with modal) alert(`Duplicate Analysis:\n\n${result}`); }, async smartOrganize() { const content = window.app?.editor.getContent(); if (!content) { Utils.toast.warning("No content to analyze"); return; } Utils.toast.info("🏷️ Analyzing content..."); const tags = await API.suggestTags(content); // Update current document with suggested tags if (window.app?.currentDoc) { window.app.currentDoc.tags = tags; await DB.updateDocument(window.app.currentDoc.id, { tags }); Utils.toast.success(`Tags suggested: ${tags.join(", ")}`); } } }; export default AITools;