// ── Undo system ── import { api } from './util.js'; const MAX_STACK = 30; const stack = []; export function pushUndo(label, restoreFn) { stack.push({ label, restore: restoreFn, time: Date.now() }); if (stack.length > MAX_STACK) stack.shift(); showHint(label); } export async function undo() { const entry = stack.pop(); if (!entry) return; await entry.restore(); await window._reloadAll(); showHint(`undo: ${entry.label}`); } export function canUndo() { return stack.length > 0; } export function lastLabel() { return stack.length ? stack[stack.length - 1].label : ''; } // Snapshot helpers export function snapshotCard(card) { return { ...card }; } export function snapshotCards(cards) { return cards.map(c => ({ ...c })); } export function pushCardEdit(label, cardId, prevData) { pushUndo(label, async () => { await api(`api/cards/${cardId}`, { method: 'PATCH', body: prevData }); }); } export function pushCardDelete(label, card) { pushUndo(label, async () => { const created = await api('api/cards', { method: 'POST', body: { title: card.title, body: card.body, color: card.color, bucket_id: card.bucket_id, parent_id: card.parent_id, position: card.position, }}); // Restore fields that POST doesn't accept if (created?.id && card.due_date) { await api(`api/cards/${created.id}`, { method: 'PATCH', body: { due_date: card.due_date } }); } }); } export function pushReorder(label, cards) { const prev = cards.map(c => ({ id: c.id, position: c.position })); pushUndo(label, async () => { await Promise.all(prev.map(p => api(`api/cards/${p.id}`, { method: 'PATCH', body: { position: p.position } }))); }); } export function pushBatch(label, patches) { // patches = [{cardId, prevData}, ...] pushUndo(label, async () => { await Promise.all(patches.map(p => api(`api/cards/${p.cardId}`, { method: 'PATCH', body: p.prevData }))); }); } // Toast hint // // Centering note: tailwind v4 compiles `-translate-x-1/2` to the *new* // CSS `translate` property, which then stacks with any inline `transform` // we set — the box ended up shifted ~ -100% (utility translate + JS // transform translate both applied) and looked left-anchored. Fix: drop // the tailwind translate utility and centre purely via the modern // `translate` property in inline styles, which is one source of truth. let hintTimer = null; function showHint(text) { let el = document.getElementById('undo-hint'); if (!el) { el = document.createElement('div'); el.id = 'undo-hint'; el.className = 'fixed bottom-16 left-1/2 z-[95] bg-t-surface border border-t-border px-4 py-2 text-xs font-pixel text-t-muted shadow-lg transition-all'; el.style.translate = '-50% 0'; document.body.appendChild(el); } el.textContent = text + ' (ctrl+z undo)'; el.style.opacity = '1'; el.style.translate = '-50% 0'; clearTimeout(hintTimer); hintTimer = setTimeout(() => { el.style.opacity = '0'; el.style.translate = '-50% 8px'; }, 2500); }