github ratatui/ratatui ratatui-widgets-v0.3.1

"Rats, we're rats; we're the rats." – Rat Movie

We are excited to announce the new version of ratatui - a Rust library that's all about cooking up TUIs 👨‍🍳🐀

Release highlights: https://ratatui.rs/highlights/v0301/

⚠️ List of breaking changes can be found here.

Features

  • 74d6a84 (block) Support shadows by orhun in #2481

    Introduce Block::shadow(...) with a new Shadow type that supports:

    • presets: overlay, block, light_shade, medium_shade,
      dark_shade
    • custom symbols via Shadow::symbol(...)
    • custom effects via Shadow::custom(...)
    use ratatui::layout::Offset;
    use ratatui::style::Stylize;
    use ratatui::widgets::{Block, Shadow};
    
    let popup = Block::bordered().title("Popup").shadow(
        Shadow::dark_shade()
            .black()
            .on_white()
            .offset(Offset::new(2, 1)),
    );

    Results in:

    ┌Popup─────┐
    │content   │▒
    └──────────┘▒
      ▒▒▒▒▒▒▒▒▒▒▒
    

    shadow

    fixes #1892


  • 4d30420 (buffer) Add CellDiffOption::AlwaysUpdate to force cell updates by sxyazi in #2480

    When this option is used, the cells are updated even when content is identical.

    Follow-up for #1605

    Trying to resolve #1116

  • 39c32c6 (buffer) Add cell diff options by benjajaja in #1605

    Problem:Escape sequences always cause a cell to count as "multiwidth",
    even when it doesn't render wider than one cell, or not as wide as the
    escape sequence would be computed as.

    Solution:Convert skip:bool to enum. Add enum option ForceWidth to
    force a cell width for diffing.

    When using the option, this also fixes some bug where diffing is not
    idempotent and causes a diff operation for (symbol.len() - 1) times.

    There are three new specific test cases:

    1. Rendering hyperlinks by squeezing the escape sequence into the first
      cell and forcing the width to the unicode width of the text part.
      This is much easier to implement for a Link widget, as it would only
      need to get the unicode-width once and not iterate over graphemes
      like Spans must do.
    2. Rendering hyperlinks by squeezing the opening sequence into the first
      cell with the first grapheme and forcing the width to that of the
      first grapheme. Then rendering each grapheme as usual. Then squeezing
      the closing sequence into the last cell with the last grapheme and
      forcing the width to that of the last grapheme.
      This is harder to implement for a Link widget, as it would have to
      iterate over graphemes with their width like Spans do.
    3. Kitty image sequence with utf-8 placeholders, similar to 2 but with
      known constant grapheme widths.

    Link widget that leverages this

    https://github.com/benjajaja/tui-link

    It would be cooler if we could just add something like .link(url) to
    Spans, because it would much simpler to insert some link and leverage
    all the Line/Text/Paragraph wrapping and whatnot. With a custom widget
    you need to take care of the Area where you'd want to render it, so
    it's not that clean. But we could iterate on this later, if even
    possible.

  • 6faaddb (core) Implement from slice for line and text by NoOPeEKS in #2371

    This PR adds the following implementations of the From trait for Line
    and Text structs:

    • Implements From<&[T]> where T is Into<Span> for Line, allowing using
      of slices to construct Lines.
    • Implements From<&[T]> where T is Into<Line> for Text, allowing using
      of slices of various types to construct Texts.

    closes #2279

  • 5fc6ab8 (core) Support layout-cache in no_std environments by junkdog in #2399

    this enables "layout-cache" for no_std builds; it's meant for embedded
    environments, where the layout engine otherwise consumes all CPU,
    capping the framerate at around ~10fps. the same app can refresh 300-500
    times per second with layout cache enabled.

    i had to add layout-cache = ["dep:critical-section"] to all builds -
    it's pretty tiny and shouldn't leave a trace in std-builds. the
    alternative is to add an extra layer of features for layout-cache with
    std and no_std, but it pollutes the feature space.


  • ee4b7a9 (crossterm) Add the missing hidden modifier by sxyazi in #2413

    Fixes sxyazi/yazi#3724, see
    sxyazi/yazi#3724 (comment) for a
    reproducer.

    This PR adds the missing Modifier::HIDDEN style and introduces a
    queue_modifier_diff to test ModifierDiff::queue().

    It also fixes a bug where CrosstermAttribute::Bold and
    CrosstermAttribute::Dim would be emitted twice when resetting
    intensity. For example:

    #[case(Modifier::DIM, Modifier::BOLD, &[CrosstermAttribute::NormalIntensity, CrosstermAttribute::Bold])]

    would become:

    #[case(Modifier::DIM, Modifier::BOLD, &[CrosstermAttribute::NormalIntensity, CrosstermAttribute::Bold, CrosstermAttribute::Bold])]

  • 9d9239a (examples) Add volatility-surface 3D visualization by floor-licker in #2322

    A design demonstration of a 3D volatility surface rendering using
    Braille canvas with interactive rotation and zoom controls. I built this
    for myself for an app I'm currently building but just wanted to share it
    with the community as well to inspire more 3D perspective terminal
    widgets in the future.

    Final Demo

    volatility-surface

    Description

    Adds a new example demonstrating 3D visualization techniques in the
    terminal. There aren't many examples showing how to represent 3D objects
    in 2D terminal space so my goal is just to demonstrate more advanced
    Canvas and Braille rendering techniques for 3D graphics. The example
    visualizes an implied volatility surface which is a common financial
    visualization using interactive rotation and zoom controls. You can run
    the interactive demo for yourself with cargo run -p volatility-surface

    demo.mov

    <img width="659" height="432" alt="image"

    src="https://github.com/user-attachments/assets/68698cb0-c5d5-4b41-a3c3-65ec8fff12f5"
    />

    Technical Highlights

    • Demonstrates how to implement perspective projection in a terminal
    • Shows advanced use of Canvas widget with Marker::Braille
    • Example of smooth animation patterns and state management
    • Self-contained with synthetic data generation (no external APIs)

  • ae975c7 (examples) Allow overlap spacing in explorer by joshka in #2316

    Store spacing as i16 so negative values map to Spacing::Overlap, and
    show overlap in the axis label.

  • 1e0ab0c (ratatui-crossterm) Add IntoCrossterm for Style by 0xferrous in #2323

  • 101a63e (render) Add function for applying buffer by musjj in #2566

    Add a public API for applying and flushing the terminal buffer.

    A minimal usage will look something like this:

    use ratatui::Terminal;
    use ratatui::backend::CrosstermBackend;
    use ratatui::buffer::Buffer;
    use ratatui::widgets::Widget;
    
    let backend = CrosstermBackend::new(io::stdout());
    let mut terminal = Terminal::new(backend)?;
    
    terminal.autoresize()?;
    
    let mut custom_buffer = Buffer::default();
    custom_buffer.resize(terminal.get_frame().area());
    custom_buffer.reset();
    
    "Hello World!".render(custom_buffer.area, &mut custom_buffer);
    
    terminal.current_buffer_mut().merge(&custom_buffer);
    terminal.apply_buffer()?;

    My primary motivation for this PR is to improve the ECS ergonomics in
    bevy_ratatui. But this
    should be useful for anyone who wants to commit incremental writes to
    the buffer without having to do everything in one monolithic
    Terminal::draw
    closure.


  • 09a3027 (symbol) Add custom marker by BenFradet in #2356

  • f9d066f (table) Let Cells span multiple columns by karkhaz in #2150

    Add a 'column_span' field to table cells. The default value
    is 1; larger values will cause cells to span over multiple columns,
    being rendered over all columns plus the spaces between them.

    Fixes #1568.

  • a5b08d6 (widgets) Add Fill widget by Metbcy in #2520

    Adds a new Fill widget that paints every cell within its area with a
    single repeated symbol and style. Integrates with Stylize so the whole
    chain works as expected:

    use ratatui::widgets::{Fill, Widget};
    use ratatui::style::Stylize;
    
    Fill::new("X").blue().bold().render(area, buf);

    Implements Widget (for both Fill and &Fill) and Styled, accepts
    anything that converts into a Cow<'a, str>, and degrades gracefully on
    empty / multi-grapheme symbols.


  • 9094fd2 (widgets) Add line shape with filled area for Canvas and Chart by bananaofhappiness in #2426

    This commit adds filled area-chart rendering for both Canvas and Chart.

    You can now render a line and fill the area between that line and a baseline Y value, which helps highlight magnitude/volume trends.

    • In Canvas, use FilledLine.
    • In Chart, use GraphType::Area and set the baseline via Dataset::fill_to_y(f64).

    The f64 baseline is now configured on Dataset (not in GraphType), so GraphType stays a simple enum variant.

    Under the hood, line rasterization in canvas was refactored to share a reusable Bresenham point iterator, and FilledLine builds on that to paint vertical spans from each line point to fill_to_y.

    Some screenshots with and without this new type:
    изображение
    изображение

    // In Canvas
    use ratatui::widgets::canvas::FilledLine;
    
    Canvas::default()
        .paint(|ctx| {
            ctx.draw(&FilledLine::new(0.0, 0.0, 10.0, 5.0, 0.0, Color::Red));
        });
    
    // In Chart
    let dataset = Dataset::default()
        .data(&data)
        .graph_type(GraphType::Area)
        .fill_to_y(0.0); // fill to y = 0
    
    Chart::new(vec![dataset]);

  • 0a87882 (uncategorized) Add impl From<u16> for Padding and Margin by JayanAXHF in #2438

  • 556cc7b (uncategorized) Add comment for inner area to popup example by Its-Just-Nans in #2309

  • 01a15f9 (uncategorized) Add AsRef impls for widget types by joshka in #2297

Bug Fixes

  • d12bb83 (barchart) Handle empty horizontal charts by fallintoplace in #2553

    Fixes #2552

    This makes the BarChart constructors ignore empty groups, matching the
    existing .data(...) builder behavior. Without this,

    BarChart::horizontal(Vec::<Bar>::new()) stores one empty group,
    proceeds into horizontal rendering, skips the bar loop, and then
    underflows when computing the group label row from bar_y - self.bar_gap.

    The fix normalizes constructor input through a shared non_empty_groups
    helper for new, horizontal, and grouped. Empty horizontal charts
    now render nothing instead of panicking, and constructor behavior is
    consistent with .data(...).


  • 6396b1c (block) Saturate block edge arithmetic by joshka in #2488

    Motivation

    • Block border and title layout used unchecked u16 arithmetic in
      several places.
    • In debug builds that can panic on tiny or edge-case geometry; in
      release builds the same arithmetic wraps.
    • The original report came from merge-border rendering on tiny areas,
      but the same pattern appeared in title layout and spacing helpers as
      well.

    Description

    • Use saturating arithmetic in Block::inner, render_sides,
      render_corners, titles_area, and vertical_space.
    • Clamp rendered title widths to u16 for layout arithmetic.
    • Replace unchecked title-width accumulation and cursor-advance math
      with bounded arithmetic.
    • Add debug-only regression tests covering empty areas, maximal padding,
      title-area edge cases, and very large title widths.

    Testing

    • Ran cargo test -p ratatui-widgets block::tests.

    Codex
    Task

  • 9143b83 (buffer) Diff for trailing cells when only style changes by gcavelier in #2308

    this PR closes #2307 by preventing unnecessary diff updates for trailing
    cells when only style changes.

    This PR was generated by Claude, and validated by me.

    Summary

    This PR fixes a visual artifact bug where block borders would appear
    offset when rendered over a widget that had a foreground color style
    applied to the entire area.

    The Fix

    - if !next_trailing.skip && prev_trailing != next_trailing {
    + // Only emit update if the SYMBOL changed, not just the style.
    + // The style of hidden trailing cells is not visible, so style
    + // differences alone should not trigger updates that can cause
    + // cursor positioning issues on some terminals.
    + if !next_trailing.skip && prev_trailing.symbol() != next_trailing.symbol() {

    This aligns the code with the documented intent: only emit updates when
    the symbol (visible content) changes, not when only the style
    changes.

    Changes

    File Change
    ratatui-core/src/buffer/buffer.rs:526-530 Compare only symbol, not
    full cell
    ratatui-core/src/buffer/buffer.rs:1376-1425 Add regression test

    Test Added

    #[test]
    fn diff_ignores_style_only_changes_in_trailing_cells() {
        // Verifies that trailing cells with same symbol but different style
        // do NOT generate diff updates
    }

    Why This Is Safe

    1. Trailing cells are hidden - they are visually covered by the wide
      character
    2. Style is invisible - the fg/bg color of a hidden cell has no
      visual effect
    3. Symbol changes still trigger updates - if the symbol changes
      (e.g., from " " to "x"), the update is still emitted
    4. Aligns with documented intent - the original comment says
      "non-blank content", not "different style"

    Related

    • The existing test diff_clears_trailing_cell_for_wide_grapheme
      verifies that symbol changes DO trigger updates
    • This fix complements that behavior by ensuring style-only changes do
      NOT trigger updates
  • e6b71f2 (build) Correct rust-toolchain->rust-version on cargo-deny-action by sermuns in #2471

    closes #2470

  • 65c5202 (changelog) Fix typo by joshka in #2300

  • 43bbaae (clippy) Fix beta clippy errors by Logan-Ruf in #2433

    Noticed these errors on my other PR and figured I could just fix them
    real quick.

    closes #2432

  • 957fbb0 (core) Use correct width for halfwidth dakuten/handakuten by orhun in #2499

    unicode-width reports U+FF9E/U+FF9F as zero-width, but terminals render
    them as 1 cell.
    Adjusts CellWidth trait accordingly for fixing this behavior.

    fixes #2188

  • d7646c7 (core) Avoid overflow in BufferDiff forced-width advance by joshka in #2487

    Motivation

    • Prevent arithmetic overflow when advancing self.pos for
      CellDiffOption::ForcedWidth(NonZeroU16) in
      ratatui-core/src/buffer/diff.rs, which could panic in debug or wrap in
      release and cause an iterator hang/DoS.

    Description

    • Replace the unchecked self.pos += width.get().saturating_sub(1) with
      a saturating addition via self.pos = self.pos.saturating_add(width.get().saturating_sub(1) as usize) to
      avoid overflow while preserving existing iterator semantics.

    Testing

    • Ran cargo test -p ratatui-core buffer::diff --lib and the buffer
      diff tests completed successfully (10 passed, 0 failed).

    Codex
    Task

  • 77f8006 (core) Avoid cursor position queries during resize by orhun in #2485

    Terminal::resize() now clears without calling get_cursor_position(),
    so that CPR (Cursor position report) calls does not interfere with
    stdin.

    Fixes #2483


  • 18aa467 (examples) Make line-gauge example compatible with macos sequoia's terminal.app by lazo4 in #2474

    Part of the fix for #1972

    Summary

    This fix makes the line-gauge example compatible with the macos
    sequoia Terminal.app which doesn't support truecolor. It reuses the
    is_true_color_supported introduced in #2211 by ffex and uses a color
    theme instead of hardcoding the colors.

    Result on macos sequoia

    Before:

    After:

    Notes

    This is my first open source contribution, thanks to @ffex for letting
    me help on this issue

  • ce2c228 (examples) Change flex example colors for MacOS default terminal by ffex in #2211

    Part of the fix for #1972

    Summary

    This fix introduces a function to check if we are in a terminal without
    truecolor(24-bit) and changes the default colors to appear fine of the
    flex example.

    Notes

    The function "is_true_color_supported” is an old problem and there is no
    common way to determine if a terminal supports or not the truecolor.

    This is the main reason why the function detects specifically the
    Terminal.app version before the Tahoe. If there are other known
    terminals with this problem, we can add it to this function.


  • ef72dba (examples) Fix import for widget examples by orhun in #2422

    closes #2299

  • 88441cf (terminal) Fix inline viewport resizing issues by clearing the screen by wyvernbw in #2355

    adds a check to the autoresize function to clear the entire screen and
    move the inline viewport to the top when the window shrinks horizontally
    in order to avoid line wrapping issues.

    Other libraries like ink purge the history as well, but the
    backend::ClearType type does not support that. Without this if the
    user scrolls up they will see previous broken renders. This should work
    well with all terminal emulators and multiplexers.

    fixes #2086

  • 91b6fb7 (tests) Use the correct type for the cell diff test by orhun in #2472

    fixes the CI!

  • 4493742 (widgets) Handle single y-axis label by fallintoplace in #2550

    Fixes #2549.

    This prevents Chart from panicking when the Y axis is configured with
    exactly one label. The X-axis rendering path already skips label
    placement when fewer than two labels are provided; this applies the same
    guard to Y-axis labels before the spacing calculation divides by
    labels_len - 1.

    This also updates the Axis::labels docs so they describe the new

    behavior:fewer than two labels are not rendered instead of causing a
    panic.

  • 0bdebd6 (widgets) Prevent chart scaling overflow by fallintoplace in #2546

    Summary

    Fixes #2545.

    This changes BarChart and Sparkline scaling to use a u128 intermediate
    before division, then caps the scaled ticks at the drawable area. That
    prevents debug-build panics and release-build wrapping when public u64
    chart values are large.

    Validation

    • cargo test -p ratatui-widgets barchart::tests
    • cargo test -p ratatui-widgets sparkline::tests
    • cargo check -p ratatui-widgets --all-features
    • cargo clippy -p ratatui-widgets --all-targets --all-features -- -D warnings
    • cargo test -p ratatui-widgets
  • e27a22a (widgets) Inherit the text alignment for Paragraph by 7Bpencil in #2369

    Paragraph didn't take into account alignment of the text it was created
    from:

    let lines = vec![
        Line::from("one"),
        Line::from("double"),
        Line::from("quadruple"),
    ];
    let text = Text::from(lines).centered();
    
    // used to be rendered left-aligned, now centered
    let paragraph = Paragraph::new(text).block(block);

    Now the Paragraph inherits the text alignment.

  • b5c0831 (widgets) Avoid panic if Clear area is outside of buffer by 7Bpencil in #2368

    If Clear area is at least partially outside of buffer, panic "index
    outside of buffer" happens on Widget::render

    Demo source code
    use crossterm::event::{self, Event, KeyModifiers};
    use ratatui::{
        layout::Rect,
        text::Line,
        widgets::{Block, Borders, Clear, Paragraph},
        DefaultTerminal, Frame,
    };
    use std::iter;
    
    fn main() {
        ratatui::run(app);
    }
    
    fn app(terminal: &mut DefaultTerminal) {
        loop {
            if let Event::Key(key_event) = event::read().expect("failed to read event") {
                if key_event.kind.is_press()
                    && key_event.modifiers.contains(KeyModifiers::CONTROL)
                    && key_event.code.is_char('c')
                {
                    break;
                }
            }
            terminal.draw(render).expect("failed to draw frame");
        }
    }
    
    fn render(frame: &mut Frame) {
        {
            let width = frame.area().width;
            let area = Rect::new(0, 0, width, 10);
            let line = Line::from("W".repeat(area.width as usize));
            let lines: Vec<Line> = iter::repeat_n(line, area.height as usize).collect();
            frame.render_widget(Paragraph::new(lines), area);
        }
        {
            let area = Rect::new(50, 2, 20, 5);
            let block = Block::default()
                .title_top(Line::from("Popup-with-Clear").centered())
                .borders(Borders::ALL);
            let lines = vec![
                Line::from("one"),
                Line::from("double"),
                Line::from("quadruple"),
            ];
            frame.render_widget(Clear, area);
            frame.render_widget(Paragraph::new(lines).block(block).centered(), area);
        }
        {
            let area = Rect::new(80, 2, 20, 5);
            let block = Block::default()
                .title_top(Line::from("Popup").centered())
                .borders(Borders::ALL);
            let lines = vec![
                Line::from("one"),
                Line::from("double"),
                Line::from("quadruple"),
            ];
            frame.render_widget(Paragraph::new(lines).block(block).centered(), area);
        }
    }

    Before the fix:

    2026-01-23.05-24-04.online-video-cutter.com.mp4

    After the fix:

    2026-01-23.05-24-49.online-video-cutter.com.mp4
  • 1ce29d6 (uncategorized) Decouple std from serde and palette features by december1981 in #2460

    The std feature now passes through to the deps rather than
    requiring std to use serde / palette.

  • 2a0b4b2 (uncategorized) Ensure consistent thumb size when scrolling by kdheepak in #2352

    This PR removes the use of f64 for calculating scrollbar thumb size
    and uses integer rounding (rounding up instead of the default rounding
    down) instead. Using integer rounding seems to fix the problem of thumb
    size not being consistent.

    Fixes #2351

  • 4986b28 (uncategorized) Allow ratatui-widgets to be used without default features by jakobhellermann in #2350

    Previously, ratatui-widgets was always included with default features.

    Additionally, the std feature would always enable the time crate.

    Fixing this gets rid of a few crates: deranged, num-conv,
    time-core, time.

  • 720303e (uncategorized) Align clear() semantics with contract by joshka in #2320

  • d2b0ce1 (uncategorized) Fix the dependency on time by asomers in #2306

    ratatui-widgets uses time's Month::length(), which requires time-0.3.37
    or later.

Refactor

  • d754c5e (app) Simplify render function in the scrollbar example by marianomarciello in #2388

    Divide the render function into two functions for vertical and
    horizontal scroll.

  • 629e4b2 (core) Reintroduce Cell::skip as a deprecated field by junkdog in #2437

    as discussed in
    #1605 (comment) -
    this brings back Cell::skip as a deprecated field in order to avoid
    breaking the API in a patch release. in terms of noise; the diff() is
    left pretty intact, but had to #[allow(deprecated)] in a couple of
    places.

    Cell::skip vs CellDiffOption

    CellDiffOption::ForcedWidth takes precedence over Cell::skip, and

    CellDiffOption::skip is already skip - so Cell::skip can only
    override when CellDiffOption::None is set. i believe this is the
    correct behavior, but probably good to have another pair of eyes on it.
    @benjajaja maybe has some input too.

    PartialEq and Hash for Cell::skip

    Cell::skip is part of PartialEq and Hash as normal fields. another
    option would be to treat CellDiffOption::None as

    CellDiffOption::Skip when Cell::skip is set, and only consider the
    effective CellDiffOption for the trait impls.


  • ca5c109 (core) Introduce CellWidth trait for cell width computation by junkdog in #2400

    this PR introduces a CellWidth trait for calculating the cell
    width/span, implemented for &str and Cell.

    The impl for Cell respects the new CellDiffOption::ForcedWidth. All
    width calculations are prefixed with a check if the symbol is ascii
    before calling symbol.width(); this micro-optimization probably won't
    do much on computers, but it helps on embedded.

    As discussed recently in #1605, this also changes the
    `CellDiffOption::ForcedWidth(type) from NonZeroUsize to NonZeroU16.


  • 9ac167d (style) Add descriptive panic for Color::Reset anstyle conversion by singhh-piyush in #2423

    Color::Reset has no equivalent in anstyle::Color. Previously,
    converting Color::Reset.into() fell through to the catch-all arm in
    From<Color> for anstyle::Color, which called

    AnsiColor::try_from(color).unwrap() and panicked with the message:
    called Result::unwrap() on an Err value: Ansi.

    This replaces the opaque unwrap() with an explicit, immediately
    understandable panic: "Color::Reset has no equivalent in anstyle".

    Fixes #2341

  • f093b02 (uncategorized) Satisfy beta Clippy buffer debug lint by joshka in #2562

    Remove a redundant borrow in Buffer's Debug implementation

Documentation

  • f8b42ad (breaking-changes) Update header for 0.30.0 by orhun in #2295

  • e82b3b7 (buffer) Run the doctests for Buffer by orhun in #2319

    addresses
    #2314 (review)

    the doctest was not running at all... (due to fn foo)

  • d8c86c3 (canvas) Add usage example for Line widget by ramadhan-dev-bright in #2473

    Added a doc-tested example for the Line struct in canvas to improve
    documentation and help new users understand how to render lines with
    color.

  • 83c1579 (changelog) Fix doubled words in two entries by adv0r in #2578

    Typo:in inin and and andand in CHANGELOG.md

  • fff27e8 (contributing) Add packages needed to run xtasks by alabhyajindal in #2409

    Fixes #2408


  • be718d0 (ratatui-core) Fix terminal rendering grammar by Zacxxx in #2555

  • 095b47d (ratatui-crossterm) Fix doubled article in module docs by adv0r in #2564

    Dear maintainer — AI-authored PR by Composer under
    @adv0r. Methodology +
    opt-out
    at tokens-for-good.
    A one-line "no thanks" → auto-apology + auto-close + permanent
    blacklist. Silent close treated the same. Your time matters more than
    this contribution.

    Typo:See the the [Examples]See the [Examples] in
    ratatui-crossterm/src/lib.rs.

  • 160b177 (ratatui-termwiz) Fix doubled article in module docs by adv0r in #2565

    Dear maintainer — AI-authored PR by Composer under
    @adv0r. Methodology +
    opt-out
    at tokens-for-good.
    A one-line "no thanks" → auto-apology + auto-close + permanent
    blacklist. Silent close treated the same. Your time matters more than
    this contribution.

    Typo:See the the [Examples]See the [Examples] in
    ratatui-termwiz/src/lib.rs.

  • 8ce5513 (sparkline) Fix typo in doc comment by adv0r in #2554

  • e6529fd (terminal) Improve terminal and setup docs for app authors by joshka in #2461

    Summary

    This updates the terminal and setup docs to better match the rendering
    behavior Ratatui actually implements, while also making the docs.rs path
    clearer for application authors.

    The main docs changes are split into two commits:

    1. docs: align terminal docs with behavior
    • align Terminal, viewport, frame, and flush docs with the real render
      pipeline
      • clarify the boundary between Terminal::flush and Backend::flush
      • improve examples for fullscreen, inline, and fixed viewport usage
    1. docs: improve terminal docs for app authors
    • improve setup-path guidance across ratatui, ratatui-core, and
      backend crates
      • clarify viewport choice, escape hatches, and common edge cases
    • reduce duplication so crate-level docs explain choices while method
      docs hold detailed contracts

    A small follow-up also fixes the crate-doc heading hierarchy in
    ratatui-core and
    ratatui-crossterm so the generated READMEs no longer need
    markdownlint-disable-next-line heading-increment suppressions.

    Verification

    • cargo test -p ratatui --doc
    • cargo test -p ratatui-core --doc
    • cargo test -p ratatui-crossterm --doc
    • cargo test -p ratatui-termion --doc
    • cargo test -p ratatui-termwiz --doc
    • cargo test --workspace --all-targets --no-run
    • cargo doc -p ratatui --no-deps
    • cargo doc -p ratatui-core --no-deps
    • cargo xtask format --check
    • cargo xtask readme --check

    Note:cargo doc -p ratatui-core --no-deps still reports pre-existing
    warnings outside this pass in layout and text docs. The terminal-doc
    warnings introduced during this work were fixed.


  • 744dc36 (uncategorized) Remove duplicate word in color and backend module by lphuc2250gma in #2535

    Two one-line typo fixes for duplicated "the" in doc-comments:

    • ratatui-core/src/style/color.rs — "/// the the older serialization
      implementation of Color are also able to be deserialized." → "...the
      older serialization..."
    • ratatui-core/src/backend.rs — "//! See the the [Examples] directory
      for more examples." → "//! See the [Examples] directory for more
      examples."

    No code/behavior change.

  • 3df8c20 (uncategorized) Ask issue authors about PR willingness by joshka in #2317

    Add a contribution question to the bug report and feature request
    templates so maintainers know whether the reporter wants to work on a
    fix/PR or needs guidance.

  • 53d925a (uncategorized) Add Documentation on the Map Projection / CRS by C-Loftus in #2324

  • 64d964b (uncategorized) Update Terminal docs by joshka in #2312

    Summary

    • Improve Terminal Rustdocs to better explain typical app setup, the
      rendering pipeline, and how
      diff-based rendering works (including full redraw behavior on viewport
      size changes).
    • Clarify viewport concepts and behavior in Viewport/Terminal docs,
      with a more complete inline
      section (anchoring, scrolling, resize behavior).
    • Reorder Terminal::draw / Terminal::try_draw closer to constructors
      so the primary rendering
      entry points are easier to find.

    Notes

    • Docs-only change; no public API or runtime behavior changes.
    • No new tests in this PR.

    Test Plan

    • cargo +nightly fmt
    • cargo +nightly docs-rs -p ratatui-core
    • cargo test -p ratatui-core --doc --features std
  • 8d73d47 (uncategorized) Fix comment to reflect correct symbol in assertion by homebrewmellow in #2314

  • fbd5621 (uncategorized) Fix misspellings by cgzones in #2310

Performance

  • dcea52b (core) Eliminate per-frame Vec allocation in Terminal::flush by junkdog in #2416

    what and how

    this PR removes the Vec<(u16, u16, &Cell)> allocation in
    Terminal::flush when calling Buffer::diff. a new method,
    Buffer::diff_iter is instead used by Terminal::flush.

    the existing diff implementation has moved to the BufferDiff iterator.

    why (who cares?)

    it's the mice. on embedded devices, allocating a short-lived, contiguous
    block of up to 40-50kb is problematic due to heap fragmentation in
    combination with tiny heaps. the requirements for a full refresh over
    1200 terminal cells is 37.5kb+vec growth padding, but since we're
    dealing with a contiguous block of memory, the actual memory
    requirements are considerably higher, depending on user-land allocation
    patterns.


Testing

  • b696ea3 (core) Split Terminal into submodules and expand test coverage by joshka in #2315

    • Split the Terminal implementation into focused submodules to improve
      readability and maintainability.
    • Add characterization tests covering Terminal initialization, buffer
      lifecycle, resizing and autoresize behavior, and rendering paths.
    • Add inline viewport tests for compute_inline_size and
      insert_before in both fallback and scrolling-regions modes, including
      an end-to-end draw -> insert_before -> draw scenario scenario.
    • Extend TestBackend cursor plumbing to support the new terminal tests
      and assert cursor/ behavior.

Miscellaneous Tasks

  • 746e9c9 (build) Add symlinks from each crate dir to root LICENSE by martinvonz in #2370

    AFAICT, each crate directory must have its own LICENSE file for it to
    become part of the published crate. This patch therefore adds LICENSE
    symlinks from each crate directory to the root LICENSE file. This
    matches what e.g. clap does (https://github.com/clap-rs/clap).

    Since ratatui-macros/LICENSE already exists and with different copyright
    holders than in the root LICENSE file, I left it unchanged.

  • 7b66bd4 (ci) Use latest released cargo-machete action by sermuns in #2469

  • b6dfafd (markdown) Fix linting issues reported by xtask lint by Logan-Ruf in #2435

  • fba4448 (ratatui) Unleash the rats v0.30.1

  • 0b03fe4 (toml) Migrate from taplo to tombi by joshka in #2501

    Summary

    This migrates the repo's TOML tooling from
    Taplo to
    Tombi.

    The main motivation is maintenance and installability:

    • Ratatui previously used Taplo for
      TOML formatting.
    • Taplo's maintenance future has been uncertain; see
      tamasfe/taplo#715.
    • Taplo's current release artifacts are not friendly to
      cargo-binstall, which means some setups fall back to building from
      source.
    • That source-build path is awkward for CI and was also a blocker while
      exploring a future Docker/devcontainer setup because of image size and
      install cost.

    Tombi is actively maintained,
    publishes prebuilt binaries, and has a VS Code extension that lets us
    keep editor behavior aligned with CI.

    What Changed

    Tooling and CI

    • Replace taplo with tombi in cargo xtask format.
    • Use
      tombi-toml/setup-tombi in
      the formatting CI job.
    • Replace .taplo.toml with tombi.toml.

    Formatting policy

    • Keep the existing 100-column TOML line width.
    • Disable schema-driven top-level table ordering for Cargo.toml.
    • Disable the corresponding tables-out-of-order lint for Cargo.toml.

    Why disable Cargo schema ordering:

    Why disable the Cargo lint warning:

    • The VS Code warning came from Tombi's tables-out-of-order lint
      rather than from formatting.
    • Since we are intentionally preserving the existing Cargo manifest
      section order, we also disable that lint for Cargo.toml to keep
      diagnostics aligned with the formatter configuration.
    • Related docs:
      https://tombi-toml.github.io/tombi/docs/configuration/.

    Installation note

    • Tombi is better positioned for binary installs than Taplo today, but
      it is still not fully in the generic cargo-binstall /
      taiki-e/install-action path.
    • Relevant upstream context:
    • binary naming / generic installer compatibility:
      tombi-toml/tombi#1164
    • crates.io publishing for cargo-binstall fallback:
      tombi-toml/tombi#1686
    • earlier crates.io publishing discussion:
      tombi-toml/tombi#632
    • In practice, this still leaves some rough edges around the generic
      Rust installer path, but Tombi already has usable prebuilt binaries and
      an official installer action, which is a better place for Ratatui than
      Taplo's current install story.
    • CI now uses
      tombi-toml/setup-tombi
      with a pinned version, which avoids the GitHub token path by not asking
      the action to resolve latest at runtime.

    Docs and editor setup

    • Update contributor docs to point to Tombi's repo, docs, and
      installation guide.
    • Note that the repo previously used Taplo and link the upstream
      maintenance discussion at tamasfe/taplo#715.
    • Add VS Code extension recommendations for
      rust-lang.rust-analyzer
      and
      tombi-toml.tombi.
    • Add committed VS Code workspace settings for shared repo defaults,
      mainly so the workspace can define formatter behavior that matches CI,
      including TOML formatter selection and nightly rustfmt args.
    • Add a dedicated formatting section to CONTRIBUTING.md covering Rust
      formatting, TOML formatting, nightly rustfmt, and the VS Code
      workspace override workaround.

    Why Commit VS Code Settings

    This PR adds committed VS Code workspace settings for shared repo
    defaults only.

    The goal is to:

    • recommend the expected extensions
    • make local editor formatting behave more like CI
    • avoid formatter drift between editor usage and the repo's checked-in
      tooling

    This is not intended to standardize everyone's full editor setup.

    Nightly rustfmt

    The repo already uses unstable rustfmt options in
    rustfmt.toml, so formatting Rust code with cargo xtask format works best with nightly Rust. This is already how CI
    behaves.

    The docs now call this out more clearly:

    • install nightly if you want local formatting to match CI
    • the reason is to pick up the unstable formatting options configured in
      rustfmt.toml

    For contributors who cannot or do not want to install nightly, the docs
    also point to a workaround: use a personal VS Code workspace file with
    override settings instead of changing the tracked workspace config.

    Notes From Implementation

    • Tombi v0.9.19 added config support to disable schema-defined
      ordering per schema, which let us preserve existing Cargo.toml section
      order cleanly.
    • The tables-out-of-order warning seen in VS Code came from Tombi
      lint, not formatting, so that needed a separate config change.
    • After disabling Cargo section ordering, the remaining TOML churn was
      much smaller and mostly mechanical.

    Files of Interest

    • .github/workflows/ci.yml
    • xtask/src/commands/format.rs
    • tombi.toml
    • .vscode/settings.json
    • .vscode/extensions.json
    • CONTRIBUTING.md

    Verification

    • cargo xtask format --check
    • cargo metadata --format-version 1
    • markdownlint-cli2 CONTRIBUTING.md
  • ed46fef (uncategorized) Update versions

  • f30bab9 (uncategorized) Ignore licker as typo

  • c7746e9 (uncategorized) Bump MSRV to 1.88.0 by orhun in #2396

  • b0a7703 (uncategorized) Escape usernames in changelog with backticks by kdheepak in #2377

    This PR escapes all github usernames in the CHANGELOG.md file by adding
    a backtick before and after the username.

    e.g.:Before

    1dc18bf (calendar) Add width and height functions by joshka in #2198
    

    After

    1dc18bf (calendar) Add width and height functions by `@joshka` in #2198
    

    This change should prevent unnecessary tags in PRs and forks, at least
    until the next release is made with git cliff changes from release-plz

  • 0031fc6 (uncategorized) Remove @ sign from github release and PR by kdheepak in #2353

    Previous PR (#2336) didn't solve
    the problem, and everyone still seems to be getting notifications (e.g.
    #2348).

    This PR removes the @ for GitHub usernames in the autogenerated PR
    body by release-plz.

    {{ changelog | replace(from=" by ", to=" by ") }}
    

    The motivation behind this is that GitHub mentions in PR descriptions
    trigger notifications for every contributor for every release because
    their username is part of the changelog.

    With this change, in GitHub PR descriptions the changelog will change
    like so:

    Before

    1dc18bf (calendar) Add width and height functions by joshka in #2198
    

    After

    1dc18bf (calendar) Add width and height functions by joshka in #2198
    
  • ab5ad3f (uncategorized) Escape username using backticks in changelog by kdheepak in #2336

    The motivation behind this is that GitHub mentions in PR descriptions trigger notifications for every contributor for every release because their username is part of the changelog.

New Contributors

Don't miss a new ratatui release

NewReleases is sending notifications on new releases.