feat(dashboard): fix Ctrl+K shortcut, debounce search queries, and add request cancellation

This commit is contained in:
Riz Ashraf committed 2026-10-02 07:27:31 +01:00
1 parent 83aa26634d
commit 87ddb01063
1 file changed
+65 -5
+65 -5
View File
@@ -972,19 +972,34 @@
return text.replace(regex, '<span class="search-highlight">$1</span>'); return text.replace(regex, '<span class="search-highlight">$1</span>');
} }
let searchDebounceTimer = null;
let activeSearchAbortController = null;
async function handleSearch(e) { async function handleSearch(e) {
const q = e.target.value; const q = e.target.value.trim();
// Provide live feedback if they type at least 3 characters, or hit Enter
if (e.key === 'Enter' || q.length >= 3 || q.length === 0) { if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
if (!q) { if (!q) {
if (activeSearchAbortController) activeSearchAbortController.abort();
document.getElementById('search-results').innerHTML = ''; document.getElementById('search-results').innerHTML = '';
return; return;
} }
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
const delay = (e.key === 'Enter') ? 0 : 250;
searchDebounceTimer = setTimeout(async () => {
if (activeSearchAbortController) activeSearchAbortController.abort();
activeSearchAbortController = new AbortController();
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, {
signal: activeSearchAbortController.signal
});
const data = await res.json(); const data = await res.json();
const container = document.getElementById('search-results'); const container = document.getElementById('search-results');
if (data.results.length === 0) { if (!data.results || data.results.length === 0) {
container.innerHTML = '<div style="padding:15px;color:var(--text-secondary);">No results found.</div>'; container.innerHTML = '<div style="padding:15px;color:var(--text-secondary);">No results found.</div>';
return; return;
} }
@@ -1002,8 +1017,13 @@
<div style="font-size:0.75em; color:var(--text-secondary); margin-top:8px; font-family:monospace; background: var(--canvas-bg); padding: 2px 4px; border-radius: 3px; display: inline-block;">ID: ${r.id}</div> <div style="font-size:0.75em; color:var(--text-secondary); margin-top:8px; font-family:monospace; background: var(--canvas-bg); padding: 2px 4px; border-radius: 3px; display: inline-block;">ID: ${r.id}</div>
</div> </div>
`).join(''); `).join('');
} catch (err) {
if (err.name !== 'AbortError') {
console.error("Search error:", err);
} }
} }
}, delay);
}
// --- WebSocket Activity Feed --- // --- WebSocket Activity Feed ---
const MAX_ACTIVITY_HISTORY = 100; const MAX_ACTIVITY_HISTORY = 100;
@@ -1238,6 +1258,45 @@
} }
} }
function setupSSE() {
try {
const sse = new EventSource('/api/activity/stream');
sse.onmessage = function(event) {
if (event.data) {
try {
const rec = JSON.parse(event.data);
const feed = document.getElementById('activity-feed');
if (feed) {
const item = document.createElement('div');
item.style.padding = '8px 12px';
item.style.borderBottom = '1px solid var(--border-color)';
item.style.fontSize = '0.85em';
item.style.background = 'rgba(39, 174, 96, 0.08)';
item.innerHTML = `<strong>${rec.category || 'activity'}</strong>: ${rec.summary || event.data}`;
feed.insertBefore(item, feed.firstChild);
}
} catch(e) {}
}
};
} catch(e) { console.error('SSE initialization error', e); }
}
// Ctrl+K / Cmd+K Global Search shortcut
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
e.preventDefault();
const searchTabBtn = document.querySelectorAll('.tab-button')[1];
if (searchTabBtn) {
switchTab('search-tab', searchTabBtn);
}
const searchInput = document.getElementById('search-box');
if (searchInput) {
searchInput.focus();
searchInput.select();
}
}
});
// --- Start --- // --- Start ---
loadVersion(); loadVersion();
loadGraph(); loadGraph();
@@ -1246,6 +1305,7 @@
loadActivityHistory(); loadActivityHistory();
loadAllExtras(); loadAllExtras();
setupWS(); setupWS();
setupSSE();
// Listen to theme toggle changes to update graph font colors // Listen to theme toggle changes to update graph font colors
const observer = new MutationObserver(() => updateGraphData()); const observer = new MutationObserver(() => updateGraphData());