diff --git a/config.php b/config.php index a9650f6..4995867 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ gBoard() && !!document.querySelect }}); // Delete selected cards registerShortcut({ match: (e) => (e.key === 'Delete' || e.key === 'Backspace') && !e.ctrlKey && gBoard() && getSelectedIds().length > 0, section: 'global', keys: 'Del', label: 'delete selected', hidden: true, handler: async () => { - // Delegated to board.js's existing handler via a synthetic event is - // impractical; re-implement the small logic here to avoid coupling. const ids = getSelectedIds(); if (localStorage.getItem('hyprcrd-confirm-delete') !== '0') { const ok = await showDialog({ title: 'delete cards', message: `Delete ${ids.length} card${ids.length > 1 ? 's' : ''}?`, danger: true, okLabel: 'Delete' }); if (!ok) return; } + const { animateCardRemoval } = await import('./modules/board.js'); + await animateCardRemoval(ids); for (const id of ids) await api(`api/cards/${id}`, { method: 'DELETE' }); clearSelection(); await window._reloadAll(); @@ -394,7 +394,7 @@ document.getElementById('quick-capture-input')?.addEventListener('keydown', asyn body = lines.slice(1).join('\n').trim(); } if (!cardTitle) return; - if (!body.includes('#inbox')) body = (body ? body + '\n\n' : '') + '#inbox'; + // No auto-tag — the card goes into the active bucket (or unsorted). // Place the card in the currently active bucket (if it's a real // user bucket). Special filters (all/current/favorites/journal/none) diff --git a/public/js/modules/board.js b/public/js/modules/board.js index 57864a3..739a8e7 100644 --- a/public/js/modules/board.js +++ b/public/js/modules/board.js @@ -38,6 +38,18 @@ const draggingCardIds = new Set(); // the all view) or wait for the next visit to the all tab to pulse. let knownCardIds = null; +/** Animate card removal: adds .card-removing to each matching card + * element and waits for the CSS animation to finish before resolving. + * Call this BEFORE the API delete + _reloadAll so the user sees the + * card shrink/fade out instead of just vanishing. */ +export function animateCardRemoval(ids) { + if (!ids || !ids.length) return Promise.resolve(); + const els = ids.map(id => board.querySelector(`[data-id="${id}"]`)).filter(Boolean); + if (!els.length) return Promise.resolve(); + for (const el of els) el.classList.add('card-removing'); + return new Promise(r => setTimeout(r, 260)); +} + /** Add a pulse class to matching board cards and strip it after the * animation. `cls` controls the variant: 'card-pulse-new' (3×, for * relay-synced cards) or 'card-pulse-local' (1×, for cards the user diff --git a/public/js/modules/context-menu.js b/public/js/modules/context-menu.js index 16c43e0..93f06c7 100644 --- a/public/js/modules/context-menu.js +++ b/public/js/modules/context-menu.js @@ -397,7 +397,9 @@ export function showContextMenu(x, y, card) { } } }); - // Delete children first, then parent (avoids orphan flicker) + // Animate out, then delete children first, then parent + const { animateCardRemoval } = await import('./board.js'); + await animateCardRemoval(allCards.map(c => c.id)); for (const c of children) await api(`api/cards/${c.id}`, { method: 'DELETE' }); await api(`api/cards/${card.id}`, { method: 'DELETE' }); await window._reloadAll(); diff --git a/public/js/modules/theme.js b/public/js/modules/theme.js index 78ea6b2..a1cbfef 100644 --- a/public/js/modules/theme.js +++ b/public/js/modules/theme.js @@ -258,6 +258,14 @@ function refreshPresetSelect() { sel.appendChild(userGroup); } if (cur && sel.querySelector(`option[value="${CSS.escape(cur)}"]`)) sel.value = cur; + // Disable delete button when a built-in preset is selected + const delBtn = $('design-preset-delete'); + if (delBtn) { + const isUser = sel.value?.startsWith('user:'); + delBtn.disabled = !isUser; + delBtn.style.opacity = isUser ? '' : '0.35'; + delBtn.style.cursor = isUser ? '' : 'not-allowed'; + } } function getSelectedPreset() {