/** * Backup & Diff viewer module for ObsiGate. * * Provides a modal UI to browse file backups, compare versions with * a unified diff view, and restore previous versions. * * Usage: * import { openBackupViewer } from './backups.js'; * openBackupViewer(vaultName, filePath); */ import { api } from './auth.js'; import { showToast } from './ui.js'; import { refreshSidebarTreePreservingState } from './sidebar.js'; import { safeCreateIcons } from './utils.js'; // --------------------------------------------------------------------------- // State // --------------------------------------------------------------------------- const state = { vault: null, path: null, backups: [], overlay: null, leftVersion: null, // timestamp of backup shown on left rightVersion: null, // timestamp of backup shown on right, or null for current diffText: '', }; // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- /** * Open the backup/diff viewer for a file. * @param {string} vault - Vault name * @param {string} path - Relative file path */ export async function openBackupViewer(vault, path) { state.vault = vault; state.path = path; state.backups = []; state.leftVersion = null; state.rightVersion = null; state.diffText = ''; // Create and show overlay const overlay = createOverlay(); document.body.appendChild(overlay); state.overlay = overlay; requestAnimationFrame(() => overlay.classList.add('active')); // Show loading state setBody(renderLoading()); // Fetch backups try { const data = await api(`/api/file/${encodeURIComponent(vault)}/backups?path=${encodeURIComponent(path)}`); state.backups = data.backups || []; } catch (err) { setBody(renderError('Failed to load backups')); return; } if (state.backups.length === 0) { setBody(renderEmpty()); return; } // Auto-select: left = most recent, right = current state.leftVersion = state.backups[0].timestamp; state.rightVersion = null; // Render version selectors renderMainView(); // Load initial diff await loadDiff(); } // --------------------------------------------------------------------------- // DOM helpers // --------------------------------------------------------------------------- function createOverlay() { const overlay = document.createElement('div'); overlay.className = 'obsigate-modal-overlay backup-viewer-overlay'; overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); }); const panel = document.createElement('div'); panel.className = 'backup-viewer-panel'; panel.id = 'backup-viewer-panel'; overlay.appendChild(panel); return overlay; } function setBody(html) { const panel = document.getElementById('backup-viewer-panel'); if (panel) panel.innerHTML = html; } function close() { if (state.overlay) { state.overlay.classList.remove('active'); setTimeout(() => { if (state.overlay) state.overlay.remove(); state.overlay = null; }, 200); } } // --------------------------------------------------------------------------- // Rendering // --------------------------------------------------------------------------- function renderLoading() { return `
Chargement des backups...
${escapeHtml(msg)}
Aucun backup disponible pour ce fichier.
Les backups sont créés automatiquement avant chaque modification.
| ${prefix} | ${escapeHtml(line)} |