// Keyboard shortcuts, the shortcuts-help modal, and the mobile nav toggle, // ported from mnw.js:504-572. /** Toggle the keyboard-shortcuts help overlay. Ported from mnw.js:531. */ export function toggleShortcutsHelp(): void { const existing = document.getElementById('shortcuts-help'); if (existing) { existing.remove(); return; } const overlay = document.createElement('div'); overlay.id = 'shortcuts-help'; overlay.className = 'modal-overlay'; overlay.style.display = 'flex'; overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); }; overlay.innerHTML = ''; // Wire the close button via JS, not inline onclick — the CSP has no // script-src 'unsafe-inline' so a generated inline handler is dead. const closeBtn = overlay.querySelector('.modal-close'); if (closeBtn) closeBtn.onclick = () => overlay.remove(); document.body.appendChild(overlay); } /** Install global keyboard shortcuts + the nav toggle. Ported from mnw.js:504. */ export function initKeyboard(): void { document.addEventListener('keydown', (e) => { const tag = document.activeElement?.tagName; const inInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'; if (e.key === 'Escape') { document.querySelector('.modal-overlay')?.remove(); } if ((e.metaKey || e.ctrlKey) && e.key === 's') { e.preventDefault(); const form = document.activeElement?.closest('form'); form?.querySelector('button[type="submit"]')?.click(); } if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) { e.preventDefault(); const searchInput = document.getElementById('header-search-input') as HTMLInputElement | null; if (searchInput) { searchInput.focus(); searchInput.select(); } } if (e.key === '?' && !inInput && !e.metaKey && !e.ctrlKey) { e.preventDefault(); toggleShortcutsHelp(); } }); document.addEventListener('click', (e) => { const toggle = document.getElementById('nav-toggle') as HTMLInputElement | null; if (toggle && toggle.checked && (e.target as Element | null)?.closest('.nav-links a, .nav-links .btn--link')) { toggle.checked = false; } }); }