Skip to content

Visualizations is a page, not an expanding card (+ open for more UAT) - #29

Merged
glendonjklassen merged 5 commits into
mainfrom
viz-menu-page
Aug 13, 2026
Merged

Visualizations is a page, not an expanding card (+ open for more UAT)#29
glendonjklassen merged 5 commits into
mainfrom
viz-menu-page

Conversation

@glendonjklassen

Copy link
Copy Markdown
Owner

Open branch for this round of UAT feedback — I'll keep pushing fixes here as you find things, so hold off merging until you're done looking. Targets main (v0.49.0 is out).

Visualizations

The maps were sub-items of a card that expanded in place. That tree was a mechanism nothing else in the app has: everywhere else a destination replaces what came before, and Plans and Memorize are both pages one layer under the Study hub. Visualizations is one too now — its own bar, its own back arrow, returning to the hub rather than to the reader.

Being a page also buys what the expansion couldn't: each map gets its full sentence at the same size as every other tool, instead of an indented line competing with the card that spawned it.

  • Webscreen = "viz" + shell/VizScreen.svelte, rendered by Shell and folded into the dest derivation so the Study tab stays lit one layer down.
  • Android — a second MapOverlay, which already carries its own BackHandler, so system back closes the page rather than the hub.
  • ExploreCard's indent parameter went with the tree; nothing calls it now.

No i18n and no engine change — the page renders the same explore.* keys the sub-cards did, so nothing needed translating and the wasm is untouched.

Verification

The new e2e pins what an inline expansion could not satisfy: the hub's own cards are gone while the page is up, and ‹ returns to Study. Mutation-tested — making the card open a map directly fails it. Playwright green (hub + maps, and the existing maps tests route Study → Visualizations → map unchanged, since that is still two clicks); Android assembleDebug + JVM unit tests green.

One thing I did NOT change

The Weave map's description reads "A visualization of weaves across the Bible" — which, on a page called Visualizations, says almost nothing. It's pre-existing copy and yours to word; tell me what it should say and I'll change it here.

🤖 Generated with Claude Code

The maps were sub-items of a card that grew a branch when tapped. That tree was
a mechanism the rest of the app does not have: everywhere else a destination
REPLACES what came before, and Plans and Memorize are both pages one layer
under the Study hub. So Visualizations is one too — its own bar, its own back
arrow, returning to the hub rather than to the reader.

Being a page also buys what the expansion could not: each map gets its full
sentence at the same size as every other tool, instead of an indented line
competing with the card that spawned it.

Web: `screen = "viz"` and shell/VizScreen.svelte, rendered by Shell and folded
into the `dest` derivation so the Study tab stays lit one layer down. Android:
a second MapOverlay, which already carries its own BackHandler, so system back
closes the page rather than the hub. ExploreCard's `indent` parameter went with
the tree — nothing calls it now.

No i18n and no engine change: the page renders the same `explore.*` keys the
sub-cards did, so nothing needed translating and the wasm is untouched.

The e2e pins what an inline expansion could not satisfy — the hub's own cards
are GONE while the page is up, and ‹ comes back to Study — and a mutation that
makes the card open a map directly fails it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
TWO FIXES, both from UAT.

1. THE BAND SHOWED ONE PLAN. It read `running[0]`, which is wrong four ways at
   once, and only ever visible to someone running more than one schedule (every
   test started exactly one, which is why it shipped):

   - only the FIRST plan appeared, with no sign the others existed — while the
     nav-strip chip two screens away correctly said "+2 more";
   - `running` holds CONCEPT STUDIES too. They are not schedules, have no day,
     and are not builtins, so one sorting first would have put its raw id on
     screen as though it were a plan's name;
   - a plan already finished for the day kept advertising today's chapters
     after the chip had correctly retired;
   - a paused first plan hid an active second one behind its "paused" badge.

   All four are rules `planToday.ts` already encodes for the chip and the
   navigator's today card. The band now goes through the same `todayPlans`,
   which is the entire reason that module exists — reaching past it into the
   wire was the mistake. Each row also narrows to `remaining`, so finishing
   Genesis 1 of a Gen 1–4 day moves the row to "Gen 2–4" instead of standing
   still all evening.

   Three plans can genuinely run at once (whole-Bible, NT and devotional are
   different classes; starting one replaces only its own class), so the e2e
   starts all three and asserts three rows with three different books. The
   mutation that slices the list back to one kills exactly that test.

   A paused plan is now ABSENT from the band rather than shown greyed — the
   band answers "what needs reading", and naming chapters it is not asking for
   reads as asking for them. That inverts an assertion I wrote yesterday, so
   the test says why.

2. THE WEAVE MAP'S DESCRIPTION said "A visualization of weaves across the
   Bible", which on a page titled Visualizations says nothing at all. It now
   says what it DRAWS and what a tap does, the shape its sibling already uses:
   an arc between every pair of books your weaves connect, heavier where there
   are more links.

And a line for when every plan is done for today, so the band stops falling
through to "nothing on the go — start a reading plan", which is false and a
little insulting to someone who just finished.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…size

"A little small vertically… about 1/3 height and it looks a little lost"
(maintainer, on a Pixel). It is: the bar's height comes from its touch targets
— 48dp of icon button on Android, the 44px floor on the web — and the passage
sat at 16 inside it. The passage is what the bar is ABOUT and its widest tap
target, so it should not be the smallest thing in the row.

19 in both shells. This does NOT grow the bar: measured rather than assumed,
the web header is 65px at 16 and 65px at 19, so the ratio is what improves.

TWO THINGS FOUND WHILE LOOKING, both Android:

- its bar never followed the reader's text size at all. `studyScale` reaches
  the study panel, the search sheet and the memorize screens, but TopBar's
  labels were fixed sp — so a reader at 28 got chrome that ignored them, while
  the web's followed `--uiScale`. It takes the scale now.
- with the size raised, a long book name at a large scale could push the
  trailing icons off the row, and what runs off the end is the ≡ — the way to
  Settings. A Compose Row cannot wrap the way the web header does, so the
  phone's nav group now takes the row's spare width and the passage ellipsizes
  instead. The web already survives this by wrapping, and has a test for it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
TWO UAT fixes.

1. PHOSPHOR'S YELLOW HURT TO LOOK AT. It shipped with the pure amber #ffb000,
   relative luminance 0.523 against a paper of 0.005 — roughly a hundredfold
   step, in the role the reader sees MOST: every verse number, every link, the
   connectors, and the search band at alpha. That is glare, not a second
   phosphor. Same hue (38°), still saturated enough to read as amber rather
   than tan, 27% less light: #d99a2b.

   The divine name came down with it, #ffcf4d → #e0b552. At 0.664 it was
   brighter than the accent, so calming the accent alone would have left it as
   the one glaring thing on the page. tierGod and readUnread follow gold, which
   is the rule every other theme already keeps.

   Still far above the bar: the worst of the four surfaces is 7.24:1 against a
   4.5 floor, so the core's contrast test holds without being what decided the
   values — on a near-black ground AA is a floor you are nowhere near, and the
   thing that actually needed measuring was luminance.

2. THE PHONE SEARCH FIELD FILLED A THIRD OF THE BAR. `.spacer` is `flex: 1`
   and so is the open field, so the two split the free space evenly and a
   reader who tapped the glass got a box with empty bar beside it. The rule
   that was already there — hide the chapter nav while searching — could not
   fix this on its own, because the spacer simply absorbed whatever the nav
   gave up. Measured 36% before, 69% after; the rest is the ✕ and the ≡, which
   have to stay reachable.

   Android is unaffected: its search is a full-screen overlay, not an inline
   field, so there is nothing to share the row with.

The search test asserts the share and prints it on failure, so the mutation
that puts the spacer back reports "the field took 36% of the bar" — the defect
in its own words.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The release workflow gates the tag against Cargo.toml and apps/web/package.json
and fails the build if they disagree, so both move here rather than after the
tag is already pushed. Cargo.lock carries the workspace version too and CI
builds --locked, so it is refreshed in the same commit.

apps/web/package-lock.json was still on 0.48.0 — its own version field drifted a
release behind and nothing reads it, so nothing caught it. Brought in line while
the file was open.
@glendonjklassen
glendonjklassen merged commit 6cc4f28 into main Aug 13, 2026
8 checks passed
@glendonjklassen
glendonjklassen deleted the viz-menu-page branch August 13, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant