feat : versionnage semver, migrations contenu, bandeau mise à jour admin
- CHANGELOG.md : structure semver (1.0.0 / 1.1.0 / 1.2.0) remplace le journal non versionné - public/version.txt : généré à chaque push depuis la première entrée CHANGELOG - scripts/push.sh : extrait la version CHANGELOG avant git add - src/UpdateChecker.php : compare version déployée vs version Gitea (raw file), cache 1 h - templates/layout.php : bandeau alerte admin (nouvelle version / migrations en attente) - templates/admin.php : dashboard moteur Folio (version déployée / disponible) - scripts/migrate_content.php + migration_001 : ajout # titre dans les articles existants - templates/maintenance.php : page HTTP 503 pendant une migration - src/helpers.php : extractMarkdownTitle(), normalisation \r\n dans lineDiff() - templates/wizard/step1.php : suppression champ titre, plan TOC dynamique - public/assets/js/wizard.js : scope titleEl, scrollToCursor, buildToc, handlers externalisés Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+16
-39
@@ -37,40 +37,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
}
|
||||
|
||||
// ─── 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;
|
||||
});
|
||||
}
|
||||
var slugField = document.getElementById('slug');
|
||||
|
||||
// ─── 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');
|
||||
var timer = null;
|
||||
|
||||
function scheduleAutosave() {
|
||||
clearTimeout(timer);
|
||||
@@ -78,27 +50,32 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
|
||||
async function doAutosave() {
|
||||
if (!titleEl || !contentEl) return;
|
||||
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({
|
||||
title: titleEl.value,
|
||||
content: contentEl.value,
|
||||
slug: slugField ? slugField.value : '',
|
||||
}),
|
||||
body: new URLSearchParams({ content: ta.value }),
|
||||
});
|
||||
var data = await res.json();
|
||||
indicator.textContent = data.ok ? 'Brouillon sauvegardé à ' + data.time : 'Erreur de sauvegarde';
|
||||
if (data.ok) {
|
||||
indicator.textContent = 'Sauvegardé à ' + data.time;
|
||||
// Mettre à jour le titre de la page si le serveur l'a extrait
|
||||
var pageTitle = document.getElementById('wz-page-title');
|
||||
if (pageTitle && data.title) {
|
||||
var prefix = pageTitle.dataset.prefix || '';
|
||||
pageTitle.textContent = prefix ? prefix + data.title : data.title;
|
||||
}
|
||||
} else {
|
||||
indicator.textContent = 'Erreur de sauvegarde';
|
||||
}
|
||||
} catch (err) {
|
||||
indicator.textContent = 'Erreur de sauvegarde';
|
||||
}
|
||||
}
|
||||
|
||||
if (titleEl) titleEl.addEventListener('input', scheduleAutosave);
|
||||
if (ta) ta.addEventListener('input', scheduleAutosave);
|
||||
if (ta) ta.addEventListener('input', scheduleAutosave);
|
||||
}
|
||||
|
||||
// ─── Insertion Markdown depuis miniatures ────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user