2026-01-08 22:49:48 -05:00
|
|
|
/**
|
2026-03-17 23:22:24 -04:00
|
|
|
* 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().
|
2026-01-08 22:49:48 -05:00
|
|
|
*/
|
|
|
|
|
|
2026-01-30 19:21:36 -05:00
|
|
|
// 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' });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 22:49:48 -05:00
|
|
|
function showKeyboardHelp() {
|
2026-03-17 23:22:24 -04:00
|
|
|
if (document.getElementById('keyboardHelpModal')) return;
|
2026-01-23 22:04:39 -05:00
|
|
|
|
|
|
|
|
const modal = document.createElement('div');
|
|
|
|
|
modal.id = 'keyboardHelpModal';
|
2026-03-17 23:22:24 -04:00
|
|
|
modal.className = 'lt-modal-overlay';
|
|
|
|
|
modal.setAttribute('aria-hidden', 'true');
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
modal.setAttribute('role', 'dialog');
|
|
|
|
|
modal.setAttribute('aria-modal', 'true');
|
|
|
|
|
modal.setAttribute('aria-labelledby', 'keyboardHelpModalTitle');
|
2026-01-23 22:04:39 -05:00
|
|
|
modal.innerHTML = `
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<div class="lt-modal lt-modal-sm">
|
2026-03-17 23:22:24 -04:00
|
|
|
<div class="lt-modal-header">
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<span class="lt-modal-title" id="keyboardHelpModalTitle">KEYBOARD SHORTCUTS</span>
|
2026-03-17 23:22:24 -04:00
|
|
|
<button class="lt-modal-close" data-modal-close aria-label="Close">✕</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="lt-modal-body">
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<h4 class="kb-section-heading">Navigation</h4>
|
|
|
|
|
<table class="kb-shortcuts-table">
|
|
|
|
|
<tr><td><kbd>J</kbd></td><td>Next ticket in list</td></tr>
|
|
|
|
|
<tr><td><kbd>K</kbd></td><td>Previous ticket in list</td></tr>
|
|
|
|
|
<tr><td><kbd>Enter</kbd></td><td>Open selected ticket</td></tr>
|
|
|
|
|
<tr><td><kbd>G</kbd> then <kbd>D</kbd></td><td>Go to Dashboard</td></tr>
|
2026-03-17 23:22:24 -04:00
|
|
|
</table>
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<h4 class="kb-section-heading">Actions</h4>
|
|
|
|
|
<table class="kb-shortcuts-table">
|
|
|
|
|
<tr><td><kbd>N</kbd></td><td>New ticket</td></tr>
|
|
|
|
|
<tr><td><kbd>C</kbd></td><td>Focus comment box</td></tr>
|
|
|
|
|
<tr><td><kbd>Ctrl/Cmd+E</kbd></td><td>Toggle Edit Mode</td></tr>
|
|
|
|
|
<tr><td><kbd>Ctrl/Cmd+S</kbd></td><td>Save Changes</td></tr>
|
2026-03-17 23:22:24 -04:00
|
|
|
</table>
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<h4 class="kb-section-heading">Quick Status (Ticket Page)</h4>
|
|
|
|
|
<table class="kb-shortcuts-table">
|
|
|
|
|
<tr><td><kbd>1</kbd></td><td>Set Open</td></tr>
|
|
|
|
|
<tr><td><kbd>2</kbd></td><td>Set Pending</td></tr>
|
|
|
|
|
<tr><td><kbd>3</kbd></td><td>Set In Progress</td></tr>
|
|
|
|
|
<tr><td><kbd>4</kbd></td><td>Set Closed</td></tr>
|
2026-03-17 23:22:24 -04:00
|
|
|
</table>
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<h4 class="kb-section-heading">Other</h4>
|
|
|
|
|
<table class="kb-shortcuts-table no-margin">
|
|
|
|
|
<tr><td><kbd>Ctrl/Cmd+K</kbd></td><td>Focus Search</td></tr>
|
|
|
|
|
<tr><td><kbd>ESC</kbd></td><td>Close Modal / Cancel</td></tr>
|
|
|
|
|
<tr><td><kbd>?</kbd></td><td>Show This Help</td></tr>
|
2026-03-17 23:22:24 -04:00
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="lt-modal-footer">
|
Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js
CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes
Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
(opacity-based), so the █ cursor never actually blinked — fixed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
|
|
|
<button class="lt-btn lt-btn-ghost" data-modal-close>CLOSE</button>
|
2026-01-23 22:04:39 -05:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-01-08 22:49:48 -05:00
|
|
|
`;
|
2026-01-23 22:04:39 -05:00
|
|
|
document.body.appendChild(modal);
|
2026-03-17 23:22:24 -04:00
|
|
|
lt.modal.open('keyboardHelpModal');
|
|
|
|
|
}
|
2026-01-30 13:15:55 -05:00
|
|
|
|
2026-03-17 23:22:24 -04:00
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2026-03-17 23:34:59 -04:00
|
|
|
if (!window.lt) return;
|
|
|
|
|
|
2026-03-17 23:22:24 -04:00
|
|
|
// 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'));
|
|
|
|
|
}
|
2026-01-30 13:15:55 -05:00
|
|
|
});
|
2026-03-17 23:22:24 -04:00
|
|
|
|
|
|
|
|
// 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'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|