/** * Only pass on the mouse reports the program actually asked for. * * A terminal sends mouse reports because some program switched a tracking mode * on. The mode belongs to the TERMINAL, not to the program: it survives that * program exiting, being killed, or crashing. So the next program inherits a * terminal that reports mouse activity it never requested, cannot parse, and * therefore shows as typed text. * * Measured against the shipped Claude binary, it enables exactly two mouse * modes: `?1000h` (click) and `?1006h` (SGR encoding). It never asks for * `?1002h` (drag) or `?1003h` (any-motion). Yet the reports arriving in the * operator's input box were `<35;99;8M`: button code 35 is 32 (motion) plus 3 * (no button), which ONLY any-motion tracking produces. Something else had * left `?1003h` on, and every mouse movement across the window was being sent * to a program that had asked for clicks. * * That is not a race to be timed better, and no amount of tidying up on exit * fixes it: the mode can be set by anything, at any time, including before ccx * ever ran. The reliable answer is to stop trusting the terminal's state and * start tracking what the CHILD asked for, which ccx can see because every byte * the child writes passes through it. A report the child cannot have asked for * is dropped, and the terminal is told to stop sending them. */ /** The mouse tracking a program has switched on. */ export interface MouseModes { /** ?1000: press and release. */ click: boolean; /** ?1002: motion while a button is held. */ drag: boolean; /** ?1003: motion at all times, the noisy one. */ motion: boolean; } export declare const NO_MOUSE: MouseModes; /** Apply what a program just wrote to what we believe it has asked for. */ export declare function applyModeChanges(current: MouseModes, output: string): MouseModes; export interface FilterResult { /** What may be forwarded to the child. */ forward: string; /** How many reports were dropped. */ dropped: number; /** * True when a MOTION report arrived that the child never asked for, so the * caller can tell the terminal to stop sending them. */ unrequestedMotion: boolean; } /** * Remove the mouse reports this program cannot have asked for. * * The rule is narrow on purpose. Reports are only ever dropped when the child's * own declared modes say it could not have wanted them: * * nothing enabled drop every report * click only (?1000) drop MOTION reports, keep presses and releases * drag or motion enabled keep everything * * Anything that is not a mouse report is passed through untouched, so ordinary * typing, arrow keys, pastes and Escape are unaffected. */ export declare function filterMouseReports(text: string, modes: MouseModes): FilterResult; /** Turn off the tracking nobody asked for. Safe to send to any terminal. */ export declare const STOP_UNREQUESTED_MOTION = "\u001B[?1003l\u001B[?1002l"; export interface MouseGate { /** Note what the child just wrote, so we know what it has asked for. */ observeOutput(text: string): void; /** Filter operator input before it reaches the child. */ filterInput(text: string): FilterResult; /** Forget the child's modes; the next child starts from nothing. */ childChanged(): void; /** What the child currently has enabled, for tests and diagnostics. */ modes(): MouseModes; } /** * The tail of some output that might be the START of a mode declaration. * * The child's output arrives in whatever chunks the pseudo-terminal produces, * so `ESC[?1003h` can straddle two of them. Scanning each chunk on its own * would miss it, and missing an ENABLE is the expensive direction: ccx would * then drop reports the child had genuinely asked for, breaking mouse support * to fix a mouse bug. * * Only an unterminated candidate is kept, so nothing is ever counted twice. */ export declare function unfinishedModeTail(text: string): string; /** * The trailing bytes that might be the start of a paste marker. * * A chunk ending in `ESC[20` decides nothing yet: the next one could make it * the opening marker, in which case what follows is content and must not be * touched. Deciding early gets it wrong in both directions, dropping bytes * from a paste or filtering nothing after one. Bounded by the marker length, * so at most a few bytes ever wait, and only until the next input arrives. */ export declare function partialPasteMarker(text: string): string; export declare function createMouseGate(): MouseGate;