πŸ“„ File detail

ink/hooks/use-search-highlight.ts

🧩 .tsπŸ“ 54 linesπŸ’Ύ 2,158 bytesπŸ“ text
← Back to All Files

🎯 Use case

This file lives under β€œink/”, which covers Ink terminal UI (layouts, TTY IO, keyboard, renderer components). On the API surface it exposes useSearchHighlight β€” mainly functions, hooks, or classes. Dependencies touch React UI. It composes internal code from components, dom, instances, and render-to-screen (relative imports).

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

import { useContext, useMemo } from 'react' import StdinContext from '../components/StdinContext.js' import type { DOMElement } from '../dom.js' import instances from '../instances.js' import type { MatchPosition } from '../render-to-screen.js'

πŸ“€ Exports (heuristic)

  • useSearchHighlight

πŸ“š External import roots

Package roots from from "…" (relative paths omitted).

  • react

πŸ–₯️ Source preview

import { useContext, useMemo } from 'react'
import StdinContext from '../components/StdinContext.js'
import type { DOMElement } from '../dom.js'
import instances from '../instances.js'
import type { MatchPosition } from '../render-to-screen.js'

/**
 * Set the search highlight query on the Ink instance. Non-empty β†’ all
 * visible occurrences are inverted on the next frame (SGR 7, screen-buffer
 * overlay, same damage machinery as selection). Empty β†’ clears.
 *
 * This is a screen-space highlight β€” it matches the RENDERED text, not the
 * source message text. Works for anything visible (bash output, file paths,
 * error messages) regardless of where it came from in the message tree. A
 * query that matched in source but got truncated/ellipsized in rendering
 * won't highlight; that's acceptable β€” we highlight what you see.
 */
export function useSearchHighlight(): {
  setQuery: (query: string) => void
  /** Paint an existing DOM subtree (from the MAIN tree) to a fresh
   *  Screen at its natural height, scan. Element-relative positions
   *  (row 0 = element top). Zero context duplication β€” the element
   *  IS the one built with all real providers. */
  scanElement: (el: DOMElement) => MatchPosition[]
  /** Position-based CURRENT highlight. Every frame writes yellow at
   *  positions[currentIdx] + rowOffset. The scan-highlight (inverse on
   *  all matches) still runs β€” this overlays on top. rowOffset tracks
   *  scroll; positions stay stable (message-relative). null clears. */
  setPositions: (
    state: {
      positions: MatchPosition[]
      rowOffset: number
      currentIdx: number
    } | null,
  ) => void
} {
  useContext(StdinContext) // anchor to App subtree for hook rules
  const ink = instances.get(process.stdout)
  return useMemo(() => {
    if (!ink) {
      return {
        setQuery: () => {},
        scanElement: () => [],
        setPositions: () => {},
      }
    }
    return {
      setQuery: (query: string) => ink.setSearchHighlight(query),
      scanElement: (el: DOMElement) => ink.scanElementSubtree(el),
      setPositions: state => ink.setSearchPositions(state),
    }
  }, [ink])
}