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:
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);