Skip to content

Commit afbf568

Browse files
odedha-drclaude
andcommitted
perf: debounce search input and SSE detail re-fetch, drop expensive JSON.stringify from search filter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 20e5a2b commit afbf568

File tree

1 file changed

+29
-24
lines changed

1 file changed

+29
-24
lines changed

src/web/public/index.html

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,7 @@ <h3>Cost Breakdown</h3>
14231423
let expandedToolInputs = new Set();
14241424
let expandedOutputs = new Set();
14251425
let turnsSearchQuery = '';
1426+
let turnsSearchTimer = null;
14261427

14271428
function fmtTime(iso) {
14281429
if (!iso) return '';
@@ -1463,17 +1464,15 @@ <h3>Cost Breakdown</h3>
14631464
const query = turnsSearchQuery.toLowerCase();
14641465
const turns = query
14651466
? allTurns.filter(t => {
1466-
// Search in text content, tool names, tool inputs, and results
1467+
if (t.toolCalls && t.toolCalls.some(tc => tc.toLowerCase().includes(query))) return true;
14671468
for (const c of (t.content || [])) {
14681469
if (c.text && c.text.toLowerCase().includes(query)) return true;
14691470
if (c.toolName && c.toolName.toLowerCase().includes(query)) return true;
1470-
if (c.toolInput && JSON.stringify(c.toolInput).toLowerCase().includes(query)) return true;
14711471
if (c.toolResult) {
14721472
if (c.toolResult.stdout && c.toolResult.stdout.toLowerCase().includes(query)) return true;
14731473
if (c.toolResult.stderr && c.toolResult.stderr.toLowerCase().includes(query)) return true;
14741474
}
14751475
}
1476-
if (t.toolCalls && t.toolCalls.some(tc => tc.toLowerCase().includes(query))) return true;
14771476
return false;
14781477
})
14791478
: allTurns;
@@ -1664,15 +1663,17 @@ <h3>Cost Breakdown</h3>
16641663
});
16651664
});
16661665

1667-
// Search input
1666+
// Search input (debounced)
16681667
const searchInput = document.getElementById('turns-search-input');
16691668
if (searchInput) {
16701669
searchInput.addEventListener('input', (e) => {
1671-
turnsSearchQuery = e.target.value;
1672-
renderTurns();
1673-
// Re-focus and restore cursor position
1674-
const el = document.getElementById('turns-search-input');
1675-
if (el) { el.focus(); el.selectionStart = el.selectionEnd = el.value.length; }
1670+
clearTimeout(turnsSearchTimer);
1671+
turnsSearchTimer = setTimeout(() => {
1672+
turnsSearchQuery = e.target.value;
1673+
renderTurns();
1674+
const el = document.getElementById('turns-search-input');
1675+
if (el) { el.focus(); el.selectionStart = el.selectionEnd = el.value.length; }
1676+
}, 250);
16761677
});
16771678
}
16781679
}
@@ -2272,6 +2273,7 @@ <h3>Cost Breakdown</h3>
22722273
}
22732274

22742275
// ---- SSE Connection ----
2276+
let sseDetailTimer = null;
22752277
function connectSSE() {
22762278
const evtSource = new EventSource('/api/events');
22772279
evtSource.onmessage = (event) => {
@@ -2286,25 +2288,28 @@ <h3>Cost Breakdown</h3>
22862288
});
22872289
updateProjectDropdown();
22882290
renderSidebar();
2289-
// Re-fetch and re-render detail for selected session
2291+
// Re-fetch and re-render detail for selected session (debounced)
22902292
if (selectedId) {
22912293
const s = allSessions.find(s => s.id === selectedId);
22922294
if (s) {
22932295
renderHeader();
2294-
fetch('/api/sessions/' + encodeURIComponent(selectedId))
2295-
.then(r => r.ok ? r.json() : null)
2296-
.then(detail => {
2297-
if (detail && selectedId === detail.id) {
2298-
selectedDetail = detail;
2299-
if (activeTab === 'overview') renderOverview();
2300-
else if (activeTab === 'turns') renderTurns();
2301-
else if (activeTab === 'tools') renderTools();
2302-
else if (activeTab === 'subagents') renderSubagents();
2303-
else if (activeTab === 'skills') renderSkills();
2304-
else if (activeTab === 'flow') renderFlow();
2305-
}
2306-
})
2307-
.catch(() => {});
2296+
clearTimeout(sseDetailTimer);
2297+
sseDetailTimer = setTimeout(() => {
2298+
fetch('/api/sessions/' + encodeURIComponent(selectedId))
2299+
.then(r => r.ok ? r.json() : null)
2300+
.then(detail => {
2301+
if (detail && selectedId === detail.id) {
2302+
selectedDetail = detail;
2303+
if (activeTab === 'overview') renderOverview();
2304+
else if (activeTab === 'turns') renderTurns();
2305+
else if (activeTab === 'tools') renderTools();
2306+
else if (activeTab === 'subagents') renderSubagents();
2307+
else if (activeTab === 'skills') renderSkills();
2308+
else if (activeTab === 'flow') renderFlow();
2309+
}
2310+
})
2311+
.catch(() => {});
2312+
}, 500);
23082313
}
23092314
}
23102315
} catch (e) {

0 commit comments

Comments
 (0)