// wizard.js — autosave, insertions, couleur catégorie, génération slug document.addEventListener('DOMContentLoaded', function () { var page = document.getElementById('vl-page'); var uuid = page ? page.dataset.uuid : ''; var autosaveUrl = page ? page.dataset.autosaveUrl : ''; // ─── Auto-resize textarea + scroll curseur ────────────────────────────── var ta = document.getElementById('wz-content'); if (ta) { function resizeTa() { ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; } ta.addEventListener('input', resizeTa); resizeTa(); // Ctrl+Home / Ctrl+End : scroller la fenêtre vers le début/fin du textarea ta.addEventListener('keydown', function (e) { if (!(e.ctrlKey || e.metaKey) || (e.key !== 'Home' && e.key !== 'End')) return; requestAnimationFrame(function () { ta.scrollIntoView({ block: e.key === 'Home' ? 'start' : 'end', behavior: 'smooth' }); }); }); } // ─── Ctrl+Enter soumet le formulaire ──────────────────────────────────── var form = document.querySelector('form[method="POST"]'); if (form) { form.addEventListener('keydown', function (e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { form.submit(); } }); } var slugField = document.getElementById('slug'); // ─── Autosave ──────────────────────────────────────────────────────────── var indicator = document.getElementById('autosave-indicator'); if (indicator && uuid && autosaveUrl) { var timer = null; function scheduleAutosave() { clearTimeout(timer); timer = setTimeout(doAutosave, 3000); } async function doAutosave() { if (!ta) return; indicator.textContent = 'Sauvegarde…'; try { var res = await fetch(autosaveUrl, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ content: ta.value }), }); var data = await res.json(); if (data.ok) { indicator.textContent = 'Sauvegardé à ' + data.time; } else { indicator.textContent = 'Erreur de sauvegarde'; } } catch (err) { indicator.textContent = 'Erreur de sauvegarde'; } } if (ta) ta.addEventListener('input', scheduleAutosave); } // ─── Insertion Markdown depuis miniatures ──────────────────────────────── var insertUrl = page ? page.dataset.insertUrl : ''; document.querySelectorAll('[data-insert-ref]').forEach(function (el) { el.addEventListener('click', function () { if (!ta) return; var ref = this.dataset.insertRef; var isImage = /\.(jpe?g|png|gif|webp|svg|avif)(\?.*)?$/i.test(ref); var md = isImage ? '' : '[' + ref + '](' + ref + ')'; var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + md; ta.focus(); ta.selectionStart = ta.selectionEnd = ta.value.length; ta.dispatchEvent(new Event('input')); }); }); if (insertUrl) { var isImg = /\.(jpe?g|png|gif|webp|svg|avif)(\?.*)?$/i.test(insertUrl); var name = decodeURIComponent(insertUrl.split('/').pop().split('?')[0]) || 'fichier'; var ref = isImg ? '' : '[' + name + '](' + insertUrl + ')'; if (ta) { var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + ref; ta.dispatchEvent(new Event('input')); } } // ─── Copier référence Markdown (bouton MD dans la liste des fichiers) ──── document.querySelectorAll('[data-copy-md-name]').forEach(function (btn) { btn.addEventListener('click', function () { if (!ta) return; var name = this.dataset.copyMdName; var isImage = this.dataset.copyMdIsImage === '1'; var md = isImage ? '' : '[' + name + '](' + name + ')'; var sep = ta.value.length > 0 && !ta.value.endsWith('\n') ? '\n' : ''; ta.value += sep + md; ta.focus(); ta.dispatchEvent(new Event('input')); }); }); // ─── Aperçu couleur catégorie (étape 3) ───────────────────────────────── var KNOWN_CATS = { 'actualité': 10, 'travaux': 35, 'scolaire': 55, 'linux': 120, 'domotique': 160, 'télécom': 190, 'blog': 220, 'informatique': 255, 'réflexion': 285, 'loisirs': 320, 'perso': 345, }; var FREE_HUES = [87, 140, 205, 237, 302]; var catInput = document.getElementById('category'); var catSwatch = document.getElementById('cat-swatch'); var catHint = document.getElementById('cat-hint'); var catSwatches = document.getElementById('cat-free-swatches'); function catHue(name) { var key = name.toLowerCase().trim(); if (KNOWN_CATS[key] !== undefined) return KNOWN_CATS[key]; var h = 0; for (var i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) & 0xffff; return h % 360; } function updateCatSwatch() { if (!catInput || !catSwatch) return; var v = catInput.value.trim(); if (v === '') { catSwatch.style.background = '#e5e7eb'; catSwatch.title = ''; if (catHint) catHint.textContent = ''; } else { var hue = catHue(v); catSwatch.style.background = 'hsl(' + hue + ',55%,52%)'; catSwatch.title = 'hsl(' + hue + ', 55%, 52%)'; if (catHint) { var known = KNOWN_CATS[v.toLowerCase()] !== undefined; catHint.textContent = known ? 'Couleur fixe' : 'Nouvelle catégorie (couleur générée)'; } } } if (catInput) { catInput.addEventListener('input', updateCatSwatch); updateCatSwatch(); if (catSwatches) { FREE_HUES.forEach(function (h) { var sw = document.createElement('span'); sw.style.cssText = 'display:inline-block;width:20px;height:20px;border-radius:4px;cursor:pointer;background:hsl(' + h + ',55%,52%)'; sw.title = 'hsl(' + h + ', 55%, 52%)'; sw.addEventListener('click', function () { // trouver ou créer le nom correspondant catInput.dispatchEvent(new Event('input')); }); catSwatches.appendChild(sw); }); } } // ─── Plan (TOC dynamique) ──────────────────────────────────────────────── var tocList = document.getElementById('wz-toc-list'); if (tocList && ta) { function buildToc() { var lines = ta.value.split('\n'); var items = []; lines.forEach(function (line) { var m = line.match(/^(#{1,6})\s+(.+)/); if (m) { items.push({ level: m[1].length, text: m[2].trim() }); } }); if (items.length === 0) { tocList.innerHTML = '