Skip to content

Commit 8f27ec4

Browse files
Precompute normalized search index for sessions
Build a per-session normalized index (nameNorm/bodyNorm) once per sessions change, then filter by includes against this index on keystrokes. This shifts costly normalization away from the hot path to updates, reducing per-keystroke churn for large histories. Preserves current search semantics with a clear separator between records to avoid odd cross-boundary matches.
1 parent 5de71d7 commit 8f27ec4

File tree

1 file changed

+26
-28
lines changed

1 file changed

+26
-28
lines changed

src/pages/IndependentPanel/App.jsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ function App() {
191191
const toSafeString = (value) =>
192192
typeof value === 'string' ? value : value == null ? '' : String(value)
193193

194-
// Filter sessions based on search query (memoized for performance)
194+
// Normalization utility for search
195195
const normalizeForSearch = (value) =>
196196
toSafeString(value)
197197
.toLowerCase()
@@ -200,34 +200,32 @@ function App() {
200200
.replace(/\s+/g, ' ')
201201
.trim()
202202

203-
const filteredSessions = useMemo(() => {
204-
const query = normalizeForSearch(debouncedQuery).trim()
205-
// Always exclude items without a valid sessionId first
206-
const validSessions = Array.isArray(sessions)
207-
? sessions.filter((s) => Boolean(s?.sessionId))
208-
: []
209-
210-
if (!query) return validSessions
211-
212-
return validSessions.filter((session) => {
213-
// Search in session name
214-
const sessionName = normalizeForSearch(session.sessionName)
215-
if (sessionName.includes(query)) {
216-
return true
217-
}
203+
// Precompute a normalized index for sessions to reduce per-keystroke work
204+
const normalizedIndex = useMemo(() => {
205+
if (!Array.isArray(sessions)) return []
206+
const SEP = '\n—\n'
207+
return sessions
208+
.filter((s) => Boolean(s?.sessionId))
209+
.map((s) => {
210+
const nameNorm = normalizeForSearch(s.sessionName)
211+
let bodyNorm = ''
212+
if (Array.isArray(s.conversationRecords)) {
213+
bodyNorm = s.conversationRecords
214+
.map((r) => `${normalizeForSearch(r?.question)} ${normalizeForSearch(r?.answer)}`)
215+
.join(SEP)
216+
}
217+
return { session: s, nameNorm, bodyNorm }
218+
})
219+
}, [sessions])
218220

219-
// Search in conversation records
220-
if (Array.isArray(session.conversationRecords)) {
221-
return session.conversationRecords.some((record) => {
222-
const question = normalizeForSearch(record?.question)
223-
const answer = normalizeForSearch(record?.answer)
224-
return question.includes(query) || answer.includes(query)
225-
})
226-
}
227-
228-
return false
229-
})
230-
}, [sessions, debouncedQuery])
221+
// Filter sessions based on search query using the precomputed index
222+
const filteredSessions = useMemo(() => {
223+
const q = normalizeForSearch(debouncedQuery).trim()
224+
if (!q) return normalizedIndex.map((i) => i.session)
225+
return normalizedIndex
226+
.filter((i) => i.nameNorm.includes(q) || i.bodyNorm.includes(q))
227+
.map((i) => i.session)
228+
}, [normalizedIndex, debouncedQuery])
231229

232230
return (
233231
<div className="IndependentPanel">

0 commit comments

Comments
 (0)