/** * What the terminal in front of us can actually do. * * `color-depth.ts` answers the palette question; this answers the rest of the * ones that change what we may safely emit: mouse reporting, bracketed paste, * the alternate screen, clipboard writes (OSC 52) and hyperlinks (OSC 8), plus * which family of terminal we are talking to. * * Why it is a module and not a few `if`s: these decisions are made from * environment variables that nobody on the team can test by hand for every * desktop — GNOME Terminal, XFCE Terminal, Konsole, xterm, tmux, the Linux * virtual console, Windows Terminal, a legacy Windows console. So the input is * a parameter, the table is data, and `tests/terminal-caps.test.mjs` pins one * fixture per family on any platform. * * The bias is **not** "assume the best". A capability we claim but the terminal * lacks costs the user something real: `?1000h` on a console that cannot report * SGR coordinates swallows its mouse; OSC 52 where nothing reads it makes * `/copy` look like it worked; the alternate screen on a serial console loses * the scrollback. Where a terminal is known to be marginal, the answer is no, * and `DSH_TUI_TERM_CAPS` is the escape hatch for the user who knows better. * * @module dsh-ssh-tui/terminal-caps */ import { type ColorDepth } from './color-depth.js'; /** Which terminal implementation is on the other end. */ export type TerminalFamily = 'windows-terminal' | 'windows-console' | 'vte' | 'konsole' | 'xterm' | 'tmux' | 'screen' | 'linux-console' | 'dumb' | 'unknown'; /** One terminal's answer to every question this TUI asks of it. */ export interface TerminalCapabilities { family: TerminalFamily; /** A human name for diagnostics (`/diag`), e.g. `GNOME Terminal (VTE 7000)`. */ label: string; colors: ColorDepth; /** Report mouse events at all (`?1000h`). */ mouse: boolean; /** Report them as SGR (`?1006h`) instead of the 32-column X10 encoding. */ mouseSgr: boolean; /** Report motion while a button is held (`?1002h`), which a drag needs. */ mouseDrag: boolean; /** `?2004h`: paste arrives wrapped, so a multi-line paste is one event. */ bracketedPaste: boolean; /** `?1049h`: the TUI gets its own screen and restores the scrollback on exit. */ alternateScreen: boolean; /** OSC 52 clipboard writes reach the user's own machine. */ osc52: boolean; /** OSC 8 hyperlinks are clickable rather than noise. */ osc8: boolean; /** OSC 0/2 window-title updates are honoured. */ title: boolean; /** `DSH_TUI_TERM_CAPS` tokens that were rejected, for `/diag` to report. */ ignoredOverrides: readonly string[]; } /** Everything the classifier reads. `platform` and `env` are injectable. */ export interface TerminalProbe { env?: NodeJS.ProcessEnv; platform?: NodeJS.Platform; } /** Capability names a user may force on or off through `DSH_TUI_TERM_CAPS`. */ declare const TOGGLEABLE: readonly ["mouse", "mouseSgr", "mouseDrag", "bracketedPaste", "alternateScreen", "osc52", "osc8", "title"]; /** * Parse `DSH_TUI_TERM_CAPS`. * * `no-mouse`, `mouse`, `mouse=false` and `mouse = false` all mean what they * look like: the `=` binds first, so the spaced spelling cannot silently invert * into the opposite answer (it did, and `osc52 = false` then *re-enabled* the * clipboard promise). A value outside the two vocabularies, an unknown name or a * bare `no-` is rejected rather than guessed, and the rejects are returned so * `/diag` can show them — this variable is the only feedback channel a user has * when an override does not appear to work. */ export declare function parseCapsOverrideReport(raw: string): { overrides: Partial>; ignored: string[]; }; /** The overrides alone, for callers that do not report. */ export declare function parseCapsOverride(raw: string): Partial>; /** * Classify the terminal from the environment. * * Detection is by the markers terminals actually set: Windows Terminal exports * `WT_SESSION`; the VTE family (GNOME Terminal, XFCE Terminal, MATE, Tilix, * Terminator, Guake) exports `VTE_VERSION`; Konsole exports `KONSOLE_VERSION`; * tmux and screen export `TMUX` / `STY` and set a `tmux*` / `screen*` TERM. * Everything else falls back to the TERM name. */ export declare function detectTerminalFamily(probe?: TerminalProbe): { family: TerminalFamily; label: string; }; /** * Every capability for the terminal in front of us. * * `DSH_TUI_TERM_CAPS` overrides individual answers (`no-mouse`, `osc52=false`); * `DSH_TUI_NO_ALT_SCREEN` and `DSH_TUI_OSC8` keep working because they came * first and are documented. */ export declare function terminalCapabilities(probe?: TerminalProbe): TerminalCapabilities; /** Mouse-tracking sequences to enable, in one write. Empty when unsupported. */ export declare function mouseEnableSequence(caps: TerminalCapabilities): string; /** Mouse-tracking sequences to disable. Always safe to emit: `l` on a mode the * terminal never enabled is ignored, and leaving a mode on is not. */ export declare function mouseDisableSequence(): string; /** Bracketed-paste enable/disable, or empty when the terminal lacks it. */ export declare function bracketedPasteSequence(caps: TerminalCapabilities, on: boolean): string; export {};