export interface IControllerTerminalMirrorOptions { cols: number; rows: number; /** * Applies PTY flow control. Called only on a change, and possibly before the PTY exists — the * caller reconciles `ptyPaused` once it does. */ setPtyPaused: (pausedArg: boolean) => void; } export interface IControllerTerminalSnapshotRequest { /** * Serializes the current screen alone and drops the reconstructed scrollback. Used when a peer * has to be handed the state again: the smaller payload is what lets it reach the live stream * instead of losing the delivery window once more while a larger one is acknowledged. */ screenOnly?: boolean; } export interface IControllerTerminalMirrorSnapshot { /** Absolute stream offset the reconstructed state is exact at. */ offset: number; /** Escape sequences that reproduce the state when written into a `cols`x`rows` grid. */ data: Buffer; cols: number; rows: number; } /** * The controller's own picture of what a terminal currently shows. * * Every byte the PTY produces is parsed by a headless emulator, so the last state of a terminal is * always available as a bounded serialization instead of a raw byte replay: an attaching peer * receives the reconstructed screen at an exact stream offset and then streams live output from * that offset, and the raw output ring only has to cover the in-flight delivery window. * * The emulator is fed in stream order and never talks back: replies it would produce as a real * terminal (device attributes, cursor reports) are dropped, because the attached browser is the * terminal that answers those. */ export declare class ControllerTerminalMirror { private readonly options; private readonly emulator; private readonly serializer; private readonly parserHandlers; private readonly barriers; /** * State the serialize addon does not reproduce, tracked as the stream is parsed and re-emitted * around the snapshot: DECSTBM margins per buffer, DECTCEM cursor visibility, the DECSCUSR * cursor style, and the SGR or SGR-pixel mouse coordinate encoding a full-screen application * needs for its clicks to decode. Title, hyperlinks and underline style or colour are * deliberately not tracked: the addon drops them and they do not change how the terminal * behaves. */ private readonly scrollRegions; /** * The last serialization, reused by every peer that asks for the same state. Peers attaching in * the same tick all resolve at one stream offset, and the tracked state can only change while * bytes are parsed — which moves the offset — or on a resize, which clears the cache, so an * equal offset and history choice is an equal screen. */ private cachedSnapshot?; private cursorVisible; private cursorStyle; private mouseEncoding; private writtenBytes; private parsedBytes; private paused; private flowControlReleased; private disposedState; private releasedState; private failureState?; /** * `allowProposedApi` is required: the buffer, parser and mode accessors this class and the * serialize addon read are proposed API. */ constructor(options: IControllerTerminalMirrorOptions); /** Absolute stream offset one past the last byte the mirror accepted. */ get streamEnd(): number; /** True while the PTY is held back because the emulator has not caught up. */ get ptyPaused(): boolean; get disposed(): boolean; /** * Why the mirror stopped reflecting its terminal. The emulator only rejects writes above its own * discard limit, which flow control keeps unreachable; if it ever happens the mirror fails * closed and attaching says so, instead of serving a state that is quietly wrong. */ get failure(): Error | undefined; write(chunkArg: Buffer): void; /** * Resizes the emulator at the current stream position, so output the root produced at the old * grid is still parsed at that grid. The emulator drops its scroll margins on resize, so the * tracked ones go with them. */ resize(colsArg: number, rowsArg: number): void; /** * The reconstructed state at the mirror's current stream end. Resolves once the emulator has * parsed every byte accepted up to that offset, which is what makes the snapshot exact there: * the serialization runs inside the write callback of the chunk ending at the offset, so no * later byte can have been applied yet. */ requestSnapshot(requestArg?: IControllerTerminalSnapshotRequest): Promise; /** * Hands the PTY back for good while the emulator keeps parsing. Called once a terminal's root is * being stopped: node-pty closes a PTY shortly after its child exits, output still unread then is * lost, and the exit is only reported after that close — so output held back by a pause that is * live when the root dies can never be recovered. From the moment a stop is decided, everything * the root writes is its final flush and must reach the stream rather than be held back, which is * also why flow control does not re-engage afterwards. The backlog is then bounded by the stop * deadline alone; a root that floods past the emulator's own discard limit anyway makes the * mirror fail closed, exactly as any other refused write does. */ releaseFlowControl(): void; dispose(): void; private fail; private release; private confirmParsed; private scheduleAtStreamEnd; private applyBacklogPressure; private captureSnapshot; private serializeBoundedBody; /** * Re-emits the tracked state the serialization leaves out. It goes after the body, which ends * with the addon's own mode block, so nothing here can influence how the body is painted. * DECSTBM is the one that needs care: it homes the cursor, so the cursor the body restored has * to be re-applied after it. */ private untrackedStateSuffix; private cursorPositionSequence; private activeBufferKind; /** * Every handler returns false, which lets the emulator's own handler run afterwards: the mirror * observes a sequence, it never replaces its effect. */ private registerModeTracking; private applyPrivateModes; /** Mirrors the emulator's own DECSTBM acceptance rules, so a rejected region is not tracked. */ private applyScrollRegion; }