Skip to content

0.0.19 plan: layout, editing, isolation, and docs you can see

Status: proposed on 2026-10-12, after the 0.0.18 cohort and rs-rich 0.0.6 on PyPI were published. It is stage 1 of the plan to 1.0.0. Its four framework items were decided with the maintainer on 2026-10-08 (see the 0.0.18 plan's Not in this release); the docs workstream was asked for on 2026-10-12. The decisions below were answered on 2026-10-12. The tracker is #732.

Goal: close the gaps a comparison with other terminal frameworks found, take the last known security edge out of rs-rich-embed, and make the docs show what the code does on every page.

  • Flex alignment: align and justify, and wrap, in layout and in the stylesheet, as Ink and iocraft have.
  • A text-area widget: a multi-line editor with syntax highlighting, selection and undo, as Textual has.
  • One process per browser session in rs-rich-web, as textual-serve isolates each session.
  • A Surface drawing handle in rs-rich-ext: intuiTUIve's screen and damage-based painter moved down, with a small surface.draw(|s| …) API, so plain rich users get positioned rendering that repaints only what changed, as ratatui's Frame gives.
  • The Chrome web view's DevTools over a pipe, not a port other local users can reach. Moved to 0.0.20 on 2026-10-13: see workstream 5.
  • Docs with an example and a picture on every page, and recordings of everything 0.0.18 added.

What stays the same: core. Nothing under crates/rich/src changes, and every golden and the differential corpus stay byte-identical. The intuiTUIve-against-ratatui benchmark gate keeps passing, now also with the Surface underneath.

Research behind this plan

  • The framework comparison (2026-10-08) behind the 0.0.18 plan's list: Ink and iocraft lay out with flexbox alignment; Textual has a TextArea with highlighting; textual-serve runs a process per browser session; ratatui's Frame gives positioned, diffed drawing without a framework.
  • The code today (main at e48551e):
  • rich_intuituive::layout solves sizes along one axis (fixed, percent, flex, auto, with min/max clamps, as CSS resolves flexible lengths) and places children from the start; there is no cross-axis alignment, no main-axis distribution and no wrapping. sheet.rs knows size, padding, gap, grid-rows, display and dock, none for alignment.
  • rich_interact::TextArea is a plain multi-line editor (rich write, #493): grapheme-aware editing and wrapping, no highlighting, selection or undo. intuiTUIve can host it as a text box.
  • rs-rich-web runs each browser session's app on a thread in the server's process (http.rs); serve_command already runs each session's program on its own PTY.
  • rich_intuituive::screen holds the cell Screen and the Painter that writes only damaged cells; rich_ext::frame already has Frame, its cell diff and graphics placement planning, which a Surface can build on.
  • rs-rich-embed's Chrome engine starts Chrome with --remote-debugging-port=0, the known limit in the 0.0.18 notes.
  • A docs audit (2026-10-12) of every page under docs/:
  • the Python site's 40 pages have examples but not one picture of what they print;
  • these Rust guide pages have no screenshot at all: rs-rich-data (all three pages), ratatui interop, custom components in rs-rich-interact, sources, code highlighters and transforms in rs-rich-ext, intuiTUIve's web, embed and Python pages, getting started, tutorial step 6, and the overviews of the guide, the core guide, rs-rich-ext and micro assets; explorers in rs-rich-interact shows one of the explorers it describes;
  • two guide pages are procedures, not output, and stay without one: the screen-reader checklist and the CLI smoke test;
  • of 33 tapes, none shows a 0.0.18 feature: stylesheets, table sorting and resizing, the lazy tree, tooltips, drag-and-drop, accessible and linear mode, rich serve or the terminal pane.

Workstreams

Each workstream lands as its own pull request, or a short stack, with an entry under Delivered. Placement follows AGENTS.md: the framework in rs-rich-intuituive, the drawing handle in rs-rich-ext, browser serving in rs-rich-web, the web view in rs-rich-embed, Python in crates/rich-py.

1. Flex alignment: intuituive

  • Cross axis: .align_items(Align::Start | Center | End | Stretch) on a container and .align_self(..) on a child. Stretch is today's behaviour and stays the default.
  • Main axis: .justify(Justify::Start | Center | End | SpaceBetween | SpaceAround | SpaceEvenly), applied to what the flexible children leave; a container with a flexible child has nothing left, as in CSS.
  • Wrapping: .wrap() on a row or column moves children that do not fit to a new line, each line solved as today; align_content places the lines.
  • In the stylesheet: align-items, align-self, justify-content, flex-wrap and align-content, with CSS's names and values; code wins over the sheet, as for every other property. The inspector shows them.
  • Tests: solver unit tests against the rectangles CSS flexbox gives for the same boxes, each alignment in rows and columns, wrapped and not, and with min/max clamps.

2. A text-area widget: intuituive, interact, ext

  • text_area(text: Signal<String>) with .language("rust") for highlighting through the code highlighters (syntect by default, lumis when registered), .line_numbers(), .soft_wrap(bool) (horizontal scrolling when off), .read_only() and .placeholder(..).
  • Editing: grapheme-aware caret and editing shared with rich_interact::TextArea by moving its buffer into rich_interact::edit::Buffer, which both use; word moves (Ctrl+←/→), Home/End, PageUp/PageDown, indent and outdent with Tab and Shift+Tab.
  • Selection: Shift with any move, the mouse (drag, double-click a word, triple-click a line), select all; copy, cut and paste through OSC 52 and bracketed paste.
  • Undo and redo (Ctrl+Z, Ctrl+Y or Ctrl+Shift+Z), grouping typed runs.
  • Large text: stored by line, only the lines in view highlighted and drawn, so a 100,000-line file scrolls without a stall (a benchmark).
  • Accessibility: a multi-line text box role, the caret line announced in linear mode.
  • Python: rs_rich.tui.text_area.

3. One process per browser session: web

  • serve runs each session in its own process. The server re-executes its own binary with RS_RICH_WEB_SESSION set; the same serve(addr, app) call in that child runs the app over a framed pipe instead of binding a port, so an app needs no change. The parent keeps the WebSocket and forwards input, resize, paste and output frames.
  • Limits per session: the process group is killed when the tab closes or the server stops (as serve_command does since 0.0.18), and an optional memory and CPU-time limit (setrlimit on Unix).
  • Isolation::Threads keeps 0.0.18's behaviour for programs that cannot re-execute themselves (tests, embedded uses).
  • Python: rs_rich.tui.serve re-executes the interpreter with the same script and arguments.
  • Tests: a crash, a hang and a memory blow-up in one session leave the others and the server running.

4. A Surface drawing handle: ext, intuituive

  • rich_ext::surface::Surface owns a cell buffer, the previous frame and a writer. surface.draw(|s| { s.render(rect, &renderable); s.set_cursor(..) }) renders any rich renderable into a rectangle, then writes only the cells that changed (rich_ext::frame's diff), in synchronized output where the terminal has it.
  • Alternate screen or inline (a fixed number of rows below the prompt), a resize handled by redrawing everything once, and graphics placements carried over from frames.
  • intuiTUIve builds on it: rich_intuituive::screen::{Screen, Painter} become re-exports of the ext types (names kept), so the benchmark gate measures the same code plain users get.
  • Python: rs_rich.ext.Surface.
  • Docs: a guide page, "Positioned drawing", with the ratatui comparison.

5. DevTools over a pipe: embed

  • Chrome starts with --remote-debugging-pipe; the engine talks CDP over file descriptors 3 and 4 (NUL-separated JSON) and no port is opened. The descriptors are passed with the command-fds crate, so the crate keeps unsafe_code = "deny".
  • Windows too (decision 6): Chrome takes the pipe there as two inherited handles named by --remote-debugging-io-pipes, which std cannot mark inheritable. The pipe ships on all three platforms or on none: if Windows cannot pass the handles without unsafe in this workspace (through a maintained crate, as command-fds does on Unix), the whole workstream moves to 0.0.20 and the port stays everywhere, still documented as a known limit.
  • Tests: no listening socket appears while a view is open, on Linux, macOS and Windows, and the existing CDP tests pass over the pipe.
  • Moved to 0.0.20 (2026-10-13). Windows fails the test above. Chrome takes the pipe there either as --remote-debugging-io-pipes=READ,WRITE (two inheritable handle values in one switch) or as C runtime descriptors 3 and 4 (STARTUPINFO.lpReserved2). std can do neither, os_pipe and filedescriptor make handles that are not inheritable, and the one crate that passes handles to a child, windows-spawn (one 0.1.0 release, August 2026), puts each handle in an argument of its own and cannot write both into one switch. So the port stays on every platform, still a known limit, and rs-rich-embed's 0.0.19 change is its manifest only. The workstream is first in line for 0.0.20 once Windows has a way through.

6. Docs: an example and a picture on every page

Docs only: no package versions move for this workstream, and it starts at once, beside the others.

  • The Python site gets its pictures. Each Python page's examples move into crates/rich-py/examples/docs/<page>.py, rendered by a new scripts/capture_python_docs.py with Console(record=True) into docs/python/media/*.svg, and shown under each example. --check runs in the Python workflow, so a picture that no longer matches its code fails CI, as capture_guide.py --check does for Rust.
  • Every Rust guide page shows output. A guide_*.rs example and its screenshot for each page the audit listed, the overviews included, and for each explorer the explorers page describes, captured by scripts/capture_guide.py like the rest. The two procedure pages are the exception.
  • Recordings of 0.0.18: tapes for the stylesheet (with hot reload), table sorting, resizing and the cell cursor, the lazy tree, tooltips and drag-and-drop, accessible and linear mode, rich serve and the terminal pane; and for 0.0.19's own features as they land (alignment, the text area, the surface). Each is on the recordings page and on its guide page.
  • The browser pages: screenshots of the xterm.js page and the DOM renderer, taken with Playwright from a scripted session, on the web page.
  • Runnable, not pasted: every new example is a compiled example program or a script CI runs, so the code on a page cannot drift from the code that made its picture.

Added on 2026-10-17 and after

More workstreams, taken into this release after the release test while nothing was tagged. Each lands in a package this cohort already moves, so the version numbers below stand; the release test runs again over them.

7. Windows: a session's process tree: web

Closes the Windows gap the release PR's review found: ending a session, or a killed server, ended only the session's process there.

  • Each session's process goes in a job object the server creates and holds the only handle to, with kill-on-close: ending the session drops it, and the system closes it when the server dies, so the process and everything it started end either way, with no watcher.
  • Through win32job, a safe API, so the crate keeps unsafe_code = "deny". It cannot set a job's memory or CPU limits, so those stay Unix only.
  • A new required CI job, windows-web, runs the Windows tests: a closed tab, a stopped server and a killed server each end the session's process and the program it started.

8. Responsive layout, an error state and snapshot tests: intuituive

Issues #712 and #713.

  • hide_below(columns), show_below(columns) and their height forms, and @media (max-width: N) in the stylesheet (CSS's names, cells for units); viewport() gives the size in a signal.
  • :error (:invalid too) set by error_when(condition), styled by the theme's error and border.error styles, and aria-invalid for assistive technology.
  • intuituive::testing: assert_screen! snapshots of the screen as text (or markup), updated with UPDATE_SNAPSHOTS=1, and the driver's press, type_text and assert_focused.
  • Python: the same builders, viewport() and the snapshot helpers.

9. Wide and long tables: interact, intuituive, cli

Issues #717 and #716.

  • scroll_x() and freeze_columns(n) on intuiTUIve tables and TableSelect: columns at their natural widths, scrolled across, with the header aligned, ‹ and › for what is out of view, and the cursor followed.
  • Viewport::sticky(n), Pager::sticky_header(n) and Pager::sticky_table_header(true), which finds a table's header in rendered lines; rich pager keeps a table's header on top by default. rich ... --pager still runs the system pager, as upstream does.

10. Reorderable lists and date and time pickers: interact, intuituive, cli

Issues #720 and #719. Each is in new modules (components/reorder.rs, components/date.rs, datetime.rs in interact; reorder.rs, datetime.rs in intuiTUIve).

  • Reorder: rich_interact::Reorder, and .reorderable(on_reorder) on an intuiTUIve list. Alt or Ctrl with the arrows, K/J for any terminal, Space to grab, and a drag; without a terminal, the new order as numbers.
  • Dates and times: rich_interact::datetime (Date moved from intuiTUIve, Time, DateTime; hand-rolled, no dependency), DatePicker, TimePicker and DateTimePicker with a range and a validator; the calendar's range, time_picker and datetime_picker in intuiTUIve.
  • rich date: a CLI binary-boundary convenience composing the public interact pickers, like rich color; recorded in docs/PORTING.md.
  • Python: rs_rich.interact.Reorder, DatePicker, TimePicker, DateTimePicker; Node.reorderable, time_picker, datetime_picker and calendar_with(min=, max=) in rs_rich.tui.

11. The inspector: selection, timings and a trace: intuituive

Issues #714 and #721.

  • Shift+F12 (or a click on the panel) gives the inspector the keys: select a node in the tree or pick one with the pointer, see its bounds, constraints, who set them, its states and the rule that hides it, with the node outlined on the app.
  • Per-node draw counts and times, per-frame totals, and a trace of keys, the mouse, focus, screens, signal writes (by the file:line that made and wrote the signal), timers and tasks, in a ring of 1,000 entries.
  • Nothing is measured while the inspector is closed; an app with a closed inspector sends the same bytes as one without.
  • termwiz on Windows in CI's backends job; the files and scope examples take --backend NAME.

12. Org, AsciiDoc and man pages: ext, cli

Issues #591 to #595.

  • rich_ext::document: one registry of document formats, each with extensions, a content sniffer and a renderer that reports diagnostics. Markdown, reStructuredText, Org, AsciiDoc and man are built in; a plugin's source renderer joins through an adapter.
  • Org, AsciiDoc and man(7)/mdoc(7) parse into one small block and inline model, drawn in the look of core's Markdown with its markdown.* styles.
  • No input makes a parser panic; nesting is capped, inline parsing is linear, and a stress test feeds garbled input to all three.
  • rich --org, --asciidoc and --man, the same names as subcommands, and detection by extension, with the content agreeing for .1 to .9. A tape shows each format.

13. What ratatui draws: intuituive, ext

A comparison of intuiTUIve with ratatui 0.30, intuiTUIve against ratatui, ranks the gaps; three batches close the ones that fit this release, and ports of ratatui's demos show them.

  • Blocks, text and layout: titles on both edges, every border kind, shadows, a style on any node, text alignment and overflow, ratio sizes and layout by hand, collapsed borders, drawing over children, layers and theme styles for a subtree.
  • Widgets, input and the shell: list and table options (symbols, directions, footers, gaps, multi-line rows, no selection), scrollbars on every scrolling widget, tab strip options, single-line and number inputs, modal shadows and backdrops, printing above an inline app, and quitting with a value or an error.
  • Charts, the canvas and colour (rs-rich-ext): charts fill their node; fill and line gauges, taller sparklines, grouped bars, area charts, more canvas markers, a world map from Natural Earth's public-domain coastlines, and the Tailwind and Material palettes; the calendar's events, surrounding days and first weekday.
  • Demos: ratatui's demo and demo2, its widget galleries and some of its apps, rebuilt on intuiTUIve's API with credit to ratatui (MIT).

14. rich log: filters, follow mode and groups: ext, interact, cli

Issue #724.

  • --level warn+ and --where 'latency_ms>500' (comparisons, regular expressions, presence), applied while the records stream.
  • --follow, which reads a truncated or replaced file again; repeats folded into one line with a count; groups by trace, span or correlation id; a summary of levels and the groups with errors.
  • Plain-text lines read by logfmt, LEVEL message, RFC 3339 and access-log parsers; a line none reads joins the record before it or shows as text.
  • --output jsonl, --redact, and -i, a live view in rs-rich-interact (LogView) with a filter line, pause and expandable records.

15. rich deps: sizes, baselines and licence policy: ext, cli

Issues #706 and #704.

  • --sizes: each crate's artifact size from a target directory, or from cargo-bloat's JSON, with --source-sizes.
  • --baseline with --timings or --sizes: a unit-by-unit comparison that fails (exit 5, as --audit does) when a unit grows past both a percentage and a floor; --save keeps a report for the next run.
  • --policy: allow and deny lists, in rich's format or cargo-deny's [licenses], with SPDX OR, AND and WITH read properly; conflicts with the root crate's licence, labelled a heuristic; and --sbom for SPDX and CycloneDX JSON.

16. Charts that read aloud, distributions and small multiples: ext

Issues #726 and #702.

  • Every chart describes itself for a screen reader (AccessibleText): its kind, ranges and key values; intuiTUIve's described(..) reads it in linear mode.
  • Distribution and DistributionSet (quantiles, a box strip) and SmallMultiples, a grid of charts on one scale.
  • VirtualTable windows columns as it does rows, with frozen columns, and fetches only the columns in view.

17. Nested JSON Lines profiles and style rules: data, ext, cli

Issues #705 and #701.

  • rich profile reads JSON Lines path by path (a.b, items[].id), with presence, absence and the mix of JSON types per path; --flat keeps the old profile.
  • --style-rule 'latency>500:bold red', --rules FILE and [[rules]] in config profiles style tables by value; StyleRule in Python.

18. Micro assets over two rows: micro

Issue #711.

  • Sizes up to 4×2; the rows a two-row asset needs are reserved in text, tables and panels, and drawn by Kitty, iTerm2, Sixel and half-blocks.

19. A PNG of the output and a redaction preview: record, ext, cli

Issues #723 and #722.

  • --export-png beside --export-svg and --export-html, drawn by the tape screenshots' renderer, so the same output gives the same image.
  • --redact-preview on rich capture and rich log: every match, its rule and its replacement, and nothing written; Redactor::preview in the library.

20. An approval queue and a jump-to-anything navigator: interact, cli

Issues #471 and #484.

  • ApprovalQueue: approve, reject or skip items one by one or marked together, with reasons, a detail pane and a report; rich approve writes decisions as JSON Lines.
  • Jump: one fuzzy box over files, headings, symbols, commands and recent picks, in groups with prefixes; rich jump.

21. Banners, playback controls and shell integration: cli

Issues #708 and #715.

  • rich banner: FIGlet text with fonts, gradients, alignment and fitting.
  • --speed, --fps, --max-frames and --max-duration for GIFs.
  • rich shell init bash|zsh|fish|powershell: completions, OSC 133 marks and a status line after each command; rich shell status and link.

Order of work

  1. Docs (6) starts at once; it needs nothing else.
  2. Surface (4) next among the code, because it moves intuiTUIve's screen and painter, which alignment and the text area draw through.
  3. Alignment (1) and the text area (2) in parallel after it.
  4. Process isolation (3) and the DevTools pipe (5) at any time; neither touches the others.
  5. The tapes for 1, 2 and 4 join workstream 6 as each lands.
  6. The release test: three independent audits, as for 0.0.18.
  7. The additions (7 to 21) after it, in parallel, then the release test again over them.

Decisions

Decided on 2026-10-12 (5 on 2026-10-08):

  1. Stylesheet names: CSS's (align-items, justify-content, flex-wrap, align-self, align-content), as the sheet is a CSS subset.
  2. Wrapping: in this release, with alignment.
  3. The text area's editing model: one buffer, edit::Buffer in rs-rich-interact, shared with rich_interact::TextArea, so grapheme and paste fixes land once.
  4. Process isolation by default for serve, with threads as the opt-out.
  5. Surface in rs-rich-ext. It bumps rs-rich-ext, so every crate that depends on it moves too (see Package impact).
  6. The DevTools pipe ships on every platform or none. Windows does not keep the port while Linux and macOS move to the pipe; behaviour stays the same everywhere (see workstream 5).
  7. Browser screenshots are regenerated at release time, not checked in CI, since browser font rendering differs between machines. The terminal recordings stay CI-checked.
  8. More in this release (2026-10-17): Windows process-tree cleanup, intuiTUIve's responsive layout, error state and snapshot tests, wide and long tables, and reorderable lists with date and time pickers (workstreams 7 to 10); then the inspector and Org, AsciiDoc and man pages (11 and 12).

Not in this release

  • An SSH front end for served apps (after process isolation).
  • A visual stylesheet editor, sibling selectors, !important, animations or transitions in the stylesheet.
  • Platform accessibility bridges (AT-SPI, UI Automation, the macOS API).
  • Anything in core.
  • Other feature work from the theme milestones; 0.0.20 and 0.0.21 are the parity releases.

Package impact

Package Proposed Reason
rs-rich-ext 0.0.16 Surface; Org, AsciiDoc and man pages; ratatui's charts; the log stream; dependency sizes, comparisons and licence policy
rs-rich-interact 0.0.7 edit::Buffer, wide tables and sticky headers, Reorder, date and time pickers, LogView; requires ext 0.0.16
rs-rich-intuituive 0.0.4 Alignment, the text area, the surface; responsive layout, :error, snapshots, wide tables, reordering, date and time pickers
rs-rich-web 0.0.2 Process per session, with job objects on Windows
rs-rich-embed 0.0.2 Manifest only: requires intuituive 0.0.4 (the DevTools pipe moved to 0.0.20)
rs-rich-micro 0.0.5 Two-row assets; requires ext 0.0.16
rs-rich-record 0.0.7 PNG rendering of any output; requires ext 0.0.16
rs-rich-data 0.0.3 Nested JSON Lines profiles; requires ext 0.0.16
rs-rich-ratatui 0.0.4 Manifest only: requires interact 0.0.7
rs-rich-cli 0.0.19 rich date, rich pager --sticky, --org, --asciidoc, --man, rich log's filters, follow mode and groups, rich deps --sizes, --baseline and --policy; requires the versions above
rs-rich (PyPI) 0.0.7 text_area, Surface, process isolation, and the additions' bindings

Core, the plugin API, macros, art, diagram, mermaid and lumis do not change.

Delivered

Workstream PR Merged as
1. Flex alignment #745 d019885
2. Text area #746 8ecec8b
3. Process per session #747 0c6df8d
4. Surface #744 208d350
5. DevTools pipe — Moved to 0.0.20 (why)
6. Docs: examples and recordings #734, #748 (the surface recording) 28a13cb, f817c76
7. Windows process trees The release PR (#750)
8. Responsive layout, :error, snapshots The release PR
9. Wide and long tables The release PR
10. Reordering, date and time pickers The release PR
11. The inspector The release PR
12. Org, AsciiDoc and man pages The release PR
13. What ratatui draws The release PR
14. rich log The release PR
15. rich deps sizes, baselines, policy The release PR
16. Charts that read aloud The release PR
17. Nested profiles, style rules The release PR
18. Two-row micro assets The release PR
19. PNG export, redaction preview The release PR
20. Approval queue, navigator The release PR
21. Banners, playback, shell integration The release PR
Release test Audit fixes in #747, #748 and #749 (04d7cff); notes and validation in the release PR

Review focus

  • Alignment against CSS: every alignment and wrap case matches what a browser lays out for the same boxes; nothing changes for a container that sets none (Stretch, Start, no wrap).
  • The text area under load: a 100,000-line file, a long line without wrapping, wide and combining characters, a paste with controls in it, undo across a paste.
  • Isolation for real: a session that crashes, hangs or eats memory does not touch the others; no orphan process survives a closed tab, a server stop or a signal.
  • The Surface moves without a behaviour change for intuiTUIve: every intuiTUIve test, tape and the benchmark gate pass unchanged.
  • No unsafe enters the workspace. (The web view's port stays until the pipe lands in 0.0.20; see workstream 5.)
  • Docs pictures match their code: capture_guide.py --check, capture_python_docs.py --check and rich record --check all pass.