diff --git a/frontend/js/ai.js b/frontend/js/ai.js index 6d763e9..98bc72a 100644 --- a/frontend/js/ai.js +++ b/frontend/js/ai.js @@ -96,6 +96,8 @@ function createMenu(items, parentEl) { fontSize: '0.8rem', color: 'var(--text-primary)', whiteSpace: 'nowrap', + background: 'transparent', + transition: 'background 0.1s', }); el.innerHTML = item.label; if (item.hint) { @@ -230,35 +232,41 @@ export async function createAIToolbar(container, getEditorView) { padding: '3px 8px', borderRadius: '4px', }); - aiBtn.title = 'AI Inline Completion (Ctrl+J)'; + aiBtn.title = 'AI Inline Completion (Ctrl+J) — Complète le texte automatiquement'; aiBtn.addEventListener('click', async () => { const v = ev(); if (!v) return; const text = getSelection(v); if (!text.trim()) return; + const origHTML = aiBtn.innerHTML; + aiBtn.innerHTML = '⏳ AI...'; + aiBtn.disabled = true; try { const result = await aiAction('inline-complete', text); replaceSelection(v, result, 'append'); showToast('AI: complétion ajoutée', 'success'); } catch (e) { showToast('AI: ' + (String(e.message || e).includes('401') ? 'clé API invalide' : e.message), 'error'); + } finally { + aiBtn.innerHTML = origHTML; + aiBtn.disabled = false; } }); // ── Edit menu ── const editBtn = createDropdownBtn('Éditer', [ - { label: '🪄 Improve writing', action: () => action('improve') }, - { label: '🔤 Fix spelling & grammar', action: () => action('fix-spelling') }, - { label: '📏 Make shorter', action: () => action('make-shorter') }, - { label: '📐 Make longer', action: () => action('make-longer') }, - { label: '📋 Simplify language', action: () => action('simplify') }, - ]); + { label: '🪄 Improve writing', action: () => action('improve'), hint: 'Améliore la qualité du texte' }, + { label: '🔤 Fix spelling & grammar', action: () => action('fix-spelling'), hint: 'Corrige les fautes' }, + { label: '📏 Make shorter', action: () => action('make-shorter'), hint: 'Rend le texte plus concis' }, + { label: '📐 Make longer', action: () => action('make-longer'), hint: 'Ajoute des détails' }, + { label: '📋 Simplify language', action: () => action('simplify'), hint: 'Simplifie le langage' }, + ], 'Modifie le texte sélectionné'); // ── Tone menu ── const toneBtn = createDropdownBtn('Ton', [ { label: '💼 Professional tone', action: () => action('tone', { tone: 'professional' }) }, { label: '💬 Casual tone', action: () => action('tone', { tone: 'casual' }) }, - ]); + ], 'Change le ton du texte'); // ── Translate menu ── const translateBtn = createDropdownBtn('Traduire', [ @@ -268,22 +276,23 @@ export async function createAIToolbar(container, getEditorView) { { label: '🇩🇪 German', action: () => action('translate', { target_lang: 'German' }) }, { label: '🇫🇷 French', action: () => action('translate', { target_lang: 'French' }) }, { label: '🇪🇸 Spanish', action: () => action('translate', { target_lang: 'Spanish' }) }, - ]); + ], 'Traduit le texte sélectionné'); // ── Generate menu ── const genBtn = createDropdownBtn('Générer', [ { label: 'ℹ️ Explain this', action: () => action('explain', {}, 'append') }, { label: '📝 Summarize', action: () => action('summarize', {}, 'append') }, { label: '✏️ Continue writing', action: () => action('continue', {}, 'append') }, - ]); + ], 'Génère du contenu à partir de la sélection'); // ── Custom Rewrite ── const rewriteBtn = document.createElement('button'); rewriteBtn.className = 'ai-toolbar-btn'; rewriteBtn.innerHTML = '💬 Réécrire'; + rewriteBtn.title = 'Réécrit le texte selon vos instructions'; Object.assign(rewriteBtn.style, { fontSize: '0.7rem', - background: 'none', + background: 'transparent', border: 'none', cursor: 'pointer', padding: '3px 6px', @@ -311,7 +320,7 @@ export async function createAIToolbar(container, getEditorView) { { label: '📊 Convert to table', action: () => action('to-table') }, { label: '⚙️ Generate frontmatter', action: () => action('frontmatter', {}, 'before') }, { label: '🔷 Convert to canvas', action: () => action('to-canvas', {}, 'append') }, - ]); + ], 'Outils de conversion'); toolbar.appendChild(aiBtn); toolbar.appendChild(createSeparator()); @@ -363,10 +372,11 @@ export async function createAIToolbar(container, getEditorView) { return toolbar; } -function createDropdownBtn(text, items) { +function createDropdownBtn(text, items, tooltip = '') { const btn = document.createElement('button'); btn.className = 'ai-toolbar-btn'; btn.innerHTML = text + ' ▾'; + btn.title = tooltip || text; Object.assign(btn.style, { position: 'relative', fontSize: '0.7rem', diff --git a/frontend/js/utils.js b/frontend/js/utils.js index 22d835c..76a8c48 100644 --- a/frontend/js/utils.js +++ b/frontend/js/utils.js @@ -319,7 +319,7 @@ async function openEditor(vaultName, filePath) { EditorView.updateListener.of((update) => { if (update.docChanged) { clearTimeout(window._obsigateAutoSaveTimer); - window._obsigateAutoSaveTimer = setTimeout(() => saveFile(), 2000); + window._obsigateAutoSaveTimer = setTimeout(() => saveFile(true), 2000); } }), ]; @@ -417,17 +417,19 @@ function closeEditor() { state.editorPath = null; } -async function saveFile() { +async function saveFile(silent = false) { if ((!state.editorView && !state.fallbackEditorEl) || !state.editorVault || !state.editorPath) return; const content = state.editorView ? state.editorView.state.doc.toString() : state.fallbackEditorEl.value; const saveBtn = document.getElementById("editor-save"); - const originalHTML = saveBtn.innerHTML; + const originalHTML = saveBtn ? saveBtn.innerHTML : ''; try { - saveBtn.disabled = true; - saveBtn.innerHTML = ''; - safeCreateIcons(); + if (saveBtn && !silent) { + saveBtn.disabled = true; + saveBtn.innerHTML = ''; + safeCreateIcons(); + } const response = await fetch(`/api/file/${encodeURIComponent(state.editorVault)}/save?path=${encodeURIComponent(state.editorPath)}`, { method: "PUT", @@ -440,17 +442,28 @@ async function saveFile() { throw new Error(error.detail || "Erreur de sauvegarde"); } - saveBtn.innerHTML = ''; - safeCreateIcons(); - - const _savedVault = state.editorVault; - const _savedPath = state.editorPath; - setTimeout(() => { - closeEditor(); - if (state.currentVault === _savedVault && state.currentPath === _savedPath) { - openFile(_savedVault, _savedPath); + if (silent) { + // Auto-save: brief visual feedback + if (saveBtn) { + saveBtn.innerHTML = ''; + safeCreateIcons(); + setTimeout(() => { + if (saveBtn) saveBtn.innerHTML = originalHTML; + }, 1500); } - }, 800); + } else { + saveBtn.innerHTML = ''; + safeCreateIcons(); + + const _savedVault = state.editorVault; + const _savedPath = state.editorPath; + setTimeout(() => { + closeEditor(); + if (state.currentVault === _savedVault && state.currentPath === _savedPath) { + openFile(_savedVault, _savedPath); + } + }, 800); + } } catch (err) { console.error("Save error:", err); alert(`Erreur: ${err.message}`);