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

Function TranscriptSearchBar

src/screens/REPL.tsx:613–728  ·  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

611 * useSearchInput handles readline editing; we report query changes and
612 * render the counter. Incremental — re-search + highlight per keystroke. */
613function TranscriptSearchBar({
614 jumpRef,
615 count,
616 current,
617 onClose,
618 onCancel,
619 setHighlight,
620 initialQuery,
621}: {
622 jumpRef: RefObject<JumpHandle | null>;
623 count: number;
624 current: number;
625 /** Enter — commit. Query persists for n/N. */
626 onClose: (lastQuery: string) => void;
627 /** Esc/ctrl+c/ctrl+g — undo to pre-/ state. */
628 onCancel: () => void;
629 setHighlight: (query: string) => void;
630 // Seed with the previous query (less: / shows last pattern). Mount-fire
631 // of the effect re-scans with the same query — idempotent (same matches,
632 // nearest-ptr, same highlights). User can edit or clear.
633 initialQuery: string;
634}): React.ReactNode {
635 const { query, cursorOffset } = useSearchInput({
636 isActive: true,
637 initialQuery,
638 onExit: () => onClose(query),
639 onCancel,
640 });
641 // Index warm-up runs before the query effect so it measures the real
642 // cost — otherwise setSearchQuery fills the cache first and warm
643 // reports ~0ms while the user felt the actual lag.
644 // First / in a transcript session pays the extractSearchText cost.
645 // Subsequent / return 0 immediately (indexWarmed ref in VML).
646 // Transcript is frozen at ctrl+o so the cache stays valid.
647 // Initial 'building' so warmDone is false on mount — the [query] effect
648 // waits for the warm effect's first resolve instead of racing it. With
649 // null initial, warmDone would be true on mount → [query] fires →
650 // setSearchQuery fills cache → warm reports ~0ms while the user felt
651 // the real lag.
652 const [indexStatus, setIndexStatus] = React.useState<'building' | { ms: number } | null>('building');
653 React.useEffect(() => {
654 let alive = true;
655 let hideTimeout: ReturnType<typeof setTimeout> | undefined;
656 const warm = jumpRef.current?.warmSearchIndex;
657 if (!warm) {
658 setIndexStatus(null); // VML not mounted yet — rare, skip indicator
659 return;
660 }
661 setIndexStatus('building');
662 warm().then(ms => {
663 if (!alive) return;
664 // <20ms = imperceptible. No point showing "indexed in 3ms".
665 if (ms < 20) {
666 setIndexStatus(null);
667 } else {
668 setIndexStatus({ ms });
669 hideTimeout = setTimeout(() => alive && setIndexStatus(null), 2000);
670 }

Callers

nothing calls this directly

Calls 2

useSearchInputFunction · 0.50
onCloseFunction · 0.50

Tested by

no test coverage detected