Skip to content

0.0.14 cohort: composable interactive views and micro assets

Published on 2026-10-04. Every workstream in the 0.0.14 plan is merged, the release test below passed on the integrated tree, and every package is on crates.io and PyPI; see Publication.

0.0.14 has two themes that meet in the middle:

  • Building blocks. Containers, splits, tabs and modal layers are components themselves, with focus routing, event bubbling and a keymap registry. A public kit (list, scroll, filter and text-buffer state, line helpers, an action menu) is what the built-in components are now made of, so your own components use the same pieces. You can define a component in Rust, subclass rs_rich.interact.Component in Python, or register one by name from a compiled-in plugin, and compose it with the built-ins.
  • On top of them: a command palette, help and shortcut overlays, the action menu in a modal, a status bar and breadcrumbs; rich explore for JSON, YAML, TOML, XML, INI and .env; tree filtering that keeps ancestors; OSC 52 copy; reloading a list while keeping the query; a theme picker.
  • Micro assets, in a new crate, rs-rich-micro: emoji-sized inline images and animations written :micro:name:, drawn with Kitty, iTerm2 or Sixel graphics where the terminal supports them and as an emoji, text or half-block fallback everywhere else, including pipes and exports. A .richmicro package format, a layered registry (built-in < user < trusted project < inline), an image pipeline, a built-in library of 13 assets drawn for this project, rich micro, and rs_rich.micro.

A default build of rs-rich is unchanged, still 0.0.9, and still renders exactly like upstream rich 15.0.0.

Independent package versions

Package Version Release role
rs-rich-art 0.0.12 Kitty and iTerm2 encoders, inline Sixel, fitting to exact cells, the image pipeline helpers
rs-rich-plugin-api 0.0.3 PluginRegistrar::component and the component contract
rs-rich-mermaid 0.0.3 Manifest only: requires plugin API 0.0.3 and art 0.0.12
rs-rich-lumis 0.0.3 Manifest only: requires plugin API 0.0.3
rs-rich-ext 0.0.12 OSC 52 clipboard, the graphics environment and side channel for Frame and LiveCoordinator, plugin component lookup
rs-rich-micro 0.0.1 (new) Micro assets: model, packages, registry, markup, pipeline, rendering, cache, built-in library
rs-rich-interact 0.0.2 Composition, the kit and keymap, overlays and chrome, explorers, plugin components, graphics in the painter
rs-rich-record 0.0.2 Manifest only: requires ext 0.0.12
rs-rich-cli 0.0.14 rich explore, rich micro, micro markup with --emoji, the micro section in rich doctor
rs-rich on PyPI 0.0.3 Component subclassing and the containers in rs_rich.interact, and rs_rich.micro

rs-rich 0.0.9 and rs-rich-macros 0.0.3 do not change.

What changed

The changelog has the full list. By workstream:

Workstream Pull request
Plan #629
1. Composition foundation: containers, focus, keymap, public kit (#478, #479, #480) #631
2. Overlays and chrome (#453, #473, #474, #475, #481, #482) #632
3. Explorers and utilities (#428, #434, #460, #464, #465, #485, #488) #633
4. Components from Python and plugins #635
5. rs-rich-micro: model, packages, registry, markup (#566, #567, #569, #570, #578, #579, #580, #583, #585) #630
6. Rendering: Kitty, iTerm2, Sixel, fallback, animation, cache (#572, #573, #574, #575, #576, #577, #584) #634
7. Assets, tools and the showcase (#568, #571, #581, #582, #587) #636
Release test this PR

Migration

  • Flow::Ignored (interact). A component returns it for an event it does not use, so the event bubbles to its container. An exhaustive match on Flow needs the new arm; the event loop treats it as Continue.
  • The Ctrl+K action menu is a modal over the list, with a title and a dimmed backdrop. Keys and mouse work as before.
  • TreeSelect filters as a tree. Typing lists matches with their ancestors, in tree order, instead of a flat ranked list.
  • New enum variants: AssetKind::Micro and StatusItem::Icon (interact), Capability::Component (plugin API). Exhaustive matches need them.
  • MapEnvironment (ext) has a public cell_pixels field, so building one with a struct literal needs it; the builder is unaffected.
  • DataExplorer::focused returns an owned Option<Path>.
  • Key displays Shift+Tab as shift+tab, not backtab (which Key::parse already read). Python components see shift+tab in event.key.
  • Keymap overrides are stricter. A line with no keys is an error rather than a silent unbind; write none to unbind. Key names can be quoted ("#", ","), and a bare , is an error.
  • RICH_CLIPBOARD=1 applies on a terminal only. It never writes OSC 52 into a pipe.
  • rich explore refuses documents over 1,000,000 nodes (exit 3), with a hint to narrow them with --inspect --select.
  • Micro markup in the CLI needs --emoji, so default output is unchanged. Project micro packs load only with --micro-project or the micro_project config key, which ./rich.toml cannot set.

Release test (2026-10-01)

Check Result
python scripts/validate_release.py --tag rs-rich-cli-v0.0.14, on main before the fixes and again on this branch Every step passes: fmt, three Clippy configurations, 2,636 Rust tests in 188 suites (0 failed, 2 ignored), the lean CLI and plugin-loader tests, the release, readiness and package tests, version and CLI-reference checks, locked build and check, staged packages for every crate, tag plan
capture_golden.py against real rich 15.0.0 (own venv, no rich-cli) Every golden file regenerates byte-identically
capture_rst_golden.py against rich-rst 1.3.2 on rich 15.0.0 Every --rst fixture regenerates byte-identically
release.py plan per tag Each of the nine crate tags selects exactly its own package at the expected version; python-v0.0.3 belongs to pypi-release.yml
crates.io and PyPI 404 for all ten new versions
Consumer install cargo install from the packaged rs-rich-cli-0.0.14 source, with the eight unpublished siblings patched to their packaged .crate contents and rs-rich 0.0.9 from crates.io: rich --version reports 0.0.14, and rich explore, rich micro list and :micro: markup work
Docs tapes rich record --check passes for every tape (68 screenshots)
Python wheel maturin build --release (rs_rich-0.0.3-cp39-abi3), installed into a fresh venv with rich==15.0.0, pytest and pillow: the whole suite passes against the installed wheel (1,191 passed, 1 skipped); rich-rs --version reports the 0.0.14 CLI
Python sdist rs_rich-0.0.3.tar.gz installed from source into a fresh venv: builds, renders a Table, answers a headless Select, loads the 13 built-in micro assets, and rich-rs micro list --report json works
Security cargo audit: no vulnerabilities in 355 dependencies
Docs mkdocs build --strict passes for the main site and the Python site; gen_python_api.py --check (99 pages), gen_versions.py --check and gen_cli_reference.py --check match

What the release test found and fixed

Two independent audits covered the whole 0.0.14 delta: micro assets, rendering and the clipboard; and the interactive layer, plugin components and the Python bindings. Each finding was reproduced first, then fixed with a regression test that failed before the fix. The changelog lists every one. The main ones:

  • Terminal escapes from untrusted packages. A pack or manifest field (a package name, author, version or file path) holding ESC reached the terminal raw from rich micro install, show and list, and kept doing so on every later command once installed. Control characters are now refused in every manifest and pack field, and every package-derived string the CLI prints is shown inert.
  • Exports on a graphics terminal. --export-html and --export-svg captured Kitty placeholder cells or blank iTerm2 cells instead of the fallback, and the pager got the image escapes. Exports, the pager and watch capture now render the fallback.
  • The cell-size query. The CSI 16 t query could swallow typeahead and stopped a backgrounded rich -p --emoji ':micro:…' & with SIGTTOU. It now runs only from the terminal's foreground process group with job signals blocked, is skipped when input is waiting, and stops reading at a CSI c fence. It never pushes bytes back with TIOCSTI, which would replay terminal replies to the shell as typed input.
  • Files. rich micro create deleted a dangling symlink at its destination; a FIFO named *.richmicro in a layer directory hung every rich micro command. Only what a call creates is ever removed, and only regular files are opened as packages.
  • A plugin's caught panic took the live session out of raw mode and printed over the view. Panics caught through session::catch_panic now leave the session alone.
  • rich explore was quadratic. A 40,000-element array took 0.8 s and a 400 KB nested document 2.4 GB. Building the tree is now linear, paths are worked out only for the focused node, and guides only for visible rows.
  • Crashes: the text buffer's caret could land inside a grapheme (a panic in debug builds), an empty Tabs panicked on Tab and on clicks, and kit::keep_ancestors panicked on an out-of-range parent.
  • Behaviour: table copies carried the on-screen text (newlines shown as pictures) instead of the data; plugins received backtab and escape instead of the key names they declared; a keymap override of # or , silently unbound the action; status-bar spinners ignored reduced motion; Viewport ignored rebinding; breadcrumb and tab clicks after a title holding controls landed on the wrong item.
  • Hardening: RICH_CLIPBOARD=1 no longer writes OSC 52 into a pipe, and the on-disk micro cache is keyed by SHA-256 rather than a 64-bit hash, so a crafted package cannot take another asset's cache entry.

Known limits

  • Real graphics terminals. The PTY tests check the bytes, the cursor and the cells around every protocol, but through a VT emulator that ignores the graphics themselves. The images were checked on a magnified contact sheet, not in Kitty, iTerm2 or a Sixel terminal; see Before publishing.
  • Plugin components work for compiled-in plugins only. The dylib and WASM runtime ABI has no component kind, and there is no CLI command to mount one.
  • Micro markup expands in --print with --emoji only, not in titles, captions or Markdown. rich asset --kind micro and rich explore --icons show the fallback, not graphics.
  • Assets are one row high. WebP and frame-sequence animations show their still image, and the disk cache holds still images only.
  • The cell-size query can still lose a key pressed in the milliseconds the terminal takes to answer, and a reply arriving after its 500 ms timeout reaches the next program. RICH_CELL_PIXELS skips the query.
  • Not yet rebindable: Confirm, Pager, TextArea, ColorPicker, Form and FilePicker declare their keymaps but still match keys inline.

Before publishing

One check cannot run in CI: open each protocol in a real terminal.

RICH_MICRO=kitty rich micro preview status/loading   # Kitty or WezTerm
RICH_MICRO=iterm rich micro preview fun/heart        # iTerm2
RICH_MICRO=sixel rich micro preview fun/coffee       # a Sixel terminal (foot, mlterm, WezTerm)
rich -p 'Deploy :micro:status/success: done' --emoji
cargo run -p rs-rich-interact --example micro_showcase --features micro

Each should draw the image in its cells, animate where the asset does, leave the text around it in place, and leave nothing behind after Ctrl+C or q.

Publication

RELEASES.toml lists every package's version in publication order, each after the crates it depends on. Tag what is due from it, after the check under Before publishing, on the merged commit on main:

python3 scripts/release_cohort.py status          # what is published, tagged, or due
python3 scripts/release_cohort.py tag --dry-run   # what it would tag
python3 scripts/release_cohort.py tag             # tag and wait, one at a time

tag skips rs-rich 0.0.9 and rs-rich-macros 0.0.3, already on crates.io, and pushes one annotated tag at a time, waiting for each version to appear on its registry before the next:

  1. rs-rich-plugin-api-v0.0.3;
  2. rs-rich-art-v0.0.12;
  3. rs-rich-mermaid-v0.0.3 and rs-rich-lumis-v0.0.3;
  4. rs-rich-ext-v0.0.12;
  5. rs-rich-micro-v0.0.1, the new crate, then its Trusted Publishing entry;
  6. rs-rich-record-v0.0.2;
  7. rs-rich-interact-v0.0.2 (after micro);
  8. rs-rich-cli-v0.0.14;
  9. python-v0.0.3.

All ten tags went out from main with release_cohort.py tag: the nine crates at 324c65b (after #638 and #639), and python-v0.0.3 at 61c002e. Its first run, at 324c65b, built every wheel but failed its test job, since the release job did not install Pillow for the micro page's example; nothing reached PyPI. #640 added it, and the tag was moved to 61c002e and published.

crates.io offers Trusted Publishing only on a crate that already exists, so rs-rich-micro 0.0.1 uploads with the crates-io environment's token: the workflow sees that the crate is new and uses it for that upload only. See Registry authentication.