MCPcopy Create free account
hub / github.com/freecodexyz/free-code / TranscriptSearchBar

Function TranscriptSearchBar

src/screens/REPL.tsx:371–475  ·  view source on GitHub ↗

less-style / bar. 1-row, same border-top styling as TranscriptModeFooter * so swapping them in the bottom slot doesn't shift ScrollBox height. * useSearchInput handles readline editing; we report query changes and * render the counter. Incremental — re-search + highlight per keystroke.

({
  jumpRef,
  count,
  current,
  onClose,
  onCancel,
  setHighlight,
  initialQuery
}: {
  jumpRef: RefObject<JumpHandle | null>;
  count: number;
  current: number;
  /** Enter — commit. Query persists for n/N. */
  onClose: (lastQuery: string) => void;
  /** Esc/ctrl+c/ctrl+g — undo to pre-/ state. */
  onCancel: () => void;
  setHighlight: (query: string) => void;
  // Seed with the previous query (less: / shows last pattern). Mount-fire
  // of the effect re-scans with the same query — idempotent (same matches,
  // nearest-ptr, same highlights). User can edit or clear.
  initialQuery: string;
})

Source from the content-addressed store, hash-verified

369 * useSearchInput handles readline editing; we report query changes and
370 * render the counter. Incremental — re-search + highlight per keystroke. */
371function TranscriptSearchBar({
372 jumpRef,
373 count,
374 current,
375 onClose,
376 onCancel,
377 setHighlight,
378 initialQuery
379}: {
380 jumpRef: RefObject<JumpHandle | null>;
381 count: number;
382 current: number;
383 /** Enter — commit. Query persists for n/N. */
384 onClose: (lastQuery: string) => void;
385 /** Esc/ctrl+c/ctrl+g — undo to pre-/ state. */
386 onCancel: () => void;
387 setHighlight: (query: string) => void;
388 // Seed with the previous query (less: / shows last pattern). Mount-fire
389 // of the effect re-scans with the same query — idempotent (same matches,
390 // nearest-ptr, same highlights). User can edit or clear.
391 initialQuery: string;
392}): React.ReactNode {
393 const {
394 query,
395 cursorOffset
396 } = useSearchInput({
397 isActive: true,
398 initialQuery,
399 onExit: () => onClose(query),
400 onCancel
401 });
402 // Index warm-up runs before the query effect so it measures the real
403 // cost — otherwise setSearchQuery fills the cache first and warm
404 // reports ~0ms while the user felt the actual lag.
405 // First / in a transcript session pays the extractSearchText cost.
406 // Subsequent / return 0 immediately (indexWarmed ref in VML).
407 // Transcript is frozen at ctrl+o so the cache stays valid.
408 // Initial 'building' so warmDone is false on mount — the [query] effect
409 // waits for the warm effect's first resolve instead of racing it. With
410 // null initial, warmDone would be true on mount → [query] fires →
411 // setSearchQuery fills cache → warm reports ~0ms while the user felt
412 // the real lag.
413 const [indexStatus, setIndexStatus] = React.useState<'building' | {
414 ms: number;
415 } | null>('building');
416 React.useEffect(() => {
417 let alive = true;
418 const warm = jumpRef.current?.warmSearchIndex;
419 if (!warm) {
420 setIndexStatus(null); // VML not mounted yet — rare, skip indicator
421 return;
422 }
423 setIndexStatus('building');
424 warm().then(ms => {
425 if (!alive) return;
426 // <20ms = imperceptible. No point showing "indexed in 3ms".
427 if (ms < 20) {
428 setIndexStatus(null);

Callers

nothing calls this directly

Calls 1

useSearchInputFunction · 0.85

Tested by

no test coverage detected