viewer: color schemes, twenty families and every variant, with a light/dark filter

Owner, 2026-10-19: full color-scheme support for the viewer, the top
Neovim-style schemes, about twenty families each with a flyout of all its
variants, a light/dark/all filter, Ayu included, alphabetical, a whole row
to click.

- The sidebar's corner button (the light/dark toggle before) opens the
  menu: the filter, then, alphabetically and all on screen at once (in
  columns in a short window), Ayu, Catppuccin, Cyberdream, Dracula,
  Everforest, GitHub, Gruvbox, Kanagawa, Material, Melange, Monokai Pro,
  Night Owl, Nightfox, Nord, One Dark, Oxocarbon, Rosé Pine, Solarized,
  Sonokai, Tokyo Night and Wanderbooks (the viewer's own Light and Dark):
  80 schemes, each variant a row with a strip of its colors.
- Colors from each scheme's official source, checked value by value
  (docs/color-schemes.md; the table names the sources).
- Every palette, the viewer's own included, keeps every text color
  (figures, gains and losses, warnings, links, secondary and faint text)
  at WCAG AA (4.5:1) on every ground it sits on, soft fills included; a
  color that falls short keeps its hue and only changes lightness. Text,
  secondary and faint text stay apart (each 1.2 times the next), gains,
  losses and text and the charts' series shown together stay plainly
  different (CIE76 ΔE 20).
- The scheme sets both egui styles and pins its mode, so the system
  turning light or dark changes nothing (it made figures unreadable).
- Preferences now live in the platform's place for app settings
  ($XDG_CONFIG_HOME/wanderbooks-viewer on Linux), not eframe's default,
  which was $XDG_DATA_HOME/wanderbooks: the data root's own place. The
  old file is moved once.
- Tests: every page of thirteen painted in all 80 schemes, the same words
  in the same places, each text readable on what was painted under it;
  the menu's order, reach and whole-row clicks in a tall and a short
  window; the system flip; preferences restored. Two review rounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-19 12:51:58 -06:00
1 parent 13b09aa766
commit af684c3612
13 files changed
+3700 -87

No files matched your search

+6 -19
View File
@@ -97,10 +97,10 @@ fn change(
prev: Decimal,
up_is_good: bool,
what: &str,
p: &theme::Palette,
) -> Option<(String, egui::Color32)> {
let pct = fmt::pct_change(prev, now)?;
let good = (pct >= 0.0) == up_is_good;
let p = &theme::LIGHT;
let arrow = if pct >= 0.0 { "▲" } else { "▼" };
Some((
format!("{arrow} {:.0}% vs {what}", pct.abs()),
@@ -153,8 +153,7 @@ fn kpis(ui: &mut Ui, b: &Books, shell: &mut Shell<'_>) {
Kpi {
label: "Cash on hand",
value: fmt::dollars(k.cash),
sub: change(k.cash, k.cash_30d_ago, true, "30 days ago")
.map(|(t, c)| (t, color_for(c, p))),
sub: change(k.cash, k.cash_30d_ago, true, "30 days ago", &p),
spark: Some(&cash_spark),
how: "The balance of every cash account (bank, savings, cash on hand) at the end of today.",
},
@@ -179,8 +178,8 @@ fn kpis(ui: &mut Ui, b: &Books, shell: &mut Shell<'_>) {
k.expenses_prev_month,
false,
"same days last month",
)
.map(|(t, c)| (t, color_for(c, p))),
&p,
),
spark: None,
how: "Expenses dated this month (accrual basis: when incurred, not when paid).",
},
@@ -195,8 +194,8 @@ fn kpis(ui: &mut Ui, b: &Books, shell: &mut Shell<'_>) {
k.revenue_prev_month,
true,
"same days last month",
)
.map(|(t, c)| (t, color_for(c, p))),
&p,
),
spark: None,
how: "Revenue dated this month.",
},
@@ -241,18 +240,6 @@ fn kpis(ui: &mut Ui, b: &Books, shell: &mut Shell<'_>) {
});
}
/// `change` picks from the light palette; map to the current one.
fn color_for(c: egui::Color32, p: &theme::Palette) -> egui::Color32 {
let l = &theme::LIGHT;
if c == l.positive {
p.positive
} else if c == l.negative {
p.negative
} else {
p.muted
}
}
/// "Needs attention", full width: a summary of how many problems, to-dos,
/// and notes there are, then each item as a row (its severity, the title
/// that opens where to look, what's wrong, and the prompt for the agent at
+1 -1
View File
@@ -530,7 +530,7 @@ pub fn show_list(ui: &mut Ui, b: &Books, shell: &mut Shell<'_>) {
};
let (out_t, out_c) = money(flow.paid, p.text);
let (in_t, in_c) = money(flow.received, p.positive);
let (net_t, net_c) = paid_cell(flow.net(), p);
let (net_t, net_c) = paid_cell(flow.net(), &p);
let resp = row(
ui,
w,