Skip to content

0.0.22 cohort: parity, the plugin marketplace and a terminal web browser

Memo, intuiTUIve's elephant, in the cartoon look Published on 2026-10-25, every crate by one release run, with rs-rich 0.0.10 on PyPI. See Publication.

0.0.22 finishes stage 2 of the plan to 1.0.0. The core's promise, "renders what rich renders", is now checked row by row: every widget, live display and document module is ported or recorded as a divergence, scope.py is ported, and every divergence has a verdict. The plugin marketplace ships end to end, from a signed index to rich plugin install. intuiTUIve gains what three demo clones shared and four controls whose labels and options change while they are shown, and a new crate, Noctiluca, is a web browser for the terminal built on them. The 0.0.22 plan says what moved on to 0.0.23.

  • Parity with rich 15.0.0. Twelve rendering-core rows, the widget and layout rows (padding, panel, align, constrain, table, layout, screen, bar, box, _ratio), the live display rows (live with the alternate screen and the stdout/stderr redirect, progress, status, spinner, file_proxy) and the document rows are complete in PORTING, with 34, 47 and 86 new golden cases (parity_core.tsv, parity_widgets.tsv, parity_docs.tsv) and parity_live.tsv. scope.py is ported (render_scope), and so is pretty.py's layout over a description of the Python object. console.py is the one library row still partial; PORTING lists what it lacks.
  • Every divergence has a verdict. DIVERGENCES opens with a table: seven entries resolved in this release, the rest permanent, each with its reason.
  • The differential fuzzer covers the documents. scripts/diff_rich.py also generates Markdown, Tree, Columns, Progress frames, Pretty and JSON cases. Its first runs found six Markdown families, all fixed; one case remains a known divergence, #769. See Parity.
  • rs-rich-marketplace 0.0.1 (new). The marketplace client: it verifies the index's minisign signature, key chain, serial and expiry, reads listings, resolves a version against the host's plugin API and ABI, and installs over HTTPS within the declared size and SHA-256, with a lockfile. No terminal code. See Plugins.
  • rich plugin. search, info, install, update, remove, list, enable, disable and verify; rich plugin browse, the same in a selector with each package's provenance; and rich plugin publish, which checks a package and writes its listing for a pull request. An installed, enabled WASM plugin loads on every run without --plugin. install shows the tier, the capabilities and the permissions and asks before it writes anything.
  • WASM plugins on by default. rs-rich-cli's wasm-plugins feature is a default, so a default rich loads sandboxed WASM plugins. Native plugins stay off by default.
  • The signing workflow and the compatibility job. The marketplace repository's workflow (rebuild the index, bump the serial, sign in a protected environment) is in docs/marketplace-repo/; CI installs the official plugins and a fixture WASM plugin from a test marketplace.
  • The gaps three clones shared. Panel titles flush with the corner and plain-text titles (Panels), a tree table (Tables), App::min_size with a node of its own or shorter forms of its message, and scope cycling that reveals a hidden pane. The clones drop their workarounds.
  • Live controls. Labels that change in place (button_of, checkbox_of, switch_of), a three-state check box (CheckState), select and radio options known by key with a disabled reason, and a live action list: help and the palette update while open, and running a binding checks it again. See Buttons and form controls.
  • Made-up releases with two holds. App::synthesize_releases_with takes Holds { first, repeat }, so a game holds a key across the keyboard's repeat delay: rs-rich-doom's core gap 4.
  • Noctiluca 0.0.1 (new), a terminal web browser. A page opened in a sandboxed headless Chrome, drawn in an intuiTUIve app: Chrome's layout snapped to cells, or reader mode; forms as stock intuiTUIve widgets, link hints, an address bar with keywords, back and forward, find, bookmarks, per-site keys and the F1 overlay of every action. Sound is off unless asked. Its guide has eleven pages: getting started, reading, forms, every key and action, media, configuration, privacy, the library, troubleshooting and what works today, with recordings on every page and one against a real Chrome. Found while writing it and fixed: a run's temporary Chrome profile is now removed on every way out (it used to stay in the temporary directory), the projection no longer shows the previous page after one is opened from the o prompt, and none in [keys] unbinds a key while a bad key name skips only its own line. rs-rich-embed gains DevTools::call and listen, and switches for audio, downloads and a kept profile, all off by default.
  • Memo in the cartoon look, and a spot for every crate. Each crate's README and the crate tables show Memo with a prop that says what the crate does.
  • The recorder. A tape writes an APNG only when its Output line or --format names it (0.0.22's decision 2), and a late read keeps a frame for each update inside it. macOS still keeps fewer frames: a known issue.

A default build of rs-rich still renders exactly like upstream rich 15.0.0, now against many more golden cases than before.

Independent package versions

Package Version Release role
rs-rich 0.0.12 Parity: twelve core rows, the widget, live and document rows, scope.py, pretty.py's layout; Live's alternate screen and FileProxy; Console::print_objects, line, rule, out, print_json, measure; more RichError variants
rs-rich-macros 0.0.6 Manifest only; requires core 0.0.12
rs-rich-plugin-api 0.0.6 Requires core 0.0.12
rs-rich-art 0.0.15 Manifest only; requires core 0.0.12
rs-rich-diagram 0.0.5 Manifest only
rs-rich-mermaid 0.0.8 Manifest only
rs-rich-lumis 0.0.6 Manifest only
rs-rich-ext 0.0.19 load_wasm_bytes; requires core 0.0.12
rs-rich-micro 0.0.8 Manifest only
rs-rich-record 0.0.10 APNG only when asked (Formats::DEFAULT, Recording::default_formats); every update of a late read kept as a frame
rs-rich-interact 0.0.10 Binding and Command gain unavailable; Palette::set_commands, Palette::focus_id, Help::set_bindings
rs-rich-ratatui 0.0.7 Manifest only
rs-rich-intuituive 0.0.7 Flush and plain-text titles, tree_table, min_size_with and min_size_forms, focus_scope_reveal, Holds; live labels, CheckState, SelectOption<K>, the live action list (action_overlay)
rs-rich-embed 0.0.5 DevTools::call and listen; audio, download and profile switches, off by default; no engine end on a refused history request
rs-rich-web 0.0.5 Manifest only; requires intuituive 0.0.7 and embed 0.0.5
rs-rich-data 0.0.6 Manifest only
rs-rich-marketplace 0.0.1 New: the signed index, listings, resolution, verified installs and the lockfile
noctiluca 0.0.1 New: the terminal web browser's reader phase
rs-rich-cli 0.0.22 rich plugin (with browse and publish); wasm-plugins on by default; requires the versions above

The Python package, rs-rich 0.0.10 on PyPI, builds against these crates. Its AccessState gains mixed, and print(emoji=False) keeps emoji codes.

Migration

Breaking changes in 0.0.x crates (the CHANGELOG has each):

  • New public fields. A struct literal of these needs the new field: rich_interact::Binding and rich_interact::Command (unavailable: None), rich_intuituive::AccessState (mixed: false) and rich::r#box::Box (ascii). Their constructors and builders are unchanged, and AccessState is Default.
  • rich::RichError has new variants (Console, Style, MissingStyle, StyleStack, NotRenderable, Live): an exhaustive match needs arms for them, or a wildcard.
  • wasm-plugins is a default feature of rs-rich-cli. A build that must not carry the WASM runtime names its features with default-features = false.
  • Rendering that now matches upstream (each was a recorded divergence or a parity gap):
  • a renderable without its own measure measures as (0, max_width), so Columns and Markdown in a table cell size as upstream sizes them;
  • a printed Screen, a printed LiveRender and a titled Rule whose end has no newline end without a line break (Renderable::print_end_line, which defaults to true);
  • a zero height on Panel and Align, and a zero width on Align, is read as unset; Constrain with a zero width renders nothing;
  • Text::from_markup replaces :emoji: codes; AnsiDecoder drops private escapes and splits lines as rich 15.0.0 does;
  • paging drops hyperlinks unless asked (Console::page_with_links);
  • prompts parse numbers as Python does and take the default only for an empty answer.
  • Tapes: a tape with no Output line no longer writes an APNG. Add Output apng (or --format apng) to keep one.
  • Behaviour: running a binding from intuiTUIve's palette checks its condition again, so a command that stopped applying after the palette opened no longer runs.

Release test

  • The release gate (scripts/validate_release.py --tag release-0.0.22) on main at 9f8ea4b with the notes on top, in a Linux container: fmt; clippy -D warnings with default features, --all-features, and the CLI without default features; cargo test --all (5,851 passed, 11 ignored, none failed), the CLI without default features (559 passed) and the plugin loaders (30 passed); the four release script suites (142 tests); the version tables, RELEASES.toml and the CLI reference up to date; cargo check --workspace --locked; check_packages.py (all 19 crates packaged and verified); release.py plan release-0.0.22 selecting all 19. The tests were built without debug info, as the container's disk could not hold a debug build of the workspace's tests.
  • The golden fixtures (38 files, 4,766 cases) regenerate byte for byte from rich 15.0.0.
  • The docs tapes: all 68 tapes, 257 screenshots, match with a release build of the CLI and the examples, built with CI's features. The eleven demo clones are intuiTUIve's examples: their suites ran in cargo test --all, and their tapes are among the 68.
  • The docs: mkdocs build --strict for both sites.
  • The declared MSRV: CI's checks on Rust 1.90 (the workspace with every feature, and the CLI with CI's features) and on 1.91 (rs-rich-lumis, and the CLI with every feature).
  • After the Noctiluca guide and fixes were merged (66689219): fmt, the version and cohort checks and cargo check --workspace --locked; clippy -D warnings with every feature on noctiluca, rs-rich-embed and rs-rich-interact, the three crates the fixes touch, and their tests (noctiluca with fake-engine, rs-rich-embed with chrome); the four Noctiluca tapes (21 screenshots) with CI's build; and mkdocs build --strict. The fixes were also run against a real Chromium. CI runs the rest on this PR.
  • CI on #770's head (531b9ba, which holds #768 too): 73 checks passed and 2 skipped (the docs publication jobs), including the feature matrix, the live browser job and the macOS and Windows jobs this pass did not run.

Not in this release

  • console.py stays partial. PORTING lists what it lacks (log, status, the screen() context, capture, record=, render hooks, size setters and the colour system from the environment, among others).
  • One fuzzer case remains a known divergence: #769, an ordered Markdown item holding only an empty link under justify="full".
  • The recorder's frame rate on macOS is still not fixed; the frame-rate test keeps its lower bound on Linux only.
  • The marketplace's official keys are not compiled in yet: until they are, --marketplace-keys or RICH_MARKETPLACE_KEYS names the keys to trust.
  • Moved to 0.0.23 by the maintainer: rs-rich-doom's core gaps (all but gap 4), the rich-rst 2.2.0 pin, and Memo's illustrations revisited.

Known limits

See Known issues, which applies to this release. Still open: the recorder keeps fewer frames on macOS and Chrome's DevTools port on Windows.

Publication

  1. On 2026-10-25, on main at 6e8e82e (#771), the 19 record tags (rs-rich-v0.0.12, rs-rich-macros-v0.0.6, rs-rich-plugin-api-v0.0.6, rs-rich-art-v0.0.15, rs-rich-diagram-v0.0.5, rs-rich-mermaid-v0.0.8, rs-rich-lumis-v0.0.6, rs-rich-ext-v0.0.19, rs-rich-micro-v0.0.8, rs-rich-record-v0.0.10, rs-rich-interact-v0.0.10, rs-rich-ratatui-v0.0.7, rs-rich-intuituive-v0.0.7, rs-rich-embed-v0.0.5, rs-rich-web-v0.0.5, rs-rich-data-v0.0.6, rs-rich-marketplace-v0.0.1, noctiluca-v0.0.1, rs-rich-cli-v0.0.22), then release-0.0.22 and python-v0.0.10, each with Release: 0.0.22 and Commit: lines (17:15 UTC).
  2. The one release.yml run for release-0.0.22 ran the full gate, then published every crate in RELEASES.toml's order and verified each version from crates.io (the publish job ran from 17:53 to 18:07 UTC; the uploads from 17:59 to 18:01; run). The two new crates, rs-rich-marketplace and noctiluca, went up with the crates-io environment's token, as a crate's first version must; every other crate went up through Trusted Publishing.
  3. The Python package, python-v0.0.10 on 6e8e82e, through pypi-release.yml: built with the crates, then published to PyPI, finishing at 17:55 UTC (run).
  4. The docs workflow ran on the tag push and succeeded.

Follow-up

  • Registry. The maintainer adds the crates.io Trusted Publishing entries for rs-rich-marketplace and noctiluca (repository buchochelliq-labs/rs-rich-cli, workflow release.yml, environment crates-io), so their next versions publish without the token. See Registry authentication.