Skip to content
Updated View as Markdown

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.rs now 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 (also Theme::terminal(), and a terminal entry in themes::PRESETS) — a const Theme whose every slot is Color::Reset or a Color::Indexed ANSI slot, so the terminal resolves the palette. No query, no timeout, no failure mode.
  • New: tuika::term::palette — TerminalPalette with parse/query, plus QUERY_FOREGROUND, QUERY_BACKGROUND, and query_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 -- --probe to 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.

primitives demo

  • New: components::Dialog — a titled, bordered, optionally backdrop-dimmed panel with key hints and an action row, placed by an OverlaySpec.
  • New: components::{Form, FormField, FormState, FormOutcome} — labelled fields with help and error rows, Tab/Shift+Tab focus, and a responsive stack_below(width) breakpoint.
  • New: components::Viewport — a clipping, panning window over an oversized child, with optional scrollbars on both axes.
  • New: Scene, SceneOverlay, Backdrop, and paint_scene — one value carrying the root and its overlay stack, with sync_focus for the registry.
  • New: DrawView (alias CanvasView) — a View from a closure, for one-off custom painting without declaring a type.
  • New: SemanticRole and Theme::{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.

mermaid demo

  • New: components::markdown::FencedBlockRenderer — the boundary, plus Markdown::block_renderer, MarkdownState::with_block_renderer, and markdown::{to_lines_with_renderer, to_linked_lines_with_renderer}.
  • New crate: tuika-mermaid — MermaidRenderer, an mmdflux-backed implementation for ```mermaid fences. 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 with windowed construction, gap, scrollbar, and measure_height.
  • New: components::textinput::{Trigger, TriggerAnchor, Token, TextSpan}, plus TextInputState::{tokens, active_token, replace_token} and TextInput::{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}
  • 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::Image and tuika::term::image::{ImageData, ImageLayer, ImageSupport}
  • Markdown is a component, and lives where the other components live.
    • Before: tuika::markdown
    • After: tuika::components::markdown
  • One runner module: a cfg is an implementation detail, not a second entry in the module list.
    • Before: tuika::async_runner::{AsyncRunner, Signal}
    • After: tuika::runner::{Runner, RunnerConfig, AsyncRunner, Signal}
  • Ratatui interop is named for the boundary, not for its only type.
    • Before: tuika::ratatui_view::RatatuiView
    • After: tuika::interop::RatatuiView

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}
  • tuika::highlight was a module and a function at once. The module is the Highlighter boundary; the function paints a selection.
    • Before: tuika::highlight(buffer, area, range, style)
    • After: tuika::mouse::paint_selection(buffer, area, range, style)
  • 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
  • Overlay sits beside OverlaySpec, 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 as tuika::Overlay)

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.rs gated on feature(doc_auto_cfg), which was merged into doc_cfg and 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+Enter reaches the text input as its own chord. TerminalSession now enables and restores enhanced keyboard reporting, so the chord arrives at TextInputState distinctly instead of being decoded as plain Enter — 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 Paragraph of its own, so a nested list, block quote, or code fence opened while the parent item’s text was still buffered — rendering - outer / - inner as a single • outerinner line, and placing a fence ahead of the item it follows.

  • Markdown: streaming no longer splits a block on a half-arrived indent. MarkdownState settles 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 a task_marker stylesheet 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.json absorbs 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.md and a markdown_table demo scene
  • chore(ci): cover the workspace on the macOS and Windows legs (#15)
  • feat(markdown): render extensible fenced blocks through FencedBlockRenderer, and add the tuika-mermaid companion crate
  • fix(term): enable modified-key reporting so Shift+Enter arrives 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, and DrawView (#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_runner into runner and rename ratatui_view to interop
  • 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.md and a crate-layout section to the README
  • chore(knowledge): split out process concepts and enforce upkeep (#5)
  • feat(components): add ItemScroll and the composer token boundaries, plus the codex example
  • 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
Navigation

Type to search…

↑↓ navigate↵ selectEsc close