Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit a9386a445802c085015c5582b86877bd97ccdce2
parent 79446981bf0273369ca05877c001d171ada3bcb5
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu, 16 Apr 2026 14:01:47 -0400

Bump MAX_HITS and use larger search window

Diffstat:
Mapp/TranscriptSearch.tsx | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 69 insertions(+), 9 deletions(-)

diff --git a/app/TranscriptSearch.tsx b/app/TranscriptSearch.tsx @@ -29,14 +29,19 @@ type HitGroup = { hits: Hit[]; }; -const MAX_HITS = 500; +const MAX_HITS = 1000; export default function TranscriptSearch({ transcripts, }: { transcripts: Summary[]; }) { - const { q: query, re: useRegex, v: activeVideo, t: activeTime } = useUrlParams(); + const { + q: query, + re: useRegex, + v: activeVideo, + t: activeTime, + } = useUrlParams(); const [input, setInput] = useState(query); useEffect(() => { setInput(query); @@ -116,13 +121,33 @@ export default function TranscriptSearch({ for (const t of transcripts) { const full = fetched[t.slug]; if (!full?.cues) continue; + const cues = full.cues; const hits: Hit[] = []; - for (const c of full.cues) { - if (matches(c.text)) { - hits.push({ start: Math.round(c.start), text: c.text }); - total++; - if (total >= MAX_HITS) break; - } + for (let i = 0; i < cues.length; i++) { + const cur = cues[i]; + const prevText = i > 0 ? cues[i - 1].text : ""; + const nextText = i < cues.length - 1 ? cues[i + 1].text : ""; + const sep1 = prevText ? " " : ""; + const sep2 = nextText ? " " : ""; + const windowText = prevText + sep1 + cur.text + sep2 + nextText; + const curStart = prevText.length + sep1.length; + const curEnd = curStart + cur.text.length; + const m = findFirstMatchInRange( + windowText, + curStart, + curEnd, + trimmed, + useRegex, + regex.re, + ); + if (!m) continue; + const crosses = m.idx < curStart || m.idx + m.length > curEnd; + hits.push({ + start: Math.round(cur.start), + text: crosses ? windowText : cur.text, + }); + total++; + if (total >= MAX_HITS) break; } if (hits.length > 0) { groups.push({ @@ -138,7 +163,6 @@ export default function TranscriptSearch({ } } return { groups, totalHits: total, capped: false }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [trimmed, transcripts, fetched, summaryBySlug, useRegex, regex.re]); return ( @@ -336,6 +360,42 @@ function TranscriptGrid({ ); } +function findFirstMatchInRange( + haystack: string, + rangeStart: number, + rangeEnd: number, + query: string, + useRegex: boolean, + regex: RegExp | null, +): { idx: number; length: number } | null { + if (useRegex) { + if (!regex) return null; + const flags = regex.flags.includes("g") ? regex.flags : regex.flags + "g"; + const re = new RegExp(regex.source, flags); + let m: RegExpExecArray | null; + while ((m = re.exec(haystack)) !== null) { + if (m.index >= rangeStart && m.index < rangeEnd) { + return { idx: m.index, length: m[0].length }; + } + if (m.index >= rangeEnd) return null; + if (m[0].length === 0) re.lastIndex++; + } + return null; + } + if (!query) return null; + const lower = haystack.toLowerCase(); + const ql = query.toLowerCase(); + let from = 0; + while (from <= haystack.length) { + const idx = lower.indexOf(ql, from); + if (idx === -1) return null; + if (idx >= rangeStart && idx < rangeEnd) return { idx, length: ql.length }; + if (idx >= rangeEnd) return null; + from = idx + 1; + } + return null; +} + function formatSeconds(s: number): string { const h = Math.floor(s / 3600); const m = Math.floor((s % 3600) / 60);