Visualizations is a page, not an expanding card (+ open for more UAT) - #29
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
screen = "viz"+shell/VizScreen.svelte, rendered by Shell and folded into thedestderivation so the Study tab stays lit one layer down.MapOverlay, which already carries its ownBackHandler, so system back closes the page rather than the hub.ExploreCard'sindentparameter 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 → mapunchanged, since that is still two clicks); AndroidassembleDebug+ 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