---
title: "tuika 0.5.0"
description: "Release notes for tuika 0.5.0 (2026-07-25). Highlights: The crate root is now a decision instead of an accumulation.; A theme can be inherited from the terminal.; Markdown fenced blocks are extensible.; Long transcripts scroll by item, not by line.."
image: "https://tuika.dev/social-card.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://tuika.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# tuika 0.5.0

Released 2026-07-25. [crates.io](https://crates.io/crates/tuika/0.5.0) · [docs.rs](https://docs.rs/tuika/0.5.0/tuika/) · [GitHub release](https://github.com/everruns/tuika/releases/tag/v0.5.0)

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](https://raw.githubusercontent.com/everruns/tuika/v0.5.0/docs/demos/primitives.gif)

- **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](https://raw.githubusercontent.com/everruns/tuika/v0.5.0/crates/tuika-mermaid/examples/mermaid_markdown/mermaid.gif)

- **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`](https://crates.io/crates/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](https://github.com/everruns/tuika/pull/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](https://github.com/everruns/tuika/pull/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](https://github.com/everruns/tuika/pull/13))
* docs(example): follow the stream in the markdown example until the reader scrolls back ([#11](https://github.com/everruns/tuika/pull/11))
* refactor(markdown): split the module along its parse/flatten passes ([#9](https://github.com/everruns/tuika/pull/9))
* fix(docs): preserve demo colors during recording ([#10](https://github.com/everruns/tuika/pull/10))
* feat(themes): inherit the terminal's palette ([#8](https://github.com/everruns/tuika/pull/8))
* feat(components): add `Dialog`, `Form`, `Viewport`, `Scene`, and `DrawView` ([#7](https://github.com/everruns/tuika/pull/7))
* refactor: give the crate root, components, and term one job each ([#6](https://github.com/everruns/tuika/pull/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](https://github.com/everruns/tuika/pull/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](https://github.com/everruns/tuika/pull/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

Source: https://tuika.dev/releases/0.5.0/index.mdx
