diff --git a/public/index.html b/public/index.html
index cff25e9..8f7de63 100644
--- a/public/index.html
+++ b/public/index.html
@@ -417,7 +417,7 @@ window.dispatchEvent(new Event('tiptap-loaded'));
.binder-scene.active .scene-words { color: rgba(255,255,255,0.6); }
.editor-pane { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
- .editor-header { padding: 20px 32px 12px; border-bottom: 1px solid var(--border-light); }
+ .editor-header { padding: 20px 32px 12px; border-bottom: 1px solid var(--border-light); display: flex; align-items: flex-start; gap: 12px; }
.editor-scene-title { font-family: 'Playfair Display', serif; font-size: 24px; font-weight: 600; color: var(--text); }
.editor-scene-meta { font-size: 13px; color: var(--text-hint); margin-top: 4px; display: flex; gap: 16px; }
.editor-body { flex: 1; overflow-y: auto; padding: 24px 32px 80px; max-width: 720px; }
@@ -468,6 +468,26 @@ window.dispatchEvent(new Event('tiptap-loaded'));
.dark .comment-mark.comment-e { background: rgba(138,170,90,0.2); border-bottom-color: rgba(138,170,90,0.4); }
.dark .comment-mark.comment-m { background: rgba(212,132,90,0.2); border-bottom-color: rgba(212,132,90,0.4); }
+ /* ── Version history panel ── */
+ .version-panel { position: fixed; top: 0; right: -420px; width: 400px; height: 100vh; background: var(--bg-card); border-left: 1px solid var(--border); z-index: 250; transition: right 0.3s ease; display: flex; flex-direction: column; box-shadow: -4px 0 20px var(--shadow); }
+ .version-panel.open { right: 0; }
+ .version-panel-header { padding: 20px 24px 16px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
+ .version-panel-header h3 { font-family: 'Playfair Display', serif; font-size: 16px; font-weight: 600; }
+ .version-panel-list { flex: 1; overflow-y: auto; padding: 12px; }
+ .version-item { padding: 14px 16px; border: 1px solid var(--border-light); border-radius: 10px; margin-bottom: 10px; cursor: pointer; transition: border-color 0.2s; }
+ .version-item:hover { border-color: var(--accent); }
+ .version-item.selected { border-color: var(--accent); background: var(--bg-alt); }
+ .version-item-date { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
+ .version-item-label { font-size: 14px; font-weight: 500; margin-bottom: 6px; }
+ .version-item-preview { font-size: 12px; color: var(--text-hint); line-height: 1.5; max-height: 60px; overflow: hidden; }
+ .version-preview-pane { padding: 16px 24px; border-top: 1px solid var(--border-light); max-height: 40vh; overflow-y: auto; }
+ .version-preview-pane h4 { font-family: 'Playfair Display', serif; font-size: 14px; margin-bottom: 10px; color: var(--text-muted); }
+ .version-preview-content { font-size: 13px; line-height: 1.6; }
+ .version-actions { padding: 12px 24px 20px; border-top: 1px solid var(--border-light); display: flex; gap: 10px; }
+ .version-actions button { flex: 1; }
+ .btn-history { background: none; border: 1px solid var(--border); padding: 6px 14px; border-radius: 8px; font-family: 'Lora', serif; font-size: 13px; color: var(--text-muted); cursor: pointer; transition: all 0.2s; }
+ .btn-history:hover { border-color: var(--accent); color: var(--text); }
+
.comment-popup { position: fixed; z-index: 300; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 14px 18px; box-shadow: 0 4px 20px var(--shadow-hover); max-width: 320px; min-width: 200px; }
.comment-popup-author { font-size: 12px; font-weight: 600; margin-bottom: 4px; }
.comment-popup-author.e { color: var(--olive); }
@@ -668,8 +688,11 @@ window.dispatchEvent(new Event('tiptap-loaded'));
Choisir une scène dans le Binder
@@ -793,6 +816,7 @@ window.dispatchEvent(new Event('tiptap-loaded'));
+
@@ -1290,6 +1314,7 @@ function openNew() {
document.getElementById('f-lien').value = '';
document.querySelectorAll('#f-perso .ms-tag').forEach(t => t.classList.remove('selected'));
document.getElementById('btn-delete').style.display = 'none';
+ document.getElementById('btn-history-frise').style.display = 'none';
document.getElementById('card-comments-section').style.display = 'none';
document.getElementById('card-comment-list').innerHTML = '';
document.getElementById('card-comment-input').value = '';
@@ -1317,6 +1342,7 @@ function openEdit(id) {
});
document.getElementById('btn-delete').style.display = 'block';
+ document.getElementById('btn-history-frise').style.display = '';
// Comments
const comments = parseComments(r.Commentaires);
@@ -2665,6 +2691,7 @@ async function openScene(scene) {
if (scene.Tags) meta.push(scene.Tags);
if (scene.Personnage) meta.push(scene.Personnage);
document.getElementById('editor-meta').textContent = meta.join(' · ');
+ document.getElementById('btn-history-redaction').style.display = '';
// Panneau fiche (intention)
buildFichePanel(scene);
@@ -3030,6 +3057,146 @@ document.addEventListener('click', (e) => {
closeCommentPopup();
}
});
+// ── Version history ──
+let versionType = null;
+let versionRecordId = null;
+let versionList = [];
+let selectedVersionId = null;
+
+async function openVersionPanel(type, recordId) {
+ if (!recordId) return;
+ versionType = type;
+ versionRecordId = recordId;
+ selectedVersionId = null;
+ document.getElementById('version-panel-title').textContent = type === 'frise' ? 'Historique — Fiche' : 'Historique — Texte';
+ document.getElementById('version-list').innerHTML = '
Chargement...
';
+ document.getElementById('version-preview').style.display = 'none';
+ document.getElementById('version-actions').style.display = 'none';
+ document.getElementById('version-panel').classList.add('open');
+
+ try {
+ const r = await fetch(`/api/versions/${type}/${recordId}`);
+ const data = await r.json();
+ versionList = data.list || [];
+ renderVersionList();
+ } catch (e) {
+ document.getElementById('version-list').innerHTML = '
Erreur de chargement
';
+ }
+}
+
+function closeVersionPanel() {
+ document.getElementById('version-panel').classList.remove('open');
+ versionType = null;
+ versionRecordId = null;
+ selectedVersionId = null;
+}
+
+function renderVersionList() {
+ const container = document.getElementById('version-list');
+ if (versionList.length === 0) {
+ container.innerHTML = '
Aucune version enregistrée pour le moment.
Les versions seront créées automatiquement à chaque modification.
';
+ return;
+ }
+ container.innerHTML = '';
+ for (const v of versionList) {
+ const div = document.createElement('div');
+ div.className = 'version-item';
+ div.dataset.id = v.Id;
+ const date = new Date(v.CreatedAt);
+ const dateStr = date.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' });
+ let preview = '';
+ try {
+ const snap = JSON.parse(v.Snapshot);
+ if (versionType === 'frise') {
+ preview = [snap.Title, snap['Résumé']].filter(Boolean).join(' — ');
+ } else {
+ const tmp = document.createElement('div');
+ tmp.innerHTML = snap.Texte || '';
+ preview = tmp.textContent.substring(0, 150);
+ }
+ } catch (e) { preview = ''; }
+ const label = v.Label || '';
+ div.innerHTML = `
${dateStr}
` +
+ (label ? `
${label.replace(/` : '') +
+ (preview ? `
${preview.replace(/` : '');
+ div.onclick = () => selectVersion(v.Id);
+ container.appendChild(div);
+ }
+}
+
+function selectVersion(id) {
+ selectedVersionId = id;
+ document.querySelectorAll('.version-item').forEach(el => el.classList.toggle('selected', Number(el.dataset.id) === id));
+ const v = versionList.find(x => x.Id === id);
+ if (!v) return;
+ try {
+ const snap = JSON.parse(v.Snapshot);
+ const previewPane = document.getElementById('version-preview');
+ const content = document.getElementById('version-preview-content');
+ if (versionType === 'frise') {
+ const parts = [];
+ if (snap.Title) parts.push(`
${snap.Title.replace(/`);
+ if (snap.Acte) parts.push(`${snap.Acte}`);
+ if (snap['Résumé']) parts.push(snap['Résumé'].replace(/Intérêt : ' + snap['Intérêt narratif'].replace(/Tags : ' + snap.Tags.replace(/');
+ content.innerHTML = parts.join('
');
+ } else {
+ content.innerHTML = DOMPurify.sanitize(snap.Texte || 'Texte vide');
+ }
+ previewPane.style.display = '';
+ document.getElementById('version-actions').style.display = '';
+ } catch (e) {
+ document.getElementById('version-preview').style.display = 'none';
+ }
+}
+
+async function restoreVersion() {
+ if (!selectedVersionId) return;
+ if (!confirm('Restaurer cette version ? L\u2019état actuel sera sauvegardé avant la restauration.')) return;
+ try {
+ const r = await fetch(`/api/versions/${selectedVersionId}/restore`, { method: 'POST' });
+ if (r.ok) {
+ closeVersionPanel();
+ if (versionType === 'frise') {
+ await loadData();
+ if (editingId) openEdit(editingId);
+ } else if (versionType === 'redaction' && currentSceneId) {
+ const scene = records.find(rec => rec.Id === currentSceneId);
+ if (scene) openScene(scene);
+ }
+ } else {
+ alert('Erreur lors de la restauration');
+ }
+ } catch (e) {
+ alert('Erreur : ' + e.message);
+ }
+}
+
+// Close popup on version panel click outside
+document.addEventListener('click', (e) => {
+ const panel = document.getElementById('version-panel');
+ if (panel.classList.contains('open') && !panel.contains(e.target) && !e.target.closest('.btn-history')) {
+ closeVersionPanel();
+ }
+});
+
+
+
+
+
+
+
+
+
+
+