Skip to content

Settings: stop the content column shifting off-screen; keep segmented pickers#9

Closed
digitalerdude wants to merge 9 commits into
mainfrom
settings-layout-fix
Closed

Settings: stop the content column shifting off-screen; keep segmented pickers#9
digitalerdude wants to merge 9 commits into
mainfrom
settings-layout-fix

Conversation

@digitalerdude

Copy link
Copy Markdown
Owner

Summary

  • Addresses Settings layout fix ryanbr/noop#153 and IU oversized in settings ryanbr/noop#161.
  • Bug: the Settings content column was shifted ~37pt left off-screen (clipping the header's first letter and the cards' left/right margins) — reported externally in IU oversized in settings ryanbr/noop#161 as "UI appears oversized in settings," which matches the same root cause self-tracked in Settings layout fix ryanbr/noop#153.
  • Root cause: six segmented Pickers in the Units/Appearance cards used .fixedSize(), pinning them to their ideal width. Inside FormRow's label(∞)+control HStack, that pushed the column past the screen width (~476pt vs 402pt), and ScreenScaffold's centering shifted everything left — the exact overflow the #43 note in SettingsView.swift already warned about, just not caught for these six.
  • Fix: removed the .fixedSize() calls so the pickers size to the row. To keep the two three-option pickers from truncating once they size down, shortened their long first labels to "Auto" (Temperature's "Match" and the theme's "System," the latter shared with the onboarding pill) and added translations for the new short labels.
  • Also fixes the profile-photo blurb, which read "Nur auf diese iPhone gespeichert" in German (bad grammar) because interpolating the nested-localized Platform.deviceNounPhrase broke wrapping — switched to Platform.deviceNoun with "this"/"diesem" written directly in the sentence.
  • The branch was cut before 8.5.1/8.5.2, so it's merged with upstream/main to resolve the resulting conflicts. That merge brought in the same .fixedSize() overflow bug in two more places (the HRV settings in one place (deep sleep window now is in units) ryanbr/noop#155 HRV-window grouping and the new Trend-charts picker) — removed there too, and shortened "Whole night" → "Night" (German "Über Nacht" still truncated at 10 chars; single-word "Nacht" fits reliably). Also added missing translations for "HRV window" (→ "HRV-Fenster") and its option labels, which had none before.

Test plan

  • macOS (Strand) and iOS (NOOPiOS) targets both build clean via xcodebuild.
  • Verified end-to-end in the Simulator in an earlier pass on this branch (see the Settings layout fix ryanbr/noop#153 write-up for the before/after).
  • Confirmed no segmented Picker in SettingsView.swift still carries .fixedSize() (grepped every pickerStyle(.segmented) call site).
  • Confirmed "Auto" has translations across all locales in both the app and StrandDesign string catalogs.

🤖 Generated with Claude Code

digitalerdude and others added 4 commits July 9, 2026 07:32


Two stdlib tools in Tools/linux-capture that turn a WHOOP data export into
leverage for the un-decoded deep-record layouts tracked in ryanbr#103:

- hci_extract.py: parse a phone Bluetooth HCI capture (iOS .pklg /
  Android btsnoop) of the official app, reassemble L2CAP/ATT, and emit the
  CRC-valid WHOOP frames as the project's capture.json. Only WHOOP streams
  reach the output.
- correlate_ground_truth.py: known-plaintext field search. Cross-reference
  capture records against the official per-night values in a WHOOP CSV export
  (reusing the Swift importer's localized header aliases) to locate each
  biometric's byte offset + encoding. Distribution-overlap scoring (recall +
  precision) rejects constants and coincidences. Prints offsets only, so the
  output is safe to post on ryanbr#103 while health data stays local.

Docs: new README sections + a ground-truth-correlation note in
WHOOP5_DEEP_DATA.md. 25 new stdlib tests; full suite 129 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ReadinessChecklistCard.lead expects LocalizedStringKey?, but
fitnessReadyLead() returns an already-resolved String. The mismatch
broke the NOOPiOS build; wrap it explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… pickers

The six segmented pickers in the Units and Appearance cards used
.fixedSize(), pinning them to their ideal width. Inside FormRow's
label+control HStack that pushed the column past the screen width, so
ScreenScaffold's centering shifted the whole Settings column ~37pt left
— clipping the header's first letter and the cards' left/right margins
(the overflow the ryanbr#43 note in this file already warns about).

Remove the .fixedSize() calls so the pickers size to the row. To keep
the two three-option pickers from truncating once they size down,
shorten their long first labels to "Auto": Temperature's "Match" and
the theme's "System" (AppearanceMode.system, shared with the onboarding
pill). Add "Auto" translations to both string catalogs.

Also fixes the profile-photo blurb: interpolating the nested-localized
Platform.deviceNounPhrase broke its wrapping and read "diese iPhone" in
German; use Platform.deviceNoun with "this" in the sentence so it wraps
and reads "diesem iPhone".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	Strand/Screens/HealthView.swift
…e-checker

The single-line nested-optional-coalescing form (gx ?? 0) * (gx ?? 0) + ...
occasionally blew the Swift type-checker's time budget in CI ("unable to
type-check this expression in reasonable time"), failing the WhoopProtocol
test job on unrelated PRs. A compiler perf cliff, not a real ambiguity;
naming the intermediate values fixes it without changing behavior.
digitalerdude and others added 4 commits July 9, 2026 21:19
…screen (ryanbr#161)

The Units/Appearance segmented pickers used .segmented + .fixedSize(), which pins them
to their ideal width and OVERFLOWS the Settings column once the labels are long or Text
Size is enlarged -- SwiftUI then sizes the whole column to that overflowing row, so the
Settings screen renders oversized/shifted (the ryanbr#161 report on iOS 26.5).

This is the exact failure mode already documented + fixed for the Age/Sex pickers under
ryanbr#43, which switched to .pickerStyle(.menu) (a compact button that fits any label length).
Apply the same proven fix to the remaining eight: Measurement, Temperature, Effort scale,
Theme, Chart colours, Trend charts, App icon, and the HRV window.

The HRV window picker (added in 8.5.1, moved in 8.5.2) followed the old .fixedSize()
pattern with unusually wide options ("Whole night"/"Deep sleep"), so it likely worsened
this in 8.5.2 -- now fixed too. NB: removing .fixedSize() alone (the obvious fix) makes a
segmented picker COLLAPSE per the ryanbr#43 note; .menu is the correct remedy.

iOS-only (Android uses SegmentedPillControl, unaffected). Layout change -- verify on device.
…w-menu

Settings: use .menu pickers so segmented controls can't oversize the screen (ryanbr#161)
# Conflicts:
#	Strand/Screens/SettingsView.swift
@digitalerdude

Copy link
Copy Markdown
Owner Author

Closing this fork-internal PR to declutter — the real submission lives at ryanbr/noop (upstream). See that repo's PR list for the current, actionable version of this work.

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.

2 participants