Skip to content

0.0.23 cohort: faster frames, pictures and video in Noctiluca, and the marketplace goes live

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

0.0.23 began as what 0.0.22 handed on and grew as the maintainer brought items forward from later releases. The core finishes porting console.py, the last partial library row, and gets faster while its bytes stay the same. intuiTUIve paints with fewer bytes and gains what rs-rich-doom built for itself. Noctiluca draws a page's pictures, GIFs and video, and was tried on eight real sites. The plugin marketplace is live and signed, with its own website. The 0.0.23 plan has every item; what moves on is in 0.0.24.

  • console.py fully ported. quiet, upstream's size rules and setters, colour and terminal detection from the environment (COLORTERM, TERM, TTY_COMPATIBLE, FORCE_COLOR), on_broken_pipe, capture, record= with the text, HTML and SVG exports, log, render hooks, set_live/clear_live, screen() and status(). Progress.log works, and a nested Live draws inside the first display, as in rich 15.0.0. New golden fixture console.tsv. Every library row in PORTING is now complete or a recorded divergence.
  • Themes reach Markdown. A theme's markdown.* styles show in rich -m, and --theme-style repr.number=… is no longer overridden by a fixed colour.
  • rich renders faster, byte for byte the same. Fast paths beside the ported code (colour names, character widths, markup tags, style normalisation, word wrapping), each pinned by a test, and Segment.text and Color.name as CompactStrings. A table of markup redraws in a third of the time, and drawing a colour swatch as segments allocates 111 times a frame, down from 5,871. The goldens, the parity tables, the differential fuzzer and a byte compare of 30,081 cases against the previous binary all agree.
  • rs-rich-ext's fast path. surface::Draw writes cells straight into a Screen; the charts, gauges, heatmaps and the other viz renderables implement it, and intuiTUIve draws with it by default. Every cell changing at 80x24 takes 104 µs, against ratatui's 340.
  • Paint strategies, with best the default. best scrolls rows that moved together with the terminal's own scrolling and otherwise paints cells: a log scrolled one row sends 147 bytes a frame, down from 4,013. App::paint_strategy and RICH_PAINT_STRATEGY choose another, and App::timedemo replays a script on the driver's clock and reports every frame. A benchmark gate keeps best within 5% of the best fixed strategy.
  • Long pages and endless feeds. scroll_virtual builds only the items near the view, with no row cap, and loads more near the end.
  • rs-rich-doom's core gaps. Raw Kitty frames, Sixel in a palette of one's own, pixel cells in their own colours, a picture fitted in a pixel buffer, a worker in lockstep with the app's clock, sound without a sound card, a voice mixer and an output that calls a renderer. See the gaps.
  • Sound in recordings. Set Audio on in a tape, or rich record --audio, records what an intuiTUIve app plays: an MP4 gets an AAC track, and the other formats a WAV beside them. Off by default.
  • Pictures and video in Noctiluca. Pictures are drawn where the page puts them, in Kitty, iTerm2, Sixel or half blocks; animated GIFs move; a playing video plays in its region, within the terminal's byte budget. Media controls, a header and status bar after the proof of concept, and about:noctiluca. See Media.
  • Noctiluca follows live logs (role=log, aria-live) and has eleven named terminal adaptations, each one switchable (Adaptations). Long pages and feeds scroll with no row cap.
  • Noctiluca on real sites. A gallery of BBC News, Wikipedia, Hacker News, MDN, The Guardian, GitHub, YouTube and Twitch. Recording it fixed seven layout bugs. Its own video fixtures replace the old garbled clip, and the guide plays Noctiluca's promo.
  • WebP everywhere. rich image, rich view, rich gif, --diff, rs-rich-art, micro assets and Noctiluca read WebP, and an animated WebP plays like a GIF. Sixel keeps a picture's exact colours when it has 256 or fewer.
  • The plugin marketplace is live. Its index is signed, and the official keys are compiled in, so rich plugin trusts it with no flags. It lists the Mermaid, DOT and lumis crates, two official WASM plugins (textcase and wordplay), five themes and three micro packs. It shows downloads, ratings and reviews, and rich plugin search filters by kind, tier and recommendation. Installed themes work with --theme, and installed micro packs load on every run. The marketplace has its own site, and the docs have a Marketplace page.
  • HTTPS trusts the system's certificates. Plugin installs, rich <url> and the download statistics work behind a TLS-inspecting proxy, follow redirects one at a time and keep one timeout for the whole chain.
  • rich --rst follows rich-rst 2.2.0: a docutils document tree with its transforms, real tables, numbered footnotes and the newer directives.
  • API checks and fuzzing, brought forward from later stages. Every crate's public API is snapshotted and checked in CI with cargo-semver-checks. Fuzz targets for every parser that takes outside input run nightly (fuzzing); the three bugs they found are fixed. The Python package's type stubs pass stubtest and mypy --strict.
  • The docs load nothing from other sites: fonts are served with the site, and the header no longer asks GitHub's API for stars.
  • intuiTUIve against Textual. A parity audit maps Textual's widgets to intuiTUIve's and files the gaps as issues #797 to #815, for the releases before 1.0.0.

A default build of rs-rich still renders exactly like upstream rich 15.0.0.

Independent package versions

Package Version Release role
rs-rich 0.0.13 console.py ported; Progress.log; nested Live; themes in Markdown; fast paths; CompactString strings; FORCE_COLOR, TTY_COMPATIBLE and exit status 1 on a broken pipe; AnsiDecoder fix
rs-rich-macros 0.0.7 Manifest only; requires core 0.0.13
rs-rich-plugin-api 0.0.7 Requires core 0.0.13; the textcase example
rs-rich-art 0.0.16 WebP; raw Kitty frames; exact-palette Sixel; the picture and frames features (Picture, Player, ByteBudget, Pacer)
rs-rich-diagram 0.0.6 Manifest only
rs-rich-mermaid 0.0.9 Manifest only
rs-rich-lumis 0.0.7 Manifest only
rs-rich-ext 0.0.20 rst as a document tree (rich-rst 2.2.0); surface::Draw fast path; Surface on best; the faster painter and PaintStats; WASM sandbox fixes; zip 9; rich's widths for U+9FBC to U+9FFF
rs-rich-micro 0.0.9 WebP, animated; zip 9
rs-rich-record 0.0.11 Sound in tapes; raw Kitty frames in the emulator; no frame kept mid synchronized update
rs-rich-interact 0.0.11 A woken session reads a waiting key at once; keymaps look up without allocating; signal-hook 0.4
rs-rich-ratatui 0.0.8 Follows core's CompactString
rs-rich-intuituive 0.0.8 Paint strategies (best the default), timedemo, richer FrameStats; scroll_virtual, damage_since; drawn nodes; rs-rich-doom's gaps (pixel_colors, draw_fitted, stepped_worker, null outputs, taps, Mixer, output_with); sound in tapes; picture_of; a hidden switch page fix
rs-rich-embed 0.0.6 Screencast on demand, cropped frames, screencast off
rs-rich-web 0.0.6 Frame compression (Server::compress); damage-only DOM diffs; the memory limit fix
rs-rich-data 0.0.7 Manifest only
rs-rich-marketplace 0.0.2 Official keys compiled in (KeySet::official); system TLS trust (tls); redirects with one deadline (redirect); the recommended listing key
noctiluca 0.0.2 Pictures, GIFs and video; live logs; terminal adaptations; long pages; WebP; seven fixes from real sites
rs-rich-cli 0.0.23 rich --rst on rich-rst 2.2.0; the marketplace's search filters, statistics, official packages, installed themes and micro packs; record --audio; serve --compress; system TLS trust; WebP; the console contract kept

The Python package, rs-rich 0.0.11 on PyPI, builds against these crates. Its type stubs are complete, and its console size follows upstream's rules.

Migration

Breaking changes in 0.0.x crates (the CHANGELOG has each, and api/semver-breaks.toml lists them with their reasons):

  • rs-rich: Segment.text and Color.name are CompactStrings. Reads compile unchanged. Where a String was moved out, write .to_string() or .into_string(); where one is assigned, add .into(). Segment::new takes impl Into<CompactString>.
  • rs-rich: the console reads FORCE_COLOR and TTY_COMPATIBLE, as rich 15.0.0 does. For the old isatty-only rule, build with .force_terminal(std::io::stdout().is_terminal()).
  • rs-rich: a broken pipe ends the process with status 1. To carry on, give ConsoleBuilder::on_broken_pipe a handler, such as .on_broken_pipe(|console| console.set_quiet(true)). The rich binary keeps exiting 0.
  • rs-rich-ext: rst::parse returns the document tree (rst::Element); Block, Inline and friends are gone. Rendering with RestructuredText::new is unchanged.
  • rs-rich-ext: a painter's Cell is 12 bytes and Copy; read its text with Cell::text(), since the text field is gone.
  • rs-rich-intuituive: FrameStats gains fields, and ImageView and Pixels come from rs-rich-art. Apps paint with best by default; choose PaintStrategy::Cells for 0.0.22's behaviour.
  • rs-rich-record: Recording has a new audio field and Options a new audio switch.
  • rs-rich-ext and rs-rich-micro use zip 9: rs-rich-ext lists an entry whose name cannot be decoded lossily, and rs-rich-micro refuses it.
  • The CLI: rich plugin search's no-match message names the marketplace's site first, and numbers follow --theme-style repr.number alone.

Release test

  • The release gate (scripts/validate_release.py --tag release-0.0.23) on main at b4bcaee with the Textual audit and the notes on top (docs only), in a Linux container on Rust 1.99: fmt; clippy -D warnings with default features, --all-features, and the CLI without default features; cargo test --all (6,124 passed, 17 ignored, none failed), the CLI without default features (593 passed) and the plugin loaders (32 passed); the five release script suites (161 tests); the version tables, RELEASES.toml and the CLI reference up to date; cargo check --workspace --locked; check_packages.py (every crate packaged and verified); release.py plan release-0.0.23 --semver-checks selecting all 19. The tests were built without debug info, to fit the container's disk.
  • The golden fixtures (39 files, 4,806 cases) regenerate byte for byte from rich 15.0.0, in a virtualenv with only rich 15.0.0.
  • The docs tapes: all 75 tapes, 291 screenshots, match with a release build of the CLI and the examples, built with CI's features.
  • 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).
  • CI on #823's head (d1341be, whose tree is main at b4bcaee): every check passed, including the feature matrix, the live browser job, the macOS and Windows jobs, the API and SemVer job, the benchmark gates and the WASM release tests this pass did not run.

Not in this release

  • AVIF is not decoded by rs-rich-art; Noctiluca takes AVIF pictures from Chrome's pixels. An opt-in avif feature is planned for 0.0.24.
  • lumis 0.17 waits: it raises rs-rich-lumis's minimum Rust to 1.95.
  • One fuzzer case remains a known divergence: #769.
  • The signer still pushes with a deploy key; the move to a GitHub App is part of 1.0.0.

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-27, on main at 2624ce5 (#824), the 19 record tags (rs-rich-v0.0.13, rs-rich-macros-v0.0.7, rs-rich-plugin-api-v0.0.7, rs-rich-art-v0.0.16, rs-rich-diagram-v0.0.6, rs-rich-mermaid-v0.0.9, rs-rich-lumis-v0.0.7, rs-rich-ext-v0.0.20, rs-rich-micro-v0.0.9, rs-rich-record-v0.0.11, rs-rich-interact-v0.0.11, rs-rich-ratatui-v0.0.8, rs-rich-intuituive-v0.0.8, rs-rich-embed-v0.0.6, rs-rich-web-v0.0.6, rs-rich-data-v0.0.7, rs-rich-marketplace-v0.0.2, noctiluca-v0.0.2, rs-rich-cli-v0.0.23), then release-0.0.23 and python-v0.0.11, each with Release: 0.0.23 and Commit: lines (17:50 UTC).
  2. The one release.yml run for release-0.0.23 ran the full gate (61 jobs, all passed), then published every crate in RELEASES.toml's order and verified each version from crates.io (the uploads from 18:42 to 18:44 UTC, verified by 18:52; run). Every crate went up through Trusted Publishing, rs-rich-marketplace and noctiluca for the first time.
  3. The Python package, python-v0.0.11 on 2624ce5, through pypi-release.yml: built with the crates, tested, then published to PyPI at 19:12 UTC (run). Its first attempt's macOS arm64 wheel never got a runner and was cancelled; the re-run of that job built it, and the rest of the run went on from there.
  4. The docs workflow ran on the tag push.

Follow-up

  • The marketplace lists the new versions of the official crates (mermaid 0.0.9, dot 0.0.6, lumis 0.0.7), and its RS_RICH_CLI_REF moves to release-0.0.23, through a pull request to rs-rich-marketplace.