| 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]) | |
| } | |