tuika 0.5.0
Released 2026-07-25. crates.io · docs.rs · GitHub release
The first release cut from this repository. Companion crates released alongside
it: tuika-codeformatters 0.3.0 and tuika-mermaid 0.1.0 (its first release).
Highlights
The crate root is now a decision instead of an accumulation. tuika:: had
grown to 30 flat public modules plus 167 names re-exported to the root, so
almost every type had two equally valid paths and neither was canonical. Four
levels now each have one job, and where a new item goes is a rule rather than a
preference:
| Path | Holds |
|---|---|
tuika:: |
the framework spine — View, Element, RenderCtx, layout, events, Theme, Surface, the host boundary |
tuika::components |
every widget |
tuika::term |
everything out-of-band: clipboard, hyperlink, progress, pointer, image, capabilities, palette |
tuika::prelude |
the spine and the components in one glob import |
- New:
tuika::prelude—use tuika::prelude::*;replaces most import blocks outright, which is the intended migration for application code. - No behavior change: this release moves and renames public items only. Every
test, snapshot, and benchmark passes unchanged apart from its imports, and
tests/public_api.rsnow pins the layout from outside the crate, the way a host sees it.
A theme can be inherited from the terminal. An application can adopt the palette the user already configured instead of imposing its own — opt-in and host-initiated, so nothing changes for an app that does not ask.
- New:
themes::TERMINAL(alsoTheme::terminal(), and aterminalentry inthemes::PRESETS) — aconst Themewhose every slot isColor::Resetor aColor::IndexedANSI slot, so the terminal resolves the palette. No query, no timeout, no failure mode. - New:
tuika::term::palette—TerminalPalettewithparse/query, plusQUERY_FOREGROUND,QUERY_BACKGROUND, andquery_sequence(). Asks the terminal for its colors with the xterm queries (OSC 10 / 11 / 4), fenced by the Device Attributes request so an unsupported query costs a round-trip rather than a timeout. - New:
Theme::from_terminal(&TerminalPalette)derives a full theme from the reply — reported foreground and background verbatim, in-between tones blended and contrast-guarded, hues from the ANSI palette. - New:
Capabilities::query_with_palette(timeout)answers “what can this terminal do” and “what colors is it using” in one round-trip. - New example:
cargo run --example inherit(and-- --probeto print what your terminal answers without taking over the screen).
The palette work is additive only — nothing moved or was renamed by it.
A dialog, a form, and a scrollable viewport are no longer every host’s
homework. Three patterns every application rebuilt by hand are components now,
and a Scene owns the base tree plus its overlays so focus and compositing stop
being hand-wired at the call site.

- New:
components::Dialog— a titled, bordered, optionally backdrop-dimmed panel with key hints and an action row, placed by anOverlaySpec. - New:
components::{Form, FormField, FormState, FormOutcome}— labelled fields with help and error rows,Tab/Shift+Tabfocus, and a responsivestack_below(width)breakpoint. - New:
components::Viewport— a clipping, panning window over an oversized child, with optional scrollbars on both axes. - New:
Scene,SceneOverlay,Backdrop, andpaint_scene— one value carrying the root and its overlay stack, withsync_focusfor the registry. - New:
DrawView(aliasCanvasView) — aViewfrom a closure, for one-off custom painting without declaring a type. - New:
SemanticRoleandTheme::{semantic_color, semantic_style, success_style, warning_style, danger_style, info_style}— success / warning / danger / info resolved from the theme instead of hardcoded per host.
Markdown fenced blocks are extensible. A fence with an unknown language used to render as code and nothing else. A host can now claim any info string and paint the block itself — which is how Mermaid diagrams became terminal-native, without tuika taking on a diagram engine.

- New:
components::markdown::FencedBlockRenderer— the boundary, plusMarkdown::block_renderer,MarkdownState::with_block_renderer, andmarkdown::{to_lines_with_renderer, to_linked_lines_with_renderer}. - New crate:
tuika-mermaid—MermaidRenderer, an mmdflux-backed implementation for```mermaidfences. Diagram layout stays out of tuika core.
Long transcripts scroll by item, not by line. ItemScroll scrolls a list of
laid-out elements — the shape a chat log or an agent transcript actually has —
and the text input grew the boundaries a composer needs.
- New:
components::ItemScroll— item-granular scrolling withwindowedconstruction,gap,scrollbar, andmeasure_height. - New:
components::textinput::{Trigger, TriggerAnchor, Token, TextSpan}, plusTextInputState::{tokens, active_token, replace_token}andTextInput::{highlights, placeholder}—@//mention and slash-command tokens, and styled spans over the edited text. - New example:
cargo run --example codex— a replica of the Codex CLI’s UI built entirely from tuika components.
Breaking Changes
Most application code migrates by replacing its use tuika::{…}; block with
use tuika::prelude::*;. The tables below cover what the prelude does not
carry.
Modules moved
- Out-of-band escapes are one family: they shared a shape but had three
unrelated names, so a reader had to learn each separately.
- Before:
tuika::clipboard,tuika::hyperlink,tuika::native,tuika::capabilities - After:
tuika::term::{clipboard, hyperlink, progress, pointer, capabilities}
- Before:
- Images split along the cell boundary: the protocol half talks to the
terminal, the view half is a component like any other.
- Before:
tuika::image::{Image, ImageData, ImageLayer, ImageSupport} - After:
tuika::components::Imageandtuika::term::image::{ImageData, ImageLayer, ImageSupport}
- Before:
- Markdown is a component, and lives where the other components live.
- Before:
tuika::markdown - After:
tuika::components::markdown
- Before:
- One runner module: a
cfgis an implementation detail, not a second entry in the module list.- Before:
tuika::async_runner::{AsyncRunner, Signal} - After:
tuika::runner::{Runner, RunnerConfig, AsyncRunner, Signal}
- Before:
- Ratatui interop is named for the boundary, not for its only type.
- Before:
tuika::ratatui_view::RatatuiView - After:
tuika::interop::RatatuiView
- Before:
Items renamed
- The OSC encoders share one shape — a pure
encode, a thin writer.- Before:
osc52,write_clipboard,osc8,osc8_with,encode_pointer_shape,write_pointer_shape - After:
term::clipboard::{encode, write},term::hyperlink::{encode, encode_with},term::pointer::{encode, write}
- Before:
tuika::highlightwas a module and a function at once. The module is theHighlighterboundary; the function paints a selection.- Before:
tuika::highlight(buffer, area, range, style) - After:
tuika::mouse::paint_selection(buffer, area, range, style)
- Before:
- Hand-prefixed names get their module back. The prefixes existed only to
disambiguate a flat root.
- Before:
markdown_to_lines,markdown_to_linked_lines,diff_rows,qr_encode,wrap_lines - After:
components::markdown::{to_lines, to_linked_lines},components::diff::rows,components::qr::encode,components::text::wrap_lines - Before:
ASCII_FONT_HEIGHT,CONSOLE_DEFAULT_CAPACITY,TOAST_DEFAULT_TTL - After:
components::ascii_font::FONT_HEIGHT,components::console::DEFAULT_CAPACITY,components::toast::DEFAULT_TTL
- Before:
Overlaysits besideOverlaySpec, since resolving a spec to a rect and pairing that rect with a view are two halves of one pipeline.- Before:
tuika::host::Overlay - After:
tuika::overlay::Overlay(still re-exported astuika::Overlay)
- Before:
Root re-exports removed
Components and the per-module surface (anim, focus, framebuffer,
highlight, keymap, live, mouse, probe, themes, width, and the
styling extras) are no longer flattened to tuika::. Reach them through
tuika::prelude::* or their module path.
Fixed
-
docs.rs builds the crate again. 0.4.0 documented nothing on docs.rs:
src/lib.rsgated onfeature(doc_auto_cfg), which was merged intodoc_cfgand removed as a name in Rust 1.92, so rustdoc failed outright on docs.rs’s nightly. Nothing else saw it — the attribute compiles only under--cfg docsrs, which no local or CI build set. CI now rehearses docs.rs’s own invocation (nightly,--cfg docsrs) alongside the consumer-facing one. -
Shift+Enterreaches the text input as its own chord.TerminalSessionnow enables and restores enhanced keyboard reporting, so the chord arrives atTextInputStatedistinctly instead of being decoded as plainEnter— the difference between “insert a newline” and “submit”. The negotiation handles iTerm2 and tmux’s xterm and CSI-u formats; Windows keeps using modifier-aware native console events. -
Markdown: a block inside a tight list item no longer swallows the item’s text. A tight list item carries no
Paragraphof its own, so a nested list, block quote, or code fence opened while the parent item’s text was still buffered — rendering- outer/- inneras a single• outerinnerline, and placing a fence ahead of the item it follows. -
Markdown: streaming no longer splits a block on a half-arrived indent.
MarkdownStatesettles its prefix at the last blank line, and mid-stream a nested item’s indent arrives as a whitespace-only unterminated line — which looked blank. The prefix was committed there, permanently cutting a list in two so the halves re-parsed as unrelated top-level lists. Only a newline-terminated blank line settles the prefix now, so a streamed render matches the one-shot render character-for-character. -
Markdown:
- [ ]/- [x]task lists render as checkboxes. The renderer had the checkbox handler and atask_markerstylesheet slot, but the parser option that emits the event was never enabled, so both were unreachable and the markers rendered as literal text.Together these cost ~3% instructions on the markdown render benches — the old counts were cheap because a nested item’s line was being dropped rather than laid out — which the committed
benches/iai-baseline.jsonabsorbs unchanged.
What’s Changed
- fix(packaging): trim the companion crates and correct the release history (#12)
- feat(markdown): render GFM task-list checkboxes as themed markers
- fix(markdown): flush the pending item line before a nested block opens
- fix(markdown): never settle the streaming prefix on an unterminated blank line
- docs(markdown): add
docs/markdown.mdand amarkdown_tabledemo scene - chore(ci): cover the workspace on the macOS and Windows legs (#15)
- feat(markdown): render extensible fenced blocks through
FencedBlockRenderer, and add thetuika-mermaidcompanion crate - fix(term): enable modified-key reporting so
Shift+Enterarrives as its own chord (#13) - docs(example): follow the stream in the markdown example until the reader scrolls back (#11)
- refactor(markdown): split the module along its parse/flatten passes (#9)
- fix(docs): preserve demo colors during recording (#10)
- feat(themes): inherit the terminal’s palette (#8)
- feat(components): add
Dialog,Form,Viewport,Scene, andDrawView(#7) - refactor: give the crate root, components, and term one job each (#6)
- refactor(term): group the out-of-band escapes under one module
- refactor(components): move markdown and the image view in with the components
- refactor: fold
async_runnerintorunnerand renameratatui_viewtointerop - refactor(tests): move the crate’s test scaffolding under
src/tests - test: pin the public module layout from outside the crate (
tests/public_api.rs) - docs: add
knowledge/specs/api-surface.mdand a crate-layout section to the README - chore(knowledge): split out process concepts and enforce upkeep (#5)
- feat(components): add
ItemScrolland the composer token boundaries, plus thecodexexample - docs: record the showcases at gallery pixel density and point yolop at its product page
- fix(docs): stop the demo recordings clipping their own scenes
- docs: add a showcases page with yolop and LLMSim demos (#3)
- chore(release): show demos in the changelog highlights and drop commit links
- chore: require signed commits, Doppler-managed secrets, and PRs for external contributions
- fix(ci): green the pipeline after the yolop extraction
- docs: add the knowledge bundle and agent workflows
- ci: add the build, documentation, release, and cross-terminal pipelines
- test: add a PTY smoke test and guard the published crate contents