Repository navigation
Releases: everruns/tuika
Release list
Release v0.13.0
Released alongside tuika-charts 0.3.0, tuika-codeformatters 0.7.0,
tuika-html 0.3.0, and tuika-mermaid 0.5.0. Every companion's tuika
requirement moves to 0.13.
Highlights
Program status for the terminal (OSC 7501) — a host can now tell the
terminal what it is doing instead of leaving it to guess from a spinner: idle,
working with 0–100 progress, done, blocked on the user, or errored, as a tree of
records keyed by id. tuika::term::program_status owns the bytes and validates
everything the host passes in; the host owns the semantics. Verified against
tuios, which renders the reports in its pane chrome and agent-state query.
- Dependencies:
tuika-htmlmoves to html5ever 0.40 and drops
markup5ever_rcdom, building its own small DOM through a vendoredTreeSink.
OSC 7501 changes nothing in tuika's own cell grid, so the highlight is the
program_status example's recording inside tuios rather than a gallery scene.
Breaking Changes
-
Clicking a select row confirms only when it is already highlighted. In
SelectStateandKeyedSelectState(and the multi-select variants), a click
on an unhighlighted row now moves the highlight and reportsChanged; a click
on the highlighted row reportsSubmitted. Keyboard Enter is unchanged. No
signature changed. A host that wants single-click confirm treats both
outcomes as confirm:// before: a click on any row returned Submitted if outcome == InputOutcome::Submitted { confirm() } // after: keep single-click confirm explicitly if matches!(outcome, InputOutcome::Changed | InputOutcome::Submitted) { confirm() }
Added
tuika::term::program_status—Report(builder:id,kind,
progress,app,title,msg,encode;Report::clear),State,
BlockKind,write, andencode_query.Report::encodereturnsNonefor
anything a conforming terminal would discard: bad id or app grammar, ids over
128 bytes or 8 segments, and C0/C1 controls in titles and messages.- The
program_statusexample (working with progress, blocked on a question,
done/error, clear on exit, with the sent payloads on screen), and OSC 7501
reporting in thecodexexample. - A Program status section in the terminal features guide.
What's Changed
- feat(term): verify OSC 7501 against tuios, add example and docs (#141)
- feat(term): add OSC 7501 program status reports (#140)
- feat(tuika-html): vendor TreeSink DOM, drop rcdom, html5ever 0.40
- chore(deps): bump futures-core to 0.3.34
- fix(site): resolve 11 Dependabot alerts in docs lockfile (#135)
- fix(select): confirm only when clicking the highlighted row (#134)
Release 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.
- 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::widthandLine::widthnow count grapheme-aware
display columns, so they agree with whatSurfaceactually 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 fromratatui-coreto tuika, so
any signature naming one changes.For most hosts the migration is an import change — the canonical path is
unchanged:// 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}andtuika::term::backend::CrosstermBackend
instead of ratatui's, andtuika::term::testbackend::TestBackendin tests.The types are deliberately shaped like the ones they replace, with two
behavioral notes:Span::widthandLine::widthcount grapheme-aware display columns
(width::str_cols) rather than per-charwidths, so they now agree with
whatSurfaceactually paints. A line containing a ZWJ emoji measures
narrower than before — and correctly.Cellstores short grapheme clusters inline instead of in a
CompactString.Cell::newis no longerconst; useCell::default()
for a blank cell.
-
Ratatui interoperability is now behind the
ratatuifeature, off by
default.Surface::render_ratatui,tuika::interop, andRatatuiViewneed
it; a host wrapping ratatui widgets adds: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, whichrender_ratatuiwas already doing a copy for. -
Surface::render_ratatuiwas split. The general escape hatch — a private
scratch buffer, composited back through the clip — is now
Surface::render_scratchand takes tuika'sBuffer.render_ratatuiis the
ratatui-specific wrapper around it. A caller using it for scratch access
rather than for ratatui widgets should switch torender_scratch, which needs
no feature. -
Line::rawandLine::styledsplit 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 throughratatui_core::symbols::pixel.Style::{bold, dim, italic, underlined, reversed, crossed_out}shorthands.tuika::term::backend::CrosstermBackendand
tuika::term::testbackend::TestBackendare public, for a host driving
Terminalitself.
Changed
- Dependencies: 55 crates to 32. Dropping
ratatui-coreremoved 23,
includingkasuari(a Cassowary solver),lru, and a secondhashbrown—
all of which existed forratatui_core::layout::Layout, which tuika replaced
with its own flex solver and never called — plusstrum,compact_str,
itertools,unicode-truncate, and a secondsynmajor 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 aCellnow stores short
grapheme clusters inline. The markdown paths rise ~2%, the cost of measuring
widths grapheme-aware; the benchmark baseline is updated accordingly. scrolling-regionsis now a feature of tuika's ownBackendtrait, and no
longer pulls inratatui-crosstermto forward a flag.- The companion crates no longer depend on ratatui.
tuika-charts,
tuika-html, andtuika-mermaiddroppedratatui-core, and
tuika-codeformattersdropped the fullratatuiumbrella; none of them had
named a ratatui type since tuika took over the cell grid.ratatuiis no
longer part oftuika-codeformatters' public interface, so a dependant pins
onlytuika. - Public documentation follows the new model: the guides, the component gallery,
and the getting-started page import fromtuika::uiinstead ofratatui::*,
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 — socargofailed to parse
Cargo.tomlbefore 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 therustupdefault, whichrust-toolchain.toml's development pin
outranks, so every check ran on the newer toolchain. The job now pins
RUSTUP_TOOLCHAINand fails if the active toolchain is not the
rust-versioninCargo.toml.
What's Changed
- refactor(deps)!: own the cell grid and drop ratatui (#114)
- fix(ci): make the MSRV job run the MSRV (#131)
- fix(demos): re-record scroll and close the broken-capture class (#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
Release v0.11.1
tuika only. The companion crates carry a caret requirement on tuika 0.11, so
tuika-charts 0.1.2, tuika-codeformatters 0.5.0, tuika-html 0.1.4, and
tuika-mermaid 0.3.2 pick this up without a republish.
Highlights
Drag selection stays in the panel it started in — a left drag that begins
inside a bordered Boxed now selects only within that panel: positions clamp to
its inner rect, a multi-row selection wraps at the panel's edges instead of the
screen's, and the copied text never picks up the pane beside it. A drag that
starts outside every panel still spans the screen as before.
- Robustness: a
SelectionRangethat outlived the geometry it was made in
can no longer index the buffer out of bounds.
Changed
- Runner drag selection is per panel. A left drag that starts inside a
borderedBoxedselects only within that panel: positions clamp to its
inner rect, a multi-row selection wraps at the panel's edges instead of the
screen's, and the copied text never picks up the pane beside it. A drag that
starts outside every panel still spans the screen as before. - Examples render in a real terminal by default rather than printing a frame.
Fixed
- A
SelectionRangethat reaches past theareahanded to
mouse::paint_selection,mouse::selected_text, orSelectionRange::contains
now stops at that area's rows instead of indexing the buffer out of bounds. - The routing guide is published on the website again.
Added
mouse::SelectionState::confine/region— restrict a gesture to a rect and
read it back, for hosts driving the selection primitives from their own loop.app_shellandworkbench_demoexamples, with recordings.
What's Changed
- fix(release): ignore dev-dependencies when ordering the publish
- docs(demos): add interactive AppShell recording
- feat(examples): add AppShell application
- fix(site): publish routing guide
- docs(demos): restore Workbench palette (#123)
- docs(readme): track current demo assets
- docs(demos): adopt Solarized Dark capture theme
- feat(examples): add native workbench showcase
- feat(examples): render in terminals by default
- feat(mouse): confine drag selection to the panel it starts in (#118)
Release v0.11.0
Released alongside tuika-charts 0.1.2, tuika-codeformatters 0.5.0,
tuika-html 0.1.4, and tuika-mermaid 0.3.2. Their tuika dependency
requirements now track 0.11.
Highlights
One route for every event — a host registers each surface once and every
Event kind (key, paste, mouse, focus) follows the same path to whichever
surface owns input this frame, so a paste can no longer take a different route
from a key and land behind an open overlay. Router resolves stages in a fixed
order and Delivery reports which surface received what, including the case
where nothing did.
Streaming markdown renders identically to a one-shot render — two cache
boundaries that only misfired at particular chunk splits are fixed, so a
document fed in as it arrives renders exactly like the same document rendered
whole.
- Binary size: a default
tuika-codeformattersbuild measured ~24.0 MiB on
a highlighting probe binary; arust+pythonbuild is ~4.8 MiB. A tuika
host that places no images drops ~8.4 KiB of graphics encoders. - Dependencies: 66 crates to 55 (the
asyncgraph, 71 to 59).
Breaking Changes
-
tuika-codeformatters: every tree-sitter grammar now sits behind its own
cargo feature. All fourteen are on by default, so a dependant taking the
crate normally is unaffected. A dependant that already passes
default-features = falsenow gets no grammars until it names them:tuika-codeformatters = { version = "0.4", default-features = false, features = ["rust", "python"] }
Feature names are the language keys lowercased without punctuation —
rust,
python,typescript(covers TSX/JSX),go,java,ruby,css,html,
csharp,php,zig,scala,sql. A language whose feature is off
behaves exactly like one the crate never supported:highlightreturns
Noneand the caller renders plain code. -
Paragraphand dialog copy wrap differently. Both now use tuika's own
wrap solver — the oneWrap/wrap_lineshas always used — instead of
textwrap. Three visible differences, all of them makingParagraphagree
with the rest of the toolkit:- Line breaks are chosen greedily (first-fit) rather than by textwrap's
optimal-fit minimum-raggedness pass, so a paragraph's rows may break at
different words than in 0.10. - A run of whitespace between two words is one break opportunity and renders
as a single space, instead of being copied through verbatim. - Breaks happen only at whitespace. textwrap also broke at Unicode line-break
opportunities such as/, which could split a linkified URL after its
scheme; a URL now stays on one row unless it is wider than the row.
Widths are counted in tuika's grapheme-aware display columns, which fixes a
latent bug:Paragraphwrapped with textwrap's per-charwidth model and
then measured the result withstr_cols, so text containing a ZWJ emoji
(a family, a flag, a skin-tone sequence) wrapped earlier than its own
measurement said it needed to. - Line breaks are chosen greedily (first-fit) rather than by textwrap's
Changed
-
tuika-codeformattersbinary size: grammars are selected by a runtime
language string, so the config builder named all fourteen and anchored every
parse table into any binary that highlighted anything — roughly 21 MiB of
read-only data, C# alone about 5 MiB. Selecting grammars by feature lets a
host drop the ones it does not use: measured on a probe binary highlighting a
single snippet, the default build is ~24.0 MiB and arust+pythonbuild
~4.8 MiB. Highlighting output under default features is unchanged. -
Graphics protocol encoders are now pay-for-use.
ImageLayer::emitruns
after every frame in everyRunnerhost and chose its encoder by matching on
ImageSupport, which linked the Kitty, iTerm2, and Sixel encoders plus the
PNG writer into every binary that used tuika at all. The encoder is now
resolved where an image is recorded — reachable only from theImageview —
so a host that places no images no longer carries any of them: about 8.4 KiB
less tuika code in a minimal host. The emitted bytes are unchanged for all
three protocols, and no public API changed. -
Dependencies: 66 crates to 55. Three runtime dependencies were removed by
taking work tuika was already half-doing:textwrap(withsmawkandunicode-linebreak) — replaced by the wrap
solverWrapalready used. See Breaking Changes for the visible
difference.ratatui-crossterm(with theinstability/darlingproc-macro tree behind
it) — tuika now writes to the terminal through its own crosstermBackend.
It already implemented that whole trait once, inHyperlinkBackend, to wrap
the one being removed. The emitted byte stream is held byte-for-byte
identical by a differential test that draws the same cells through both
backends. The crate remains an optional dependency of the
scrolling-regionsfeature, which forwards its matching flag so a host's
own copy still compiles.tokio-stream(underasync) — replaced byfutures-core, which the
build already contained:asyncenablescrossterm/event-stream, and that
pulls it in.tokio_stream::Streamisfutures_core::Stream, so a host
passing aReceiverStreamis unaffected;tokio-streamstays a
dev-dependency and the doc example still uses it. Theasyncgraph goes
from 71 crates to 59.
No public API changed, and no rendered output changed outside the wrapping
differences above.
Added
-
Input routing: the new
routingmodule (Router,Delivery,
RouteStage,InputTarget, all re-exported from the crate root and the
prelude) delivers an event to the surface that owns input this frame. A host
registers each surface once and everyEventkind follows the same route, so
a paste can no longer take a different path from a key and land behind an open
overlay.Routerreads theFocusRegistryan overlay-bearingScenealready
synchronized — and does not hold it, so a stage closure may take&muton the
host — resolves stages in a fixed order (global chord, declared exceptions,
the active surface, last-chance global), and reports throughDeliverywhich
surface received what, including the case where nothing did. See the
routing guide. -
term::hyperlink::apply_buffer_links_in: the area-clipped counterpart to
apply_buffer_links, for a component embedding OSC 8 runs into a sub-rect of
a shared buffer.apply_buffer_linksis unchanged and still clips to the
whole buffer. -
Fuzz and black-box robustness suites (
src/tests/fuzz.rs,
tests/robustness.rs): adversarial text and event streams through the wrap
solver, composed view trees, the stateful components, and the parsers that
read untrusted terminal replies; plus a sweep of every component against every
corpus at every degenerate size, asserting no panic, no paint outside the
component's own rect, and no control byte in a cell. Both fixes below came out
of them. Test-only — no API change. -
Session stress suite (
tests/stress_ui.rs): every screen mode driven
throughRunnerandAsyncRunnerover an in-memory screen that resizes
between frames, plus mode changes mid-session, adversarial scrollback
publishing, and shell/overlay/dock composition at degenerate sizes. The
split-footer fix below came out of it. Test-only — no API change.
Fixed
-
A split footer is pinned and published at the terminal's current size.
Both runners write the footer's reserved rows, and render each queued
scrollback block, at the geometry theTerminallast observed — and neither
observed a resize before the loop's first frame or before draining the
publish queue. Two consequences, both on the ordinary path: a window resized
between the terminal being constructed and the first frame pinned the footer
at the old size, and a block published in response to a resize (a host
logging the new dimensions, a status line reflowing) was committed to the
scrollback at the previous width. Both loops now learn the size first. -
A component no longer stamps hyperlink markers outside its own rect.
ParagraphandMarkdownembed OSC 8 runs into the cells of a linked label
after painting, and the placement was clipped to the buffer rather than to
the rect the component was laid out in. A markdown block whose links ran past
the rows or columns it was given wrote an escape onto a neighbouring
component's cell — visible as a stray link, or an orphaned escape once the
neighbour repainted. Placements outside the area are now dropped. -
Streamed markdown renders identically to a one-shot render. The settled
prefix is cached at the last blank line that ends a block, and two constructs
were mistaken for one:- A fence line carrying an info string (
```rustinside an already-open
block) was treated as a closer, so a later blank line settled a boundary
inside a code block; everything after it rendered as markdown while a
re-render of the same source rendered it as code. - A blank line between list items settled the prefix, so the rest of the list
was parsed in isolation and its numbering restarted — a streamed
1. … 2. … 3. …rendered as1. … 1. … 1. …. A blank line inside a list is
a boundary only once a following top-level block proves the list ended, and
never on the strength of an in-flight partial line (1before it becomes
1.).
Both were found by a new streaming/one-shot fuzz differential, and both
depended on where the stream's chunk boundaries happened to fall. - A fence line carrying an info string (
-
A link's underline stops at the URL. When markdown wrapped a run of spans,
the space it re-inserted between two words inherited the style and OS...
Release v0.10.0
Released alongside tuika-charts 0.1.1, tuika-codeformatters 0.4.3,
tuika-html 0.1.3, and tuika-mermaid 0.3.1. Their tuika dependency
requirements now track 0.10.
Added
- Components:
ProgressBar::label_stylestyles the caption set by
label, the same theme-by-default/explicit-override pattern ascolors. The
trailingNN%ofpercentstays separate chrome on the theme's muted style. - Components:
SelectionAnchorselects howTableandSelectListwindow
themselves around the selection when they resolve their own window —
Center(today's recentering behavior, still the default) orEdge, which
moves the window only when the selection would leave it. - Virtualization:
VirtualWindow::keepingis the edge-following policy as a
pure function of the caller's current start;SelectViewportState::resolve
now uses it, so the persistent and stateless paths share one implementation. - Charts: both axes carry tick labels, so a chart states its scale instead
of leaving the reader to guess it. Ticks land on round values (a power of ten
times 1, 2, 2.5, or 5) and label precision follows the tick step.
Axis::formatreplaces the numeric formatter,Axis::tickstargets a tick
count, andAxis::hiddenopts an axis out. Labels are drawn as cells in
both render modes — the graphics image covers the plot only — so the same
numbers land in the same cells whether or not the terminal has graphics.
Changed
- Breaking: Mouse capture is now opt-in in alternate-screen mode, preserving native OSC 8
link activation, terminal selection, and scrolling by default. Applications
that need pointer or wheel events useScreenMode::with_mouse_capture,
MouseCapture::Enabled, orAltScreen::enter_with_mouse_capture; captured
sessions may use runner selection andctrl_click_urlas application-side
fallbacks. MarkdownState::linkspreserves labeled and bare hyperlink targets across
incremental updates, settled-prefix caching, wrapping, and resize, so hosts
drawing streaming lines can apply native OSC 8 links without re-parsing.- Charts:
Axis::categoriesnames positions instead of measuring them,
which is what most bar and area charts actually want.Chart::stackcombines
bar and area series —Stack::Normalsums them,Stack::Percentscales every
position to 100 — while unstacked bar series split their category band into
slots and sit side by side.Chart::horizontalswaps the axes so a category
gets a whole row of width for its name.Series::markersandSeries::labels
annotate samples,Chart::focusmarks one position and lists every series'
value there, andSeries::donutwithChart::centerdraws a ring. A rule is
drawn along zero whenever the value domain straddles it. - Breaking: chart axis labels are on by default, so a chart that previously
filled its whole area now gives a few columns to the y-axis gutter.
Chart::x_axisandChart::y_axiswithAxis::hiddenrestore the old
geometry exactly; the x labels reuse the margin row the plot already reserved
and cost no plot height. Aligning both renderers on one cell grid also removed
the graphics path's internal margins, so a graphics plot now covers the same
data cells its portable counterpart does.
Fixed
- Charts: graphics-mode plots are transparent where nothing is drawn rather
than filled with the theme background, so cell-drawn text — value labels, a
donut's centre text — is no longer hidden by the image composited over it. - Charts: automatic y domains reach the zero baseline whenever a chart
carries a bar or area series. These marks encode their value in the filled
span, so a domain starting at the data minimum drew a bar of 1 beside a bar
of 5 as a sliver beside a full column — a ratio the data never contained.
Line, step, and scatter series are read as positions and keep the tighter
domain that resolves their variation;Chart::y_domainstill overrides both.
Release v0.9.0
Released alongside tuika-charts 0.1.0 — its first release — plus
tuika-mermaid 0.3.0, tuika-codeformatters 0.4.2, and tuika-html 0.1.2.
Their tuika dependency requirements now track 0.9.
Highlights
Charts, in a companion crate. tuika-charts renders line, area, bar, step,
and scatter series and picks its own fidelity: terminal graphics where the
emulator supports them, portable half-block and Braille cells everywhere else,
from the same Chart description.
Trees and multi-pane interaction. TreeList brings stable-id expansion,
selection, and persistent scrolling over host-owned rows, on the same reusable
primitives — SelectViewportState, FocusRegistry::focus, hit testing — that
let several panes in one screen share focus and the mouse.
- Runner: one
FrameSourceseam and oneSignalreplace tenrun*methods
across the two runtimes; the synchronous loop is now drivable from a
caller-owned terminal, so a whole application can be tested with no tty.
Added
-
Runner::run_driven_byis the synchronous counterpart to
AsyncRunner::run_driven_by: the loop against a caller-owned terminal and
event source, with noTerminalSession, no terminal construction, and no
stdout-facing graphics or clipboard work.runandrun_with_backendare now
built on it, so every synchronous entry point shares one loop. It is generic
over the run's error type, so an infallible backend (TestBackend) pairs with
an infallible source. -
EventSourceis where that loop gets its input, andscripted_eventsbuilds
one from a known sequence of events — replaying them in order, then going idle
like a quiet terminal so tick pacing is unchanged. Together they let a host
(or a test) drive a whole application with no tty: previously the synchronous
loop could not be exercised at all, and only its pieces (RunnerCore, the
clock, selection) had coverage. Seven end-to-end tests now cover the initial
frame, event-driven state, clean updates neither rebuilding nor repainting,
the borrowed application seam, redraw requests, deferred resize repaints, and
split-footer publishing. -
SelectViewportStatecouples index selection to a persistent top row for
SelectListandTable. Its resolvedVirtualWindowis shared with mouse
hit testing, so selection scrolls only across viewport edges and clicks do not
recenter the list. The existing selection-centered.viewport(rows)remains
available; persistent hosts migrate toresolveplus.visible_window. -
TreeList,TreeRow, andTreeStateprovide stable-id expansion, selection,
keyboard/mouse navigation, ancestor fallback across refreshes, persistent
scrolling, branch rendering, and a scrollbar over host-provided tree rows. -
FocusRegistry::focus(id)lets aHitMapfocus a registered pane while
rejecting unknown ids and requests blocked by overlay input ownership. -
AsyncRunner::run_with_messagesdelivers a typed application stream beside
terminal events and ticks, with deterministic completion/error/redraw/exit
behavior and no shared mutable state or polling. -
AsyncApplicationis the borrowed-view application seam for the async runner,
the counterpart toApplication: previously a frame could borrow application
state only on the synchronous runner, so a Tokio host had to clone into an
owned tree or share throughRc<RefCell<_>>. -
FrameSource/AsyncFrameSourceis the single seam every run method now
takes, with exactly two implementors:&mut appfor anApplication, and
from_fn(&mut state, view, update)(async_from_fnfor the async runner) for
the closure form.no_messages()is the empty application-message stream. -
AsyncRunner::redraw_handlematchesRunner::redraw_handle, so aLive
value (or any background producer) can mark the next frame stale on either
runner.RedrawHandlenow registers the waiting task's waker, so the async
loop is woken by a request instead of only noticing one on its next tick;
bursts still coalesce into a single repaint. -
RunnerandAsyncRunnerrestore drag-to-select behavior by default when
their terminal session captures the mouse. Selection is painted over the
final cell frame, releasing copies through OSC 52 on real-terminal runs, and
wheel events continue to reach application scrolling. Applications can claim
gestures withUpdateResult::Consumed/Dirtyor opt out with
with_text_selection(false). -
Hover styling:
mouse::HoverTrackerpairs the pointer-motion stream with the
existingHitMaphit-testing, reporting when the hovered region changes so a
host can restyle it (and knows a redraw is warranted). -
Timed style transitions:
anim::Transitionis a retargetable eased ramp for
state-driven motion (hover on/off, focus, expansion) — retargeting mid-flight
continues from the current value instead of jumping.anim::lerpand
style::lerp_colorinterpolate scalars and 24-bit colors; non-RGB colors
snap to the nearer endpoint, since an indexed color has no blendable value. -
Gradient color spans:
style::Gradientis a multi-stop color ramp with
even (across) or explicit (with_stops) stops;Gradient::linesweeps a
string's foreground across the ramp per display column (wide glyphs get one
coherent color) for use withText.TransitionandGradient(with
lerp_color) are also in the prelude.
Fixed
- The published crate excludes the generated
site/bundle and the public
docs/tree, keeping the 0.9.0 archive below crates.io's 10 MiB upload limit.
README guide and image links now use release-tag-pinned absolute URLs, and the
split-footer recording lives in its focused guide rather than being
duplicated in the README.
Committed IAI benchmark result snapshots are excluded from both crates that
own them; benchmark source remains available. tuika-mermaidrenders a Mermaid decision node with a<br/>label as a
diagram, with every label line inside the shape. The upstream defect
(mmdflux#387) — which
unwound out ofView::renderand took the host application down, and failed
silently in release builds by dropping the label or painting a short one over
the shape's own borders — is fixed in mmdflux 2.6.1, whichtuika-mermaid
now requires. The adapter-side guard that degraded these fences to the
code-block fallback is gone with it; the unconditional panic containment
around the layout engine stays.
Changed
Breaking: the runner's run* surface is one seam plus two axes. The methods
were a cross product of frame source, runtime, terminal ownership, and extra
input, encoded as names — ten of them on AsyncRunner — with arbitrary holes in
the product. The frame source is now a FrameSource argument rather than a
name, and messages ride the existing Signal, which leaves only where the
terminal and the input come from in the names.
Signal gained a message type parameter, Signal<M = Infallible>. Because the
default is uninhabited, an existing match signal { Signal::Tick => …, Signal::Event(e) => … } still compiles and stays exhaustive. A match on a
reference (match &signal) does need a _ arm added. AsyncSignal<M> is now
a deprecated alias for Signal<M>.
| Before | After |
|---|---|
Runner::run(theme, state, view, update) |
Runner::run(theme, from_fn(state, view, update)) |
Runner::run_app(theme, app) |
Runner::run(theme, app) (shim, deprecated) |
Runner::run_with_backend(theme, backend, state, view, update) |
Runner::run_with_backend(theme, backend, from_fn(state, view, update)) |
Runner::run_app_with_backend(theme, backend, app) |
Runner::run_with_backend(theme, backend, app) (shim, deprecated) |
AsyncRunner::run(theme, state, view, update) |
AsyncRunner::run(theme, async_from_fn(state, view, update)) |
AsyncRunner::run_with_messages(theme, state, messages, view, update) |
AsyncRunner::run_with_messages(theme, async_from_fn(state, view, update), messages) |
AsyncRunner::run_with_backend(theme, backend, state, view, update) |
AsyncRunner::run_with_backend(theme, backend, async_from_fn(state, view, update)) |
AsyncRunner::run_with_events(terminal, theme, state, events, view, update) |
AsyncRunner::run_driven_by(terminal, theme, async_from_fn(state, view, update), events, no_messages()) (shim, deprecated) |
AsyncRunner::run_with_events_and_messages(terminal, theme, state, events, messages, view, update) |
AsyncRunner::run_driven_by(terminal, theme, async_from_fn(state, view, update), events, messages) (shim, deprecated) |
Methods marked shim keep working with a deprecation warning. The rest changed
shape under the same name, so they fail to compile rather than warn — the
migration is mechanical and the table above is exhaustive.
-
Paragraphnow treats its input as human-facing prose: barehttp(s)URLs
use the stylesheet's link role and carry OSC 8 targets through wrapping by
default.Paragraph::link_policy(LinkPolicy::NONE)restores literal,
single-style rendering;Text,Wrap,CodeBlock, andConsoleremain
inert. -
Breaking:
UpdateResult::Cleannow means an input was unhandled and may
receive runner default behavior; return the newUpdateResult::Consumedfor
a handled input that does not need a repaint. Exhaustive matches over
UpdateResultmust add the new variant. -
tuika-mermaidre-lays out a diagram wider than the available columns at
progressively tighter node separation until it fits, instead of letting it be
clipped at the pane's right edge. Fitting is best-effort: a graph with many
parallel branches can be irreducibly wid...
Release v0.8.0
Released alongside tuika-codeformatters 0.4.1, tuika-mermaid 0.2.1, and
tuika-html 0.1.1. Their tuika dependency requirements now track 0.8.
Highlights
Turn-key application shell. AppShell, a borrowed Application runtime,
completion palettes, dialog presets, and activity lists give hosts a coherent
foundation for responsive tool-style applications without retained view state.
Key-stable collection views. SelectionScreen and KeyedTable render
borrowed data while preserving application-key selection across reordering,
filtering, and streaming updates.
Added
view_fn(measure, render)defines one-off borrowed views inline with explicit
intrinsic measurement and allocation-freeFnrendering, and composes anywhere
a normalViewis accepted.SelectionScreencomposes responsive action, agent, permission, and resume
pickers fromAppShell, borrowed selectable rows, semantic heading/rule
styles, and optional custom chrome while keeping short-height selections
visible.ApplicationandRunner::{run_app, run_app_with_backend}provide a
data-driven synchronous runtime whose frame tree can borrow application state
throughScopedElement<'_>;TestHarness::{render_app, step_app}exercises
the same contract without a terminal, and the existing owned-element closure
API remains.KeyedTable,KeyedColumn,KeyedSelectState, and
KeyedMultiSelectStateadapt and render only visible borrowed rows while
preserving application-key selection across reorder, filtering, and
streaming updates, with scrolling margins, keyboard/mouse navigation,
responsive columns, aligned styled cells, and leading cursor/check indicators.KeyedRowSourceandNavigableKeyedRowSourceletKeyedTableborrow an
indirect visible order from authoritative storage, compare computed composite
identity without per-frame key clones, and materialize a key only when input
changes selection. IndexedKeyedColumnconstructors project parallel row
metadata such as fuzzy-match spans without cached wrapper rows.AppShellcomposes a responsive tool-style application frame from borrowed
or owned header, main, status, and footer views, with optional theme-aware
rules and short-terminal chrome collapse.CompletionItem,CompletionState, andCompletionPaletteprovide reusable
fuzzy-ranked command and token completion with host-owned query/selection
state and explicit replacement text.ConfirmDialog,ChoiceDialog,MultiChoiceDialog, andInputDialog, with
paired host-owned state types, provide higher-level modal flows that convert
into the generalDialogbuilder.ActivityItem,ActivityStatus, andActivityListrender multi-step task
lifecycle state, including optional determinate progress for individual
steps, without owning scheduling or workflow state.
Changed
SelectListnow further clamps its configured viewport to the height it is
actually rendered into, keeping a selected row visible after parent chrome
or a terminal resize reduces the allocation.- Runner resize signals now force a frame even when application updates return
UpdateResult::Clean; headless harness resize signals also apply their new
viewport dimensions automatically.
Release v0.7.0
Released alongside tuika-html 0.1.0, the new companion crate behind the
block-HTML seam, plus tuika-codeformatters 0.4.0 and tuika-mermaid 0.2.0.
The existing companions adopt tuika 0.7's breaking view-measurement API and
update their tuika dependency requirement in the same release.
Highlights
Responsive application primitives. Docked panels, target-relative overlays,
virtualized collections, semantic styles, and single-line input give hosts a
coherent framework for complex terminal applications without retained UI state.
Rich HTML in Markdown. Presentational inline HTML now shares Markdown's
styles, while the new parser-free HtmlBlockRenderer seam lets tuika-html
render styled block HTML without adding a parser to tuika core.
Added
-
Wrapped flex lines with independent row/column gaps, grow and shrink weights,
min/max constraints,align_self, cross-lineAlignContent, and exact
boundary-based cell rounding.FlexItemStyleseparates child properties from
LayoutStyle;solve_layoutalso reports resolved line geometry. -
Flowfor intrinsic-width wrapping and a deliberately small equal-column
row-majorGridcomponent. -
Extensible
MeasureRequest/AvailableSpacemeasurement, with known axes
and definite/min-content/max-content modes. The default adapter preserves
existingView::measureimplementations. -
Runtime-neutral
RunnerCore, configurableTerminalSessionConfig, and
testing::TestHarnessfor state/signal/view application tests without a
terminal or async runtime. -
render_once/write_oncefor ANSI-styled ordinary output, andview!
when(...)/for(... in ...)composition. -
ClockandSystemClockprovide one monotonic time seam.
SelectionState::handle_with_clockmakes double-click gestures deterministic,
andRunner::with_clocklets replayable synchronous hosts own tick time;
existinghandleandRunner::newbehavior remains system-clock backed. -
VirtualWindowprovides overflow-safe clamped and selection-centered ranges;
Scrollbarrenders the same window vertically or horizontally with semantic
styling and local glyph/style overrides. Scroll, item scroll, viewport,
select, and table now share those primitives.SelectList::windowedand
Table::windowedaccept only the visible records while preserving absolute
selection and full-collection scrollbar geometry. -
DockState,DockSpec, andDockLayoutprovide a host-owned responsive
lifecycle for one auxiliary panel: wide panels dock, narrow passive panels
hide, and focused narrow panels resolve as overlay drawers without introducing
a retained panel manager. -
Target-relative overlays:
TargetPlacementselects above/below/left/right,
cross-axis alignment, gap, and optional edge-aware flipping;
OverlaySpec::resolve_targetresolves it directly and
SceneOverlay::targetfollows aRectProbefrom the scene root in the same
frame. Screen-anchored placement remains unchanged. -
StyleRoleandStyleResolverform an open semantic styling seam for hosts
and companion crates.RenderCtx::styleresolves built-in or namespaced
application roles, resolver bundles partially overlay stylesheet defaults,
and resolver revisions invalidate measurement caches.paint_with_context
andtesting::render_with_contextinstall and test the complete policy. -
ScopedElement<'view>, the boxed frame-borrowed counterpart to owned
Element, for heterogeneous component subtrees that read host state without
cloning it. -
Inline HTML in markdown. The presentational inline tags render instead of
being dropped:<b>/<strong>,<i>/<em>/<var>/<cite>/<dfn>,
<code>/<kbd>/<samp>/<tt>,<s>/<del>/<strike>,<u>/<ins>,
<mark>,<a href>,<img src alt>,<br>, and<sub>/<sup>. Each
resolves the sameStyleSheetrole as the markdown construct it mirrors, so a
host that restylesstrongrestyles<b>with it;<a>and<img>take the
existing hyperlink andImageResolverpaths. No new dependency and no HTML
parser: this is a fixed tag whitelist, so anything outside it — block-level
HTML,<script>, unlisted attributes — is dropped as before, and never echoed
as literal markup. -
One structured markdown block seam.
MarkdownBlockRendererreceives a
non-exhaustiveMarkdownBlockdescriptor (FencedorHtml) and a shared
MarkdownBlockContextcontaining width, theme, and the active stylesheet.
Markdown::block_rendererandMarkdownState::with_block_rendererappend to
an ordered renderer chain, so Mermaid, HTML, and host-defined block parsers
compose without adding another trait or field for every syntax. -
tuika-htmlbounds nesting on the source before parsing, so a fragment deep
enough to overflow html5ever's recursive tree building is refused rather than
crashing the host. -
markdown::Renderersbuilds the same ordered block-renderer chain for
markdown::to_lines_with/to_linked_lines_with. -
ProgressBar::labeldraws a centered, clipped caption over determinate and
indeterminate bars. -
Scroll::wrap(true)reflows owned styled lines at render width before
applying the scroll window. -
Table::selection_styleandSelectList::selection_styleallow per-instance
selection foreground, background, and modifiers. -
SelectNavigationpolicies for optional j/k, Ctrl+N/P, Tab/Shift+Tab, and
numeric selection aliases; explicit mouse hit-testing onSelectState; and
MultiSelectStatefor toggleable multiple-selection workflows. -
KeyHints::from_keymap, priority-aware whole-hint fitting, andKeymapHelp
so one labeled keymap declaration drives dispatch, responsive footer hints,
and a complete help view. -
SingleLineInputStatefor search/command fields, with newline normalization,
Enter/Ctrl+J submission, and allocation-free borrowed text access. -
tuika::uire-exportsRect,Color,Style,Modifier,Line, andSpan
for custom views without a directratatui-coredependency. -
testing::render_with_sheetrenders consumer views under an explicit
stylesheet in the same hermetic buffer harness astesting::render.
Changed
-
Breaking:
FencedBlockRendererandHtmlBlockRendererare replaced by
MarkdownBlockRenderer. Match onMarkdownBlock, read width/theme/sheet from
MarkdownBlockContext, register every implementation through
block_renderer, and build free-function chains with
Renderers::new().renderer(&first).renderer(&second). HTML fences now receive
the host's active stylesheet instead of synthesizing theme defaults. -
Breaking:
LayoutStyle::gapis split intorow_gapandcolumn_gap
(the.gap(...)builder still sets both), andItem::dimensionis replaced
byItem::style. UseItem::newfor the compatible compact path or
Item::styled(FlexItemStyle, ...)for independent flex properties. -
Breaking: keymap character specs are now explicitly logical text. Write
the character produced by the active layout (A,?,ctrl+R) rather than
shift+aorshift+/;Shiftremains valid for non-character chords such as
shift+enter. Ambiguousshift+characterspecs now returnKeyParseError
(or panic through staticLayer::bind) instead of silently discarding Shift. -
Breaking:
AsyncRunnerupdate closures now returnUpdateResultinstead
ofControlFlow<()>, matching synchronousRunner; clean ticks and events no
longer rebuild or repaint the view. -
Breaking: interactive component handlers now return one root/prelude
InputOutcome(Ignored,Consumed,Changed,Submitted, orCancelled).
Read the submitted value back from its host-owned state. Replace
SelectOutcome,MultiSelectOutcome,FormOutcome,TabSelectOutcome,
TextInputEvent, and directEventFlowmatches withInputOutcome; call
.flow()when only propagation matters.SelectState,MultiSelectState,
andScrollStatenow makeDefaultidentical tonew(); use
SelectState::unselected()for an initially cursorless list. -
Breaking:
StyleSheetadds typed toast, diff, and key-hint fields. Toasts,
diffs,KeyHints, andKeymapHelpnow derive their defaults from those roles
instead of renderer literals; explicitDiff::stylecolors still win for one
instance. ExhaustiveStyleSheetliterals must add the new fields or use
..StyleSheet::from_theme(&theme). ConstructRenderCtxthrough
RenderCtx::newrather than a struct literal now that it carries the optional
resolver. -
Breaking:
View::measurenow takes&RenderCtx, and every composition
container forwards the frame's active theme, stylesheet, and focus state.
Migratefn measure(&self, available: Size)implementations to
fn measure(&self, available: Size, ctx: &RenderCtx); callers likewise pass
the context used for rendering.Flex::solvenow takes that context, and
ItemScroll::{measure_height, measure_views}take it as their final argument. -
Markdownand the companiontuika-html::Htmlview now measure with the same
theme and stylesheet they render with.Boxedimplements stylesheet panel
padding as real layout; an explicit.padding(...)wins over the stylesheet. -
element,view!, and composition containers now preserve borrowed child
views at any depth. Existing owned trees continue to useElement; borrowed
trees use the same builders and are bounded to their frame lifetime. -
Flexmeasures padded children against their actual inner box and reports
fixed/percent child dimensions when the container itself is auto-sized, so
nested measurement matches the rects assigned during rendering. -
`TextInputSt...
Release v0.6.0
Released alongside tuika-codeformatters 0.3.1 and tuika-mermaid 0.1.1,
which update their tuika dependency requirement for 0.6 compatibility.
Highlights
Split-footer terminal mode. Hosts can keep a live footer pinned to the bottom
of the terminal while completed content moves into native scrollback, then return
every reserved row cleanly on exit.
Borrowed scene roots. ScopedScene renders and dispatches events through a
borrowed View, so hosts can keep application state outside the scene without
requiring 'static ownership.
Added
- Borrowed scene roots.
ScopedScene<'_, V>borrows a concreteViewfor
one frame while owning the same orderedSceneOverlay/Dialogstack as
Scene. Hosts can paint large live models directly without cloning them into
a'staticElement; rendering, backdrop, placement, and focus-owner
semantics are shared with owned scenes. - Screen modes.
ScreenModepicks which part of the terminal a frame owns:
Alternate(the previous, still-default behavior) orsplit_footer(rows),
which reserves rows at the bottom of the main screen and leaves everything
above as the terminal's own scrollback — the shell prompt, the wheel, mouse
selection, and the output the app publishes, which survives its exit.
RunnerConfig::screen_modedrives both runners; a host with its own loop
composesTerminalSession::enter_with,screen::pin_footer, and
screen::close_footer. - Publishing above a footer.
Runner::scrollback()/
AsyncRunner::scrollback()return a cloneable,Send + SyncScrollback
queue of views the loop commits above the footer;screen::publish_block
commits one view straight from a host's own loop, with noSendbound. - New
split_footerexample, andcargo run --example codex -- --split-footer
runs the whole coding-agent UI in the mode. - New
scrolling-regionsfeature. It is a compatibility mirror of ratatui's
(Cargo unifies features one way, andHyperlinkBackendmust still implement
Backend), not an optimization: rows scrolled out of a DECSTBM region are
discarded by the terminal instead of entering its scrollback.
Changed
-
Breaking:
RunnerConfiggains ascreen_modefield. Struct literals need
a default update:Before:
RunnerConfig { tick_rate }
After:
RunnerConfig { tick_rate, ..RunnerConfig::default() }
-
ScreenModeandScrollbackjoin the crate root and the prelude.
Release 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 seam |
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 a
Color::IndexedANSI slot, so the terminal resolves the palette. No query, no
timeout, no failure mode. - New:
tuika::term::palette—TerminalPalettewithparse/query, plus
QUERY_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 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, 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 seam, 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 seams a composer needs.
- New:
components::ItemScroll— item-granular scrolling withwindowed
construction,gap,scrollbar, andmeasure_height. - New:
components::textinput::{Trigger, TriggerAnchor, Token, TextSpan},
plusTextInputState::{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}
- 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 seam, 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 the
Highlighterseam; 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 into
doc_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.TerminalSession
now enables and restores enhanced keyboard reporting, so the chord arrives at
TextInputStatedistinctly 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 noParagraphof 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.
`Mark...











