Resolve the catalog slug from both the current-locale display name and the
English canonical name. A marker stores the catalog name as it read at
creation time, so one minted under the English default and then viewed in
another locale never matched a map built only from the current locale's
names — its rich per-biomarker description silently fell back to the generic
line. The English bundle is always in the client chunk, so adding its names
makes the lookup independent of the locale the marker was created in.
Tidy the header control cluster:
- Drop the standalone "adjust target range" control and its sheet; the
reference bounds are already editable inside the full marker editor behind
the pencil. Remove the now-dead handler, mutation, and `targetRange.*`
strings across all locales.
- Give the show-all-values, edit, and delete controls one shared ghost-icon
treatment instead of the show-all control's separate muted variant.
- Add hover tooltips to edit and delete, matching the other icon controls.
- Order the controls left → right: delete, edit, show all values, add value.
Code-review and design follow-ups before the v1.22 cut. No behaviour change
beyond the items below.
Coach / insights:
- Coach SSE route: a non-provider error raised inside the open producer (e.g. a
DB failure persisting the reply) now records on the wide event and forwards to
GlitchTip instead of masquerading as a provider outage; it still surfaces an
error frame since the stream is open.
- Lab ↔ outcome correlations: hedge faint-tier pairs ("a faint hint, if
anything … too small to lean on") so the narrated confidence never outruns the
effect, matching the daily engine.
- Reminder sweep: mint the plan-review reminder and clear the plan's reviewDate
in one transaction so a half-applied write can't duplicate the reminder.
- Fold the two raw provider-fallback runners onto one shared chain walker.
Design / accessibility:
- Lift sub-44px tap targets to the 44px mobile floor (suggested-action card,
Coach bubble affordances), brighten the resting timestamp clock for non-text
contrast, and route the lab chart height through the shared constant.
- Replace ad-hoc arbitrary type sizes with the scale token and static size-11
icon buttons with the named icon-lg variant.
- Settings: add the missing About card-header descriptions, normalise the
low-stock numeric widths, and relax the notification-status rows from nested
cards to a divided list.
Housekeeping:
- Drop three unused exported types (and the now-orphaned source const), re-stamp
v1.21.5 comments to v1.22, and register the live-judge eval harness as a knip
entry so the script-only module resolves.
One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
Bring interactive elements that fell below the 44px iOS/WCAG touch
floor up to min-h-11 on phones (relaxing to the denser sm density on
larger screens), and add visible focus rings where they were missing:
- TimesOfDayChips remove button + API-token copy buttons gain an
inset hit-area so the visual density stays tight while the tap
target clears the floor.
- Dose-history row-actions menu trigger and the insights suggested-
prompt chips raise to min-h-11 sm:min-h-9.
- Sortable table headers and the glucose advanced disclosure toggle
gain a focus-visible ring and a phone tap floor.
Hidden states of the floating coach button now leave the tab order and
accessibility tree, its focus ring carries an offset, its icon is dark
on the gradient, an unread nudge is announced once via a live region,
and it sits correctly above the fold on tablet widths and yields to
the onboarding tour. Chart error cards announce themselves, share the
exact height variables of the skeleton band, carry per-chart retry
labels, and a failed chunk renders inside the regular card shell. The
header icon cluster spaces its extended hit areas apart, the tab strip
keeps horizontal paint room for focus rings, the streak row reserves
its slot in every card state, the selection bar clears the bottom nav
on landscape phones, and the version poller keeps an in-memory reload
guard for browsers that block session storage. Mood notes render as
plain paragraphs with a toggle that appears only when text is actually
clamped, the remember control reveals on hover and hands focus to its
confirmation, and the hypnogram names its source and paints only the
lanes the night contains.
A round coach button sits fixed bottom-right on every authenticated
page, above the bottom nav and safe-area inset, with a small dot when a
nudge is unread — replacing the bubble that only existed while a nudge
was pending. The per-chart target-adjust link is gone (the page-level
control remains), the duplicated customize entry on insights sub-pages
collapses onto the tab strip, and the full-width show-all-values foot
button becomes an icon beside the target control. The tab strip's
scroll container keeps four pixels of paint room so pill borders and
focus rings stop clipping at fractional zoom, and the mood tag
breakdown bars adopt the muted opacity the other mood charts use.
Icons inside buttons drop their manual margins in favour of the
button's built-in gap (two hundred call sites), form field stacks settle
on one spacing step, card titles on exactly two sizes, inline field
errors on one text size, and a dozen hand-rolled loading pulses move
onto the shared skeleton primitive while keeping their silhouettes. The
intake dialog wording joins the rest of the app on 'übersprungen', and
the UI guidelines now document the two deliberate footer and
touch-target systems.
The score ring's bloom was clipped by the SVG's own viewport — the
drop-shadow sits on a child of the viewBox-filling arc — so the glow
never extended past the circle; the SVG now overflows visibly on every
host. The insight pill strip gains chevron buttons and wheel scrolling
so an overflowing row is reachable with a mouse. Safari favourites
rendered the transparent icon on a white plate; a flattened
apple-touch-icon and a maskable manifest icon fix both. The
time-of-day mood chart adopts the damped fill the weekday chart already
uses, eighteen delete confirmations move onto the destructive button
variant (one of them had the wrong text colour), five confirmation
dialogs gain pending states, the sources dialog stacks its primary
action on top on mobile, and the token form aligns input and button
heights.
The page-scoped selection bar sat sticky bottom-2, behind the fixed
64px mobile bottom-nav, so the destructive Delete (N) button was
unreachable on phones and ignored the iOS home-indicator inset. Lift
it to clear the nav band + safe area on mobile and drop back to
bottom-4 at sm+ where the nav is hidden.
The per-row select checkbox wrapped a Radix checkbox (a 16px button)
in a 44px label; a label does not forward taps to a button, so the
effective target failed WCAG 2.5.5. Make the 44px area a single
button that owns the toggle, with the checkbox a pointer-events-none
controlled visual, avoiding any double-toggle.
Bring the mood pagination buttons to the 44px target used by the
measurements list. Add (sort: Desc) to the two source-filter indexes
so the schema matches the DESC migration SQL; schema-fidelity only,
no new migration.
Extract the duplicated list chrome — SortableHead and the single-row
DeleteButton — into a shared src/components/data-list/ module and add a
page-scoped multi-select with a selection action bar, used by both the
measurements and mood management lists.
Filter bar: each list keeps its primary Select (type / mood level) and
gains a source Select (measurements sends sourceEq, mood sends source)
plus an optional date range wired to the existing from/to params. Every
filter resets pagination to page 1 and is baked into the query key via
the centralised factory (queryKeys.measurementsList / moodEntriesList)
so the cache stays correct per filter combination.
Multi-select is scoped to the current page: a leading checkbox column on
desktop, a checkbox on the mobile cards, and a select-all-on-page header
checkbox. When at least one row is selected an action bar shows the count,
a clear affordance, and a destructive Delete (N) behind an AlertDialog
confirm that POSTs to the bulk-delete route, then invalidates the same
dependent-key bundle the single delete busts and clears the selection.
Selection clears on any page / filter / sort change; the selection math
lives in a pure, unit-tested helper. The locked-type insights subpage
keeps its focused single-metric view (source filter + date range
suppressed).
New strings land in a dataList namespace plus per-list source-filter and
bulk-delete keys, translated across all six locales.