---
title: "tuika 0.12.0"
description: "Release notes for tuika 0.12.0 (2026-09-15). Highlights: tuika owns the cell grid."
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.12.0

Released 2026-09-15. [crates.io](https://crates.io/crates/tuika/0.12.0) · [docs.rs](https://docs.rs/tuika/0.12.0/tuika/) · [GitHub release](https://github.com/everruns/tuika/releases/tag/v0.12.0)

Released alongside `tuika-charts` 0.2.0, `tuika-codeformatters` 0.6.0,
`tuika-html` 0.2.0, and `tuika-mermaid` 0.4.0. Every companion's tuika
requirement moves to 0.12, and all four drop the `ratatui-core` (or `ratatui`)
dependency they no longer use.

## Highlights

**tuika owns the cell grid** — `Rect`, `Color`/`Style`, `Line`/`Span`,
`Buffer`/`Cell`, `Backend`, and `Terminal` are tuika's own types now, so a host
takes one crate instead of two and is no longer exposed to a `ratatui-core`
major bump. Existing ratatui widgets still compose, behind the optional
`ratatui` feature.

![markdown demo](https://raw.githubusercontent.com/everruns/tuika/v0.12.0/docs/demos/markdown.gif)

- **Dependencies**: 55 crates to 32; the cold dependency build drops from
  roughly 32s to 6s.
- **Performance**: `render`, `frame_windowed`, and the scroll paths fall
  7.3–7.4% in instruction count.
- **Correctness**: `Span::width` and `Line::width` now count grapheme-aware
  display columns, so they agree with what `Surface` actually paints.

Rendering is unchanged on screen, so this release carries no new recording.

## Breaking Changes

- **tuika no longer depends on ratatui.** It now owns its own `Rect`/`Position`,
  `Color`/`Modifier`/`Style`, `Line`/`Span`, `Buffer`/`Cell`, `Backend`, and
  `Terminal`. Every one of those types moved from `ratatui-core` to tuika, so
  any signature naming one changes.

  For most hosts the migration is an import change — the canonical path is
  unchanged:

```rust
  // before
  use ratatui_core::layout::Rect;
  use ratatui_core::style::{Color, Modifier, Style};
  use ratatui_core::text::{Line, Span};

  // after (or just `use tuika::prelude::*;`)
  use tuika::ui::{Color, Line, Modifier, Rect, Span, Style};
```

  A host driving its own terminal takes `tuika::term::terminal::{Terminal,
  TerminalOptions, Viewport}` and `tuika::term::backend::CrosstermBackend`
  instead of ratatui's, and `tuika::term::testbackend::TestBackend` in tests.

  The types are deliberately shaped like the ones they replace, with two
  behavioral notes:
  - `Span::width` and `Line::width` count grapheme-aware display columns
    (`width::str_cols`) rather than per-`char` widths, so they now agree with
    what `Surface` actually paints. A line containing a ZWJ emoji measures
    narrower than before — and correctly.
  - `Cell` stores short grapheme clusters inline instead of in a
    `CompactString`. `Cell::new` is no longer `const`; use `Cell::default()`
    for a blank cell.

- **Ratatui interoperability is now behind the `ratatui` feature**, off by
  default. `Surface::render_ratatui`, `tuika::interop`, and `RatatuiView` need
  it; a host wrapping ratatui widgets adds:

```toml
  tuika = { version = "0.12", features = ["ratatui"] }
```

  Every ratatui widget still works. The boundary is no longer a shared `Buffer`
  — the two crates no longer share a cell type — but a cell-by-cell conversion
  over the rendered area, which `render_ratatui` was already doing a copy for.

- **`Surface::render_ratatui` was split.** The general escape hatch — a private
  scratch buffer, composited back through the clip — is now
  `Surface::render_scratch` and takes tuika's `Buffer`. `render_ratatui` is the
  ratatui-specific wrapper around it. A caller using it for scratch access
  rather than for ratatui widgets should switch to `render_scratch`, which needs
  no feature.

- **`Line::raw` and `Line::styled` split their content on line breaks**, one
  span per line, matching what they replaced. `Line::raw("")` therefore yields a
  line with no spans.

## Added

- `Surface::render_scratch` — clipped scratch-buffer access without ratatui.
- `Buffer::set_style`, `Buffer::content()`, `Cell::raw_symbol()` (tells a wide
  grapheme's placeholder apart from a blank cell).
- `framebuffer::QUADRANTS` — the sixteen quadrant block glyphs, previously
  reached through `ratatui_core::symbols::pixel`.
- `Style::{bold, dim, italic, underlined, reversed, crossed_out}` shorthands.
- `tuika::term::backend::CrosstermBackend` and
  `tuika::term::testbackend::TestBackend` are public, for a host driving
  `Terminal` itself.

## Changed

- **Dependencies: 55 crates to 32.** Dropping `ratatui-core` removed 23,
  including `kasuari` (a Cassowary solver), `lru`, and a second `hashbrown` —
  all of which existed for `ratatui_core::layout::Layout`, which tuika replaced
  with its own flex solver and never called — plus `strum`, `compact_str`,
  `itertools`, `unicode-truncate`, and a second `syn` major version. The cold
  dependency build drops from roughly 32s to 6s.
- **Rendering is ~7% cheaper.** `render`, `frame_windowed`, and the scroll paths
  fall 7.3–7.4% in instruction count, because a `Cell` now stores short
  grapheme clusters inline. The markdown paths rise ~2%, the cost of measuring
  widths grapheme-aware; the benchmark baseline is updated accordingly.
- `scrolling-regions` is now a feature of tuika's own `Backend` trait, and no
  longer pulls in `ratatui-crossterm` to forward a flag.
- **The companion crates no longer depend on ratatui.** `tuika-charts`,
  `tuika-html`, and `tuika-mermaid` dropped `ratatui-core`, and
  `tuika-codeformatters` dropped the full `ratatui` umbrella; none of them had
  named a ratatui type since tuika took over the cell grid. `ratatui` is no
  longer part of `tuika-codeformatters`' public interface, so a dependant pins
  only `tuika`.
- Public documentation follows the new model: the guides, the component gallery,
  and the getting-started page import from `tuika::ui` instead of `ratatui::*`,
  and no longer describe ratatui as running underneath.

## Fixed

- **The workspace builds on its declared MSRV again.** A dependency in the root
  manifest was written as a multi-line inline table, which TOML 1.0 does not
  allow and the Cargo on Rust 1.88 rejects outright — so `cargo` failed to parse
  `Cargo.toml` before compiling anything. Only the manifest changed; no
  dependency version or feature moved.

  The MSRV CI job did not catch it because it never ran the MSRV: the job set
  1.88 as the `rustup` default, which `rust-toolchain.toml`'s development pin
  outranks, so every check ran on the newer toolchain. The job now pins
  `RUSTUP_TOOLCHAIN` and fails if the active toolchain is not the
  `rust-version` in `Cargo.toml`.

## What's Changed

* refactor(deps)!: own the cell grid and drop ratatui ([#114](https://github.com/everruns/tuika/pull/114))
* fix(ci): make the MSRV job run the MSRV ([#131](https://github.com/everruns/tuika/pull/131))
* fix(demos): re-record scroll and close the broken-capture class ([#132](https://github.com/everruns/tuika/pull/132))
* chore(deps): drop the companion crates' unused ratatui dependencies
* docs: retire ratatui from the guides, gallery, and durable knowledge
* fix(release): keep companion dev-dependencies out of the published manifest

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