Skip to content

Release v0.4.2

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Sep 08:50

Release Notes v0.4.2

Released: 2026-09-02

A correctness release for the theme contract, the Catppuccin family, and the terminal theme selector. Three tokens that every builtin already shipped are now first-class constants, Catppuccin's background layering matches upstream, and a docs audit removed a cluster of stale API claims while adding the missing iced guide.

🌟 Highlights

bg.elevated, bg.active, and cursor_line are part of the contract

All 39 builtin themes already defined these, but opaline::names had no constants for them and the contract tests did not cover them, so consumers had to use raw strings with no guarantee the tokens existed. tokens::BG_ELEVATED, tokens::BG_ACTIVE, and styles::CURSOR_LINE now exist, and the contract grows to 28 tokens and 14 styles. The contract test lists are rewritten in terms of the names constants with a size assertion, so the code and the documented numbers can no longer drift. Reported in issue #2.

🐛 Catppuccin backgrounds match upstream

Mocha, Macchiato, and Frappé mapped bg.base to crust and bg.panel to base, so an app painting its canvas with bg.base rendered #11111b in Mocha instead of the familiar #1e1e2e. All three now use bg.base = base, bg.panel = mantle, bg.code = crust, aligning them with Latte and with upstream Catppuccin. The surface ladder (highlight, elevated, active, selection) is unchanged, and a fidelity test pins the mapping for all four flavors.

🐛 Theme selector key handling and scrolled headers

ThemeSelectorState::handle_key now ignores KeyEventKind::Release, so terminals that report key-up (Windows, the kitty protocol) no longer fire every action twice. Ctrl/Alt/Super chords such as Ctrl+C return Noop instead of landing in the filter string, and pressing Enter as the very first key applies the preview so the global theme matches the id handed back. Section headings are computed from visible rows only, so the first row of a scrolled window carries its own section heading, and a heading with no room for an item beneath it is no longer drawn.

🐛 Non-ASCII hex input no longer panics

OpalineColor::from_hex sliced by byte index, so a multibyte character in the input could panic at a char boundary. Non-ASCII input now returns ColorParseError::InvalidHex.

🐛 Fixes

  • owo-colors: rapid_blink maps to blink_fast() (SGR 6) instead of folding into blink(), so the two blink modifiers are distinguishable in output.
  • egui: Stroke::new width arguments are pinned to f32 literals across to_egui_visuals, avoiding inference breakage from float-literal ambiguity.
  • Showcase example: the samples panel in examples/theme_showcase.rs now renders only contract style names (line_number, cursor_line, selected, active_selected, focused_border, unfocused_border, …), so every row shows real colors in every builtin theme. The theme count comes from builtins::BUILTIN_COUNT instead of a hardcoded 20.

✅ Testing

  • Feature-dependent integration tests are gated on their features. adapter_tests, gradient_tests, and builtins_tests gate at the crate level, and individual gradient tests in loader_tests, resolver_tests, and builtins_tests are gated inline. cargo test previously only built under the default or full feature set even though the library compiled under every combination; verified with --no-default-features, builtin-themes only, gradients only, ratatui only, and --all-features.

📝 Documentation

A docs audit against the current source fixed a batch of drift:

  • Added docs/guide/iced.md plus its sidebar entry in docs/.vitepress/config.ts; the adapter was advertised in the README and feature reference with no guide page.
  • The crate-level feature table in src/lib.rs listed 6 of 13 features and omitted iced and the colored adapter; it now documents crossterm, owo-colors, css, syntect, egui, iced, and widgets.
  • Fixed the README CI badge, which pointed at ci.yml instead of cicd.yml.
  • Corrected non-compiling snippets in the ratatui guide (Stylize vs Styled, CircularReference patterns missing the token field).
  • The CSS guide showed Catppuccin hex values and a diff_added class that Theme::default() (SilkCircuit Neon) does not have; it now shows real generator output. generate_css_classes documents that reversed, slow_blink, and rapid_blink are skipped.
  • The egui guide no longer claims text.primary drives override_text_color or that every color property is overridden; text cursor, shadow, and text-edit colors keep egui's defaults.
  • Missing-token fallbacks are documented as neutral gray, not magenta.
  • Clarified that widgets also enables ratatui, that name-based global loading needs builtin-themes, and that the loader does not expand ~. Added the three missing syntect settings fields and ThemeNotFound to the error table.
  • The bg.* family is now documented as a layering ladder: bg.base is the canvas, bg.panel the secondary pane, and the rest step toward the text color.
  • Dropped the hardcoded test count from the dev command tables.

🔨 Maintenance

  • deny.toml acknowledges RUSTSEC-2026-0192 (ttf-parser unmaintained), reachable only with the egui feature via egui 0.33 → epaint → ab_glyph → owned_ttf_parser. The ignore can drop once egui ships a release without it.

Upgrade Notes

No API breakage. Two behavior changes are worth checking:

  • If your app hardcoded assumptions about Catppuccin's bg.base/bg.panel/bg.code mapping, re-check your canvas and panel colors against the new values.
  • Replace raw "bg.elevated", "bg.active", and "cursor_line" strings with tokens::BG_ELEVATED, tokens::BG_ACTIVE, and styles::CURSOR_LINE.
  • Custom theme authors: contract tests now enforce 28 tokens, 14 styles, and 5 gradients. Run cargo test --all-features after adding a theme.
  • Consumers relying on rapid_blink rendering as a plain blink through the owo-colors adapter will now see SGR 6.