Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 02:05
· 30 commits to main since this release
5bbb220

Added

  • TerminalBuilder::envs sets several child environment variables from an
    iterator of key-value pairs.
    Values keep their iteration and builder-call
    order, and remain explicit when env_clear disables inherited variables.

  • Screen::links reports the OSC 8 hyperlinks an application emitted.
    A hyperlink changes no cell — its label renders exactly as unlinked text
    would — so the URL existed nowhere a test could reach, and an assertion
    that a TUI linked an issue, a file or a doc page passed identically
    against an application that emitted no link at all, or linked the wrong
    target
    . Captured rather than answered, on the same grounds as the
    OSC 52 clipboard: the only evidence otherwise available is the
    application's own visible output, which proves the code path ran and
    nothing about where it points.

    Each span reports its uri, its id (spans sharing one are one logical
    link), the label it wrapped, and whether the application ever closed
    it — an unterminated link is a real defect, because in a real terminal
    every character written afterwards joins it. Two bounds keep the capture
    honest: the log holds the most recent 64 spans and evicts oldest-first, so
    a TUI that redraws its links every frame still reports the current
    frame's; and a label past the capture bound is reported as unknown
    rather than as a prefix, since a prefix of the wrong length is a wrong
    answer.

  • Screen::cursor_shape and Screen::cursor_blink report DECSCUSR.
    A screen where the application asked for a bar and one where it never
    asked used to be the same Screen. The shape is load-bearing behaviour
    rather than decoration — a modal editor switches to a bar for insert and
    back to a block for normal, and "the mode indicator says INSERT" and "the
    terminal was actually put into insert" are different claims. It also makes
    the restore assertable, which is the half that ships broken: a program
    that changes the cursor and never changes it back leaves the user's
    terminal wrong after exit, the same class of defect alternate_screen()
    already catches.

    Shape and blink are one DECSCUSR parameter but two facts, so they are
    reported apart. CursorShape::Default — the application never sent the
    escape — is a third state and is reported as itself rather than folded
    into Block.

  • Key::Insert, encoding ESC [ 2 ~, and chording like its
    neighbours (Key::Insert.shift()ESC [ 2 ; 2 ~). It was the 2
    missing from a navigation run that already had 3, 5 and 6, so an
    application binding Insert could not be tested without hand-writing the
    escape.

Changed

  • Key and Signal are now #[non_exhaustive]. This is breaking for
    downstream code that matches either without a wildcard arm; adding a
    _ => … fixes it, and equality and construction are unaffected.

    Worth doing now rather than later. Adding a variant to an exhaustive
    public enum is itself a breaking change, so every future key and every
    future signal would have cost a version of its own — Key has no F13+ and
    no keypad, and Signal carries seven of POSIX's thirty, missing
    SIGWINCH and SIGCONT, which are exactly what a terminal application
    reacts to. Both types are constructed far more often than matched
    (t.send(Key::Enter), t.signal(Signal::Int)), so the cost falls almost
    entirely on the crate and not on its users. #[non_exhaustive] is
    breaking to add, which makes the cheapest moment the earliest one.

    Color is deliberately left exhaustive. Default, palette index and 24-bit
    RGB is the whole terminal colour model — there is no fourth variant
    waiting — and Color is the one enum here that downstream code really
    does match on.

Security

  • A decoded image can no longer choose how much memory it allocates.
    GraphicsPayload::decode trusted four sizes that the program under test
    writes: kitty's s=/v=, sixel's raster attributes, its !n repeat count
    and its #n colour-register index. A compressed kitty payload was also
    inflated with no output limit, and zlib reaches about 1000:1. Each turned a
    handful of bytes into a request for tens of gigabytes — !4294967295~ is
    twelve bytes; a declared 65535x65535 is about twenty and asks for 17 GB
    before the pixel data is touched at all.

    Decoding is now bounded: no image above 4096x4096 (far beyond what a
    terminal can place, and 64 MiB of RGBA once built), sixel colour registers
    capped at 65536, and a compressed payload inflated only as far as its
    declared size needs. Refusals are a new DecodeError::TooLarge rather than
    a silent clamp — DecodeError is #[non_exhaustive], so matching on it
    already required a wildcard.

    Present in every release before this one. It sits behind the off-by-default
    decode feature and is reached only when a test calls decode(), so a
    suite that merely counts images was never exposed. SECURITY.md now
    enumerates these bounds with the rest.

Fixed

  • A hard reset (RIS, ESC c) returns the cursor shape to the terminal's
    default and closes any open OSC 8 span.
    printf '\033c' is one of the
    ways a program hands the terminal back on exit, so reporting the last
    DECSCUSR after one claimed a shape the terminal no longer held — and it
    did so in exactly the case cursor_shape exists to check. The window
    title, the clipboard, the bell count and the link log are deliberately
    left alone: the title is a window property RIS does not restore in
    xterm, and the rest are records of what the application emitted rather
    than state the terminal still holds.

  • The crate's doctests build with default features disabled. The bundled
    snapshot macro example is compiled only when its insta feature exists,
    and CI now runs cargo test --workspace --no-default-features so this
    supported configuration cannot silently rot again.