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_blinkmaps toblink_fast()(SGR 6) instead of folding intoblink(), so the two blink modifiers are distinguishable in output. - egui:
Stroke::newwidth arguments are pinned tof32literals acrossto_egui_visuals, avoiding inference breakage from float-literal ambiguity. - Showcase example: the samples panel in
examples/theme_showcase.rsnow 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 frombuiltins::BUILTIN_COUNTinstead of a hardcoded20.
✅ Testing
- Feature-dependent integration tests are gated on their features.
adapter_tests,gradient_tests, andbuiltins_testsgate at the crate level, and individual gradient tests inloader_tests,resolver_tests, andbuiltins_testsare gated inline.cargo testpreviously only built under the default or full feature set even though the library compiled under every combination; verified with--no-default-features,builtin-themesonly,gradientsonly,ratatuionly, and--all-features.
📝 Documentation
A docs audit against the current source fixed a batch of drift:
- Added
docs/guide/iced.mdplus its sidebar entry indocs/.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.rslisted 6 of 13 features and omittedicedand thecoloredadapter; it now documentscrossterm,owo-colors,css,syntect,egui,iced, andwidgets. - Fixed the README CI badge, which pointed at
ci.ymlinstead ofcicd.yml. - Corrected non-compiling snippets in the ratatui guide (
StylizevsStyled,CircularReferencepatterns missing thetokenfield). - The CSS guide showed Catppuccin hex values and a
diff_addedclass thatTheme::default()(SilkCircuit Neon) does not have; it now shows real generator output.generate_css_classesdocuments thatreversed,slow_blink, andrapid_blinkare skipped. - The egui guide no longer claims
text.primarydrivesoverride_text_coloror 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
widgetsalso enablesratatui, that name-based global loading needsbuiltin-themes, and that the loader does not expand~. Added the three missing syntect settings fields andThemeNotFoundto the error table. - The
bg.*family is now documented as a layering ladder:bg.baseis the canvas,bg.panelthe secondary pane, and the rest step toward the text color. - Dropped the hardcoded test count from the dev command tables.
🔨 Maintenance
deny.tomlacknowledgesRUSTSEC-2026-0192(ttf-parser unmaintained), reachable only with theeguifeature 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.codemapping, re-check your canvas and panel colors against the new values. - Replace raw
"bg.elevated","bg.active", and"cursor_line"strings withtokens::BG_ELEVATED,tokens::BG_ACTIVE, andstyles::CURSOR_LINE. - Custom theme authors: contract tests now enforce 28 tokens, 14 styles, and 5 gradients. Run
cargo test --all-featuresafter adding a theme. - Consumers relying on
rapid_blinkrendering as a plain blink through the owo-colors adapter will now see SGR 6.