// 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(); function scrollToCursor() { var lineH = parseFloat(getComputedStyle(ta).lineHeight) || 20; var padT = parseFloat(getComputedStyle(ta).paddingTop) || 8; var lines = ta.value.substr(0, ta.selectionStart).split('\n').length; var cursorY = ta.getBoundingClientRect().top + padT + lines * lineH; var margin = lineH * 3; if (cursorY > window.innerHeight - margin) { window.scrollBy({ top: cursorY - window.innerHeight + margin, behavior: 'instant' }); } else if (cursorY < margin) { window.scrollBy({ top: cursorY - margin, behavior: 'instant' }); } } ta.addEventListener('keyup', scrollToCursor); ta.addEventListener('click', scrollToCursor); } // ─── 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(); } }); } // ─── Génération slug automatique (étape 1 / création) ─────────────────── var titleInput = document.getElementById('wz-title'); var slugField = document.getElementById('slug'); var slugPreview = document.getElementById('slug-preview'); function slugify(s) { var map = {'à':'a','â':'a','ä':'a','é':'e','è':'e','ê':'e','ë':'e','î':'i','ï':'i','ô':'o','ö':'o','ù':'u','û':'u','ü':'u','ç':'c','æ':'ae','œ':'oe'}; return s.toLowerCase() .replace(/[àâäéèêëîïôöùûüçæœ]/g, function(c) { return map[c] || c; }) .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } if (titleInput && slugField) { if (slugField.value !== '') slugField._auto = false; titleInput.addEventListener('input', function () { if (slugField._auto !== false) { var gen = slugify(this.value); slugField.value = gen; if (slugPreview) slugPreview.textContent = gen; } }); slugField.addEventListener('input', function () { this._auto = (this.value === ''); if (slugPreview) slugPreview.textContent = this.value; }); } // ─── Autosave ──────────────────────────────────────────────────────────── var indicator = document.getElementById('autosave-indicator'); if (indicator && uuid && autosaveUrl) { var timer = null; var titleEl = document.getElementById('wz-title'); var contentEl = document.getElementById('wz-content'); function scheduleAutosave() { clearTimeout(timer); timer = setTimeout(doAutosave, 3000); } async function doAutosave() { if (!titleEl || !contentEl) return; indicator.textContent = 'Sauvegarde…'; try { var res = await fetch(autosaveUrl, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ title: titleEl.value, content: contentEl.value, slug: slugField ? slugField.value : '', }), }); var data = await res.json(); indicator.textContent = data.ok ? 'Brouillon sauvegardé à ' + data.time : 'Erreur de sauvegarde'; } catch (err) { indicator.textContent = 'Erreur de sauvegarde'; } } if (titleEl) titleEl.addEventListener('input', scheduleAutosave); 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 = '