The design's Slack/Linear/Gmail rows show numeric badges (12, 3) that
real apps publish in their tab title prefix — "(12) Slack | …",
"(3) Inbox — Linear", "(99+) Gmail". Add a pure parser
ely_domain::parse_title_unread_count that recognises the leading
"(N)" pattern and surfaces it through BrowserTab::unread_count(); the
sidebar launcher row renders a glass pill badge whenever the count
is non-zero, capped at "99+" for very high counts.
No domain field, no fabrication: the badge appears only when a real
website publishes its own unread count via the title. Tests cover
canonical formats, non-prefixed titles, leading whitespace, and
non-numeric / overflow inputs.
- AppearanceSettings derives Default instead of carrying a manual impl
that's identical to the derived one.
- topbar::render_lock_or_search collapses the duplicated Search arms
into a single fallback so clippy stops flagging identical blocks.
- command_overlay row helpers bundle id/title/hint/keys into a small
CommandRowContent struct so render_row, render_row_with_glyph, and
render_row_inner stay under the 7-arg threshold without losing any
call-site clarity.
Introduces a small persistent appearance contract: WallpaperTheme
(Dawn/Violet/Mint/Slate), ThemeMode (System/Light/Dark), reduce_motion.
Defaults to Dawn + System + motion-on. Field accessors and setters live
on the struct so the core can mutate without exposing internals; serde
support uses kebab-case so the on-disk form matches the design CSS
naming.
Tests cover defaults, mutation independence, and a JSON round-trip with
the kebab-case names. serde_json is added as a dev-dependency only —
the production crate does not depend on it.