/** * Keyboard shortcuts for power users. * App-specific shortcuts registered via lt.keys.on() from web_template/base.js. * ESC, Ctrl+K, and ? are handled by lt.keys.initDefaults(). */ // Track currently selected row for J/K navigation let currentSelectedRowIndex = -1; function navigateTableRow(direction) { const rows = document.querySelectorAll('tbody tr'); if (rows.length === 0) return; rows.forEach(row => row.classList.remove('keyboard-selected')); if (direction === 'next') { currentSelectedRowIndex = Math.min(currentSelectedRowIndex + 1, rows.length - 1); } else { currentSelectedRowIndex = Math.max(currentSelectedRowIndex - 1, 0); } const selectedRow = rows[currentSelectedRowIndex]; if (selectedRow) { selectedRow.classList.add('keyboard-selected'); selectedRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } function showKeyboardHelp() { if (document.getElementById('keyboardHelpModal')) return; const modal = document.createElement('div'); modal.id = 'keyboardHelpModal'; modal.className = 'lt-modal-overlay'; modal.setAttribute('aria-hidden', 'true'); modal.innerHTML = `
KEYBOARD SHORTCUTS

Navigation

JNext ticket in list
KPrevious ticket in list
EnterOpen selected ticket
G then DGo to Dashboard

Actions

NNew ticket
CFocus comment box
Ctrl/Cmd+EToggle Edit Mode
Ctrl/Cmd+SSave Changes

Quick Status (Ticket Page)

1Set Open
2Set Pending
3Set In Progress
4Set Closed

Other

Ctrl/Cmd+KFocus Search
ESCClose Modal / Cancel
?Show This Help
`; document.body.appendChild(modal); lt.modal.open('keyboardHelpModal'); } document.addEventListener('DOMContentLoaded', function() { if (!window.lt) return; // Ctrl+E: Toggle edit mode (ticket pages) lt.keys.on('ctrl+e', function() { const editButton = document.getElementById('editButton'); if (editButton) { editButton.click(); lt.toast.info('Edit mode ' + (editButton.classList.contains('active') ? 'enabled' : 'disabled')); } }); // Ctrl+S: Save ticket (ticket pages) lt.keys.on('ctrl+s', function() { const editButton = document.getElementById('editButton'); if (editButton && editButton.classList.contains('active')) { editButton.click(); lt.toast.success('Saving ticket...'); } }); // ?: Show keyboard shortcuts help (lt.keys.initDefaults also handles this, but we override to show our modal) lt.keys.on('?', function() { showKeyboardHelp(); }); // J: Next row lt.keys.on('j', () => navigateTableRow('next')); // K: Previous row lt.keys.on('k', () => navigateTableRow('prev')); // Enter: Open selected ticket lt.keys.on('enter', function() { const selectedRow = document.querySelector('tbody tr.keyboard-selected'); if (selectedRow) { const ticketLink = selectedRow.querySelector('a[href*="/ticket/"]'); if (ticketLink) window.location.href = ticketLink.href; } }); // N: New ticket lt.keys.on('n', function() { const newTicketBtn = document.querySelector('a[href*="/create"]'); if (newTicketBtn) window.location.href = newTicketBtn.href; }); // C: Focus comment box lt.keys.on('c', function() { const commentBox = document.getElementById('newComment'); if (commentBox) { commentBox.focus(); commentBox.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); // G then D: Go to Dashboard (vim-style) lt.keys.on('g', function() { window._pendingG = true; setTimeout(() => { window._pendingG = false; }, 1000); }); lt.keys.on('d', function() { if (window._pendingG) { window._pendingG = false; window.location.href = '/'; } }); // 1-4: Quick status change on ticket page ['1', '2', '3', '4'].forEach(key => { lt.keys.on(key, function() { const statusSelect = document.getElementById('statusSelect'); if (statusSelect && !document.querySelector('.lt-modal-overlay[aria-hidden="false"]')) { const statusMap = { '1': 'Open', '2': 'Pending', '3': 'In Progress', '4': 'Closed' }; const targetStatus = statusMap[key]; const option = Array.from(statusSelect.options).find(opt => opt.value === targetStatus); if (option && !option.disabled) { statusSelect.value = targetStatus; statusSelect.dispatchEvent(new Event('change')); } } }); }); });