Fix frontend JS: CSRF resync, status-comment flow, markdown/XSS, kanban
- base.js lt.api: resync window.CSRF_TOKEN from response bodies before throwing on errors and attach err.data/err.status, so a desynced client auto-recovers without a reload - add lt.ticketStatus.submit: status changes that require a comment now prompt, post the comment, and retry update_ticket with it; wired into the ticket dropdown, dashboard quick-status, kanban drag-drop and the 1-4 keyboard shortcuts (bulk ops unchanged) — matches the new server requires_comment enforcement - base.js markdown.render: drop the unsafe marked/markdownit delegation; always use the built-in XSS-safe renderer - ticket.js: XHR upload sends the X-CSRF-Token header and resyncs the token; use lt.escHtml instead of a re-inlined escape chain; @-mention trigger requires a word boundary (no firing inside emails); idempotent, anchor-safe highlightMentions - base.js typeahead: discard out-of-order async results - markdown.js: balanced table tbody/thead; ticket-ref linkification runs after code extraction so #ids inside code aren't linked - dashboard.js kanban: don't swallow the click after a drag - keyboard-shortcuts.js: J/K skip hidden/skeleton rows; drop duplicate ? Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,27 @@
|
||||
|
||||
// Track currently selected row for J/K navigation
|
||||
let currentSelectedRowIndex = -1;
|
||||
let lastNavRowCount = -1;
|
||||
|
||||
// Only navigate real, visible rows — skip skeleton placeholders and rows hidden
|
||||
// by filters/column toggles (offsetParent is null when display:none).
|
||||
function getNavigableRows() {
|
||||
return Array.from(document.querySelectorAll('tbody tr')).filter(function(row) {
|
||||
return !row.classList.contains('lt-skeleton-row') && row.offsetParent !== null;
|
||||
});
|
||||
}
|
||||
|
||||
function navigateTableRow(direction) {
|
||||
const rows = document.querySelectorAll('tbody tr');
|
||||
const rows = getNavigableRows();
|
||||
if (rows.length === 0) return;
|
||||
|
||||
// Reset the index when the row set changes (e.g. filter/reload) so navigation
|
||||
// never lands on a stale/hidden index.
|
||||
if (rows.length !== lastNavRowCount) {
|
||||
currentSelectedRowIndex = -1;
|
||||
lastNavRowCount = rows.length;
|
||||
}
|
||||
|
||||
rows.forEach(row => row.classList.remove('keyboard-selected'));
|
||||
|
||||
if (direction === 'next') {
|
||||
@@ -47,10 +63,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
});
|
||||
|
||||
// ?: Show keyboard shortcuts help — use the static #lt-keys-help modal in the footer
|
||||
lt.keys.on('?', function() {
|
||||
if (window.lt) lt.modal.open('lt-keys-help');
|
||||
});
|
||||
// Note: the '?' help shortcut is registered by lt.keys.initDefaults(); do not
|
||||
// re-bind it here or the help modal opens twice.
|
||||
|
||||
// J: Next row
|
||||
lt.keys.on('j', () => navigateTableRow('next'));
|
||||
|
||||
Reference in New Issue
Block a user