Repository navigation
feat(Broadway): propagate layout size classes - #376
Conversation
|
|
||
| /// The horizontal space available to a component. Unspecified preserves an unknown system value. | ||
| public enum BHorizontalSizeClass: Hashable, Sendable { | ||
| case unspecified |
There was a problem hiding this comment.
I don’t love including unspecified here; it means every check has to include this check. Any way to avoid it? Can we calculate our size class on our available space instead?
There was a problem hiding this comment.
Posted by an AI agent on kve's behalf.
Implemented in e698652. Both Broadway size-class enums now expose only .compact and .regular. UIKit .unspecified/unknown values and SwiftUI nil/unknown values normalize to .compact at the bridge; the default on both axes is also compact. Known system values, live observation, and scoped overrides remain intact. This gives styling code two-case switches without deriving system traits from measured bounds.
Rebased and pushed all five stack PRs. In #378, 6a04f77 preserves all nine UIKit input combinations in the pane-policy regression, including the unavailable-value fallback. Validation passed: 141 Broadway tests on this root PR, then 2,975 iOS unit tests, 28 host macro tests, and 31 architecture tests at stack tip 2fd6f699853ac4fa592a90d3928f83e836bca635. SwiftFormat passed. The fresh full CI gates, including snapshots, are running.
Register typed horizontal and vertical traits, map SwiftUI root inputs, and expose scoped stylesheet overrides. Verify repeated trait changes in a mounted host. Validation: BroadwayCoreTests (118 tests), BroadwayUITests (20 tests), and architecture checks passed on pinned Xcode 27.0.
9611da6 to
329a963
Compare
Expose only compact and regular on both axes. Normalize unspecified UIKit values and missing SwiftUI values at the bridge, and keep defaults, live observation, and scoped overrides consistent. Validation: 141 Broadway tests passed from clean artifacts; architecture checks and SwiftFormat passed. Shared-cache attempts failed on stale LifecycleKit deployment metadata; isolated build and execution passed.
> _Posted by an AI agent on kve's behalf._ ## Summary Add explicit frame layout traits and recorded Duo profiles to the Xcode 27.2 baseline from main. Apply requested content clearance after UIKit finalizes containment and native geometry. This layer retains the existing Where navigation. Its image references establish the new capture baseline before the adaptive app changes. Calendar month sections, day rows, and Timeline rows measure eagerly so full-content captures include the final rows; the selection and tabletop changes remain in the app PR. Eager Timeline measurement removes blank tails caused by lazy height estimates in a bottom-anchored scroll view. Settings waits for native search chrome to settle before measuring its full height. Existing scrolling onboarding and Devices fixtures use full-content frames. Theme choices also fall back to a vertical layout when narrow, and large-text Logged Days rows retain their dates, notes, and labels. ## Screenshots Developer Overlay, light iPhone full-content capture. Retaining the scene restores uniform native glass without changing the menu's styling, dimensions, or content. | Before | After | |---|---| | <img alt="Detached-scene menu with bright bands and a hard material cutoff" width="260" src="https://media.githubusercontent.com/media/kyleve/Stuff/9e759fc8b0ac8db6569bfb03d49cdafd54c7ad2c/Where/WhereUI/SnapshotTests/__Snapshots__/DeveloperOverlaySnapshotTests/developerOverlay.MenuTopLeading_iPhone.png"> | <img alt="Scene-backed menu with uniform glass and complete content" width="260" src="https://media.githubusercontent.com/media/kyleve/Stuff/f05ca62400c415631e21c5e649ce655700d9f18a/Where/WhereUI/SnapshotTests/__Snapshots__/DeveloperOverlaySnapshotTests/developerOverlay.MenuTopLeading_iPhone.png"> | Before: `9e759fc8`. After: `f05ca6240`; all six overlay captures reproduced byte for byte. ## Primary APIs - `SnapshotConfiguration.Frame.LayoutTraits` owns horizontal and vertical size classes and interface idiom. Configuration-level layout traits remain supported and take precedence over frame defaults. Phone and iPad presets carry truthful traits without changing their filenames. - `Frame.scenePolicy` controls capture scene retention without renaming references. Both configured assertions and hosted PNG export honor it. - `Frame.viewport` captures bounded shell composition. It must be paired with full-content captures of scrolling panes. - The four Duo profiles use measured native geometry. The module README retains the measurement date, runtime/build, collection method, and validation limits; the unused raw JSON dump is removed. `.narrowWindow` covers a synthetic multitasking container. ```swift let configuration = SnapshotConfiguration(device: .duoInnerPortrait) configuration.applyTraits(to: host) ``` Previews and UIKit capture use the same mapping. Conversion from configuration traits to frame traits uses exhaustive switches for both size classes and idiom, so new enum cases require an explicit mapping. Custom frames can supply `layoutTraits`; component frames inherit the host's traits. ## Capture and tooling boundaries All capture entry points acquire one lock before host discovery, validation, and preparation. Fixed, regular-width phone captures retain their scene so native bar geometry stays stable. Other synthetic captures detach the marked window by default, preserving compact title space and anchoring full-content search to expanded content. Capture and measurement hosts reconcile their effective insets after containment and layout, with another reconciliation after settling. The predicate uses content-controller traits, not pixel width or simulator identity. Frames can explicitly retain the scene when native chrome requires it. Periscope and Developer Overlay use this option without changing their configured traits, insets, or sizing. Developer Overlay's native glass otherwise develops bright bands and a hard material cutoff. Retention can expose physical reserved regions, so general presets keep the automatic policy. Both paths restore scene and presentation state on success or failure. A nil inset override retains native geometry. Only the captured root overrides its safe-area getter, during final pixel rendering. Native getters remain active during containment, measurement, and settling so inset changes propagate to children. Root pinning protects translated tiles. Overriding ancestors doubled asymmetric insets after measurement rehosting. Annotation padding has zero safe-area insets to avoid moving centered content. Tall tiled Year annotations retain the pre-existing limitation listed below. Regressions cover these boundaries, already-mounted inputs, compact and regular title readability, native bar stability, tiling, and failure cleanup. Full-content captures reset restored scroll offsets after readiness; viewport captures preserve them. Accessibility subjects stay mounted during readiness and settling. Ordinary captures retain their controller parent during parsing; only upstream tiling temporarily detaches it. Annotation wrappers grow only after the final accessibility parse. Synthetic viewports recover native navigation-bar metadata synchronously, restore window geometry before parsing, and restore the original getters on every exit. Pixel settling retains main’s normalized pixel comparison and retry policy. Final reference-comparison tolerances remain unchanged. Hosted resize, restoration, full-content, PNG export, and cancellation checks cover the shared pipeline. Snapshot shard discovery accepts literal multiline source arrays with shared support files. Unsupported declarations fail without borrowing a following target's sources. The existing shard assignments stay unchanged. ## Compatibility and review focus - The stack requires iOS 27.1. Development and CI use Xcode and simulator 27.2. The native macOS deployment target is unchanged. - Review the capture isolation and frame-trait API changes before the references. The fix addresses stale inset compensation: requested top/bottom values of 5/9 previously reached native children as 0/0. - The app integration in #372 fixes a separate stale developer-overlay inset delivery. Native accessibility capture corrections belong to this foundation layer; later app layers own their navigation and search behavior. - The Duo profiles retain their recorded 27.1 measurements. Re-measurement on the native 27.2 Duo runtime remains an explicit release-validation item. - Theme descriptions retain three lines of minimum space and grow for large text. The shared picker no longer truncates the Flat description at AX5. ## Testing - At `42ab5b40e`, `./test --review --only SnapshotKitTests --only WhereUISnapshotTests/DeveloperOverlaySnapshotTests` passed all 56 SnapshotKit tests and the Developer Overlay image suite (all six captures matched byte for byte). Architecture and SwiftFormat passed. Review fixes preserve the existing mappings and presets; no references changed. - Full gates on the current head `42ab5b40e`: [GitHub Actions](https://github.com/kyleve/Stuff/actions/runs/43081928022) and [CircleCI pipeline 2195](https://app.circleci.com/pipelines/circleci/HfkN3cJ2hk23YfG42g3oS1/E7hn5FALiSX67sknpk9SZu/2195). - At `f05ca6240`, the full run passed 2,922 iOS unit tests and 28 host macro tests. All 71 snapshot tests are covered by the full run plus focused assertions after review; 679 affected WhereUI tests also passed after the eager Timeline change. - `./test --no-generate --review --timings --only WhereUITests` with the seven affected snapshot suites verified the refreshed references. A final `./test --no-generate --no-build --review --only WhereUISnapshotTests/DeveloperOverlaySnapshotTests` repeated all six overlay captures byte for byte. Architecture checks and SwiftFormat passed. - Full gates for the capture-repair baseline (before the exhaustive-conversion refactor and documentation cleanup): [GitHub Actions](https://github.com/kyleve/Stuff/actions/runs/43079785790) and [CircleCI pipeline 2188](https://app.circleci.com/pipelines/circleci/HfkN3cJ2hk23YfG42g3oS1/E7hn5FALiSX67sknpk9SZu/2188). The capture fixes and reference migration are verified at this PR's own layer. A green stack tip cannot establish that an earlier PR's renderer and references agree. Layer-specific Timeline, Settings, and Year references retain the behavior present here rather than borrowing later adaptive UI semantics. - Independent source review covered capture containment, final annotation sizing, native-bar getter restoration, Settings measurement, and eager Timeline layout. Visual review covered the refreshed references and preserved comparison-sheet metadata; comparison tolerances are unchanged. - Two tall Year accessibility images retain a pre-existing marker offset and incomplete native-toolbar legends ([STU-325](https://linear.app/kve/issue/STU-325)). - Native Duo 27.2 geometry re-measurement and interactive device checks remain pending. Synthetic regular-simulator coverage does not establish native system-bar geometry or spoken VoiceOver behavior. ## Stack First active layer, based on main after [#376](#376) merged. This supplies the capture foundation for #378 and #372. Capture corrections and their references land here; adaptive corrections and references land with the app integration. ## Backlog reconciliation The 13 preserved Duo records are tracked in Linear. [STU-318](https://linear.app/kve/issue/STU-318) owns adoption and its six implementation sub-issues. This slice relates to [STU-319](https://linear.app/kve/issue/STU-319), [STU-320](https://linear.app/kve/issue/STU-320), [STU-327](https://linear.app/kve/issue/STU-327). Unmerged implementation remains In Review. Remaining Backlog work: native Duo release validation ([STU-316](https://linear.app/kve/issue/STU-316)), intermittent Locations settling diagnostics ([STU-317](https://linear.app/kve/issue/STU-317)), and tall Year accessibility annotations ([STU-325](https://linear.app/kve/issue/STU-325)). Existing [STU-114](https://linear.app/kve/issue/STU-114) and [STU-252](https://linear.app/kve/issue/STU-252) now distinguish the partial fixes from their remaining requirements; both retain their priorities, labels, and open state.
Summary
Add typed horizontal and vertical size classes to Broadway. UIKit roots observe changes live; SwiftUI roots map their environment into the same traits. Scoped overrides let component styles resolve the container's layout policy.
This is foundation only. It preserves the current main toolchain and screen behavior.
Primary APIs
BHorizontalSizeClassandBVerticalSizeClassexpose only compact and regular values. Both default to compact. UIKit unspecified/unknown values and SwiftUI missing/unknown values normalize to compact at the bridge, so stylesheet switches need only two cases.BTraits.systemregisters both, andbHorizontalSizeClass/bVerticalSizeClassprovide scoped SwiftUI overrides.Stylesheets read
context.traits.horizontalSizeClassandverticalSizeClassduring slicing. Measured geometry remains a view input. The fallback is an explicit styling policy; known system traits and scoped overrides are preserved without inferring size classes from device identity or bounds.Independent review found one downstream test matrix using the removed enum case. PR #378 now tests all nine raw UIKit size-class combinations through the Broadway bridge, preserving single-pane fallback coverage.
Stack
1 of 5, based on
main. The snapshot foundation follows this PR; reusable navigation and geometry come next. The final app PR describes the complete experience and remaining native release checks.Testing
2fd6f699853ac4fa592a90d3928f83e836bca635,./test --allpassed on Xcode 27.2: 2,975 iOS unit tests, 28 host macro tests, and 31 architecture tests. SwiftFormat passed. The root PR also passed 141 Broadway tests from an isolated build; an earlier shared-cache build hit stale LifecycleKit deployment-target metadata.e9236bb54771a1e144ffbc35ea9076e8f2c4569fpassed both CI gates, including all image suites. The current change normalizes unavailable Broadway size classes to compact and updates the downstream UIKit-input regression matrix; reference PNGs and renderer settings are unchanged. New full gates are running on the current tip: GitHub Actions and CircleCI pipeline 2166.Backlog reconciliation
The 13 preserved Duo records are tracked in Linear. STU-318 owns adoption and its six implementation sub-issues. This slice relates to STU-318. Unmerged implementation remains In Review.
Remaining Backlog work: native Duo release validation (STU-316), intermittent Locations settling diagnostics (STU-317), and tall Year accessibility annotations (STU-325). Existing STU-114 and STU-252 now distinguish the partial fixes from their remaining requirements; both retain their priorities, labels, and open state.