Repository navigation
Safe area: a bottom bar no longer jumps between two heights - #6129
Conversation
A setSafeArea(true) container measured its preferred size from its current bounds and cached it. Measured before its first layout, or while its parent had moved and it had not, a bottom tab bar was sized for its content alone; layout then padded the bottom inset into that height and clipped the labels. Whichever later pass re-measured it put it back, so the bar jumped between the two layouts as the screen changed (Savor's customer tabs show it). Measurement now also counts the inset the last layout applied, and layout compares the inset it applies with the one measured, on any axis the parent sized from the preferred size; when they differ the size is measured again and the root lays out again in the same revalidate (bounded), so no frame paints the stale size. The inset arithmetic is a pure function, so the check writes no style and fires no extra events. Apps without display insets take no new path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Cloudflare Preview
|
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 231 screenshots: 231 matched. |
|
Compared 210 screenshots: 210 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows x64Runner CPU: AMD64 Family 25 Model 1 Stepping 1, AuthenticAMD (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
|
Compared 210 screenshots: 210 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows arm64Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
|
Compared 206 screenshots: 206 matched. ParparVM vs HotSpot (JDK 25): Linux x64Runner CPU: AMD EPYC 9V74 80-Core Processor (baseline 1 improvement past the baseline (not a failure; rebaseline to lock it in):
Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
A change that moves performance ON PURPOSE records the new baseline in Result: no regression (improved past the baseline: consider rebaselining) |
|
Compared 210 screenshots: 210 matched. ParparVM vs HotSpot (JDK 25): Linux arm64Runner CPU: Neoverse-N2 (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
|
Compared 196 screenshots: 196 matched. Benchmark Results
Detailed Performance Metrics
ParparVM vs HotSpot (JDK 25): macOS arm64Runner CPU: Apple M1 (Virtual) (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
|
Compared 192 screenshots: 192 matched. Benchmark Results
Detailed Performance Metrics
|
Master's #6106 suppresses the snap's padding events and flags the container for re-measure instead. Folded into the split snap: during measurement only nested containers are flagged, so the size just measured is kept and the stale-inset check can run. Measurement now uses the inset the last layout applied (or the first reading in a new safe-area context) rather than the position read mid-layout, which can be stale in either direction. The context (safe margins and surface size) is recorded so rotation, a new safe area, hiding or re-adding the bar re-reads its position. New tests cover a bar pushed off the edge, partly over it, and a display gaining an inset after layout. Goldens: Android SheetSlideUpAnimation and the iOS metal-27 animation filmstrips, where bars measured before placement are now sized with their inset (the toolbar matches its height in the static screenshots). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 195 screenshots: 195 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
The snippet compiled only with an undeclared resourceFile, and master's edit of it left a baseline entry for a hash that no longer exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native fidelity (ios-27-metal)68 pairs compared -- median 94.6%, worst 74.5% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
Native fidelity (Android, Material 3)54 pairs compared -- median 95.6%, worst 91.3% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
A bottom Tabs bar is measured again from inside its own layout, while its style holds the snapped padding; the remembered inset was then added on top of it, and the bar was an inset too tall (iOS TabsBehavior). Layout now records the padding it snapped from, saved and restored around nested layouts, and measurement counts the inset against that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native fidelity (iOS Modern, Metal)68 pairs compared -- median 95.0%, worst 83.5% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
…ow the safe-area fixes cn1ReadSafeAreaInsets was defined only in the iPhone branch of the size-publishing code, while viewSafeAreaInsetsDidChange calls it on every UIKit slice, so the Catalyst and tvOS builds failed with -Wobjc-method-access. The Metal goldens predate #6129/#6080 (the toolbar now clears the status bar) and the watch Sheet golden predates its rounded presentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Dash sample: deterministic runner rules, bot and golden replays Phase 1 of the endless-runner sample (scripts/dash): an int-only 60 Hz simulation shared by app, server, editor and replay viewer. One abstract lane/height/distance world, so 2D and 3D are renderings and a level can swing from side-on to chase camera mid-run without changing how it plays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: race backend, level leaderboards, crews and level packs Server-run races with parties, quick races filled with bots, input stamping and flood limits, WebRTC signaling relay, replay-verified level boards (all/week/day, friends, crew), crews, and the Forge reworked for level packs. Migrations for SQLite, PostgreSQL and MySQL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: runner client, live races with voice, track editor and admin The client renders one deterministic Run in 3D and swings the camera between the side view and the chase view on a level's cues. Solo runs submit their input log for the server to re-simulate; races run in lockstep with seat inputs from the server, with WebRTC voice signalled over the game socket. The track editor paints obstacles, pickups and camera cues on a timeline and playtests the draft. The admin console lists and spectates live races. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: push a friend's beaten score and a crew's party Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: guide chapter, CI workflow and Initializr template Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: make the scripts executable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: website page, flagship card and a draft blog post Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: README prose Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: keep the template's names clear of the renamer The Initializr renames every Dash in the template, so MATERIAL_DASHBOARD and "dashes" were renamed too and the generated app did not compile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: richer art and lobby UI, screenshots and Initializr preview A rendered city backdrop, animated stars, count-ups and reward bursts; a lobby with currency pills, side rails and a bottom nav; result cards, level cards and shop rows rebuilt; runners and tracks from the new Rig/Theme; the side camera frames the runner; e2e screenshots copied into the guide images. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: side view over a causeway, crisper skyline, quiet coach at the end Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: city runs on real streets, in-form result cards, scores day fix Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: cap city XP, in-form dim without fading the card, route minimap progress Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: street signs and landmarks, denser fair tracks, desktop admin, promo video - City runs put the street name overhead and boards for real places along the route (Overpass, replaceable through Landmarks.setSource). - Causeway chunks carry more coins, power-ups and traps, spaced so a sharp bot finishes every weekly seed (new fairness test); goldens regenerated. - Race bot leads by the input delay; party lobby chat is bubbles on one scrollable page; demo boards spread so the scores screen is not a tie. - PromoVideoTest films a captioned tour with the simulator recorder (run-e2e.sh --video); the JavaSE recorder now captures the presented frame, so a recording no longer tears mid-paint. - Guide and website: engagement and live-ops section, desktop admin shots, street/landmark shots, gallery and the promo video. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: red-team round - desktop-size admin with history, real names in race chat - Emotes carry the sender's display name, never the account email. - A development server seeds four weeks of history (DemoHistory): staggered sign-ups, bot races with replays, gem sales, wins and trophies, so the console's charts have a shape; the field's skills spread so runs end along the track instead of tying at the finish. - The desktop pass shoots a 1440x900 window. - Party lobby: the code and invite stay put while runners and chat scroll; the share icon takes the button's colour. - Run and race shots wait for a stretch of track with something on it. - ScoresTest's ghost check asserts the day's seed, not which runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: second red-team round - pipeline races condition, race and scores shots - The console's pipeline conditions name "races" as the backend does; a seeded "races at least 10" no longer reads (and saves) as days away. - The demo history seeds once per database, not once per fresh field. - The race shot is taken while the race runs; the scores shot is the day's endless board, where every run ends somewhere different. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: console wording and deep links match the runner The Promote and Pipelines screen picker offered "deck" and "battle", which the server refuses; it now lists the server's own Promotion.SCREENS. The query table and the invite card say races, not battles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: CI round - every shot in the guide, snippets marked, template synced Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: on-screen run buttons, side view keeps its skyline, aligned lobby, editor and admin side bar VirtualButton and VirtualJoystick setAnchor are public, so two controls can share a corner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: runner redesigned as a faceted low-poly android athlete Lofted octagonal segments with flat normals replace the capsules and spheres; the menu portraits draw the same body as shaded panels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: shots of the new runner and touch buttons, a bounded race warm-up in the party test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: a gameplay clip beside the promo tour, the test bot steers every tick The bot polled once per frame, so a loaded machine stepped several ticks between decisions and the runner hit the first obstacle. RunView.setBeforeTick runs it before every tick instead. The promo tour records at full size with game audio and taps, and a second clip films one run in the phone skin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash sample: build against the released framework, match the profile column, guide snippets from code The shipped template compiles against the released Codename One, where VirtualButton.setAnchor is not public, so the touch buttons use the corner anchor alone and the framework change is dropped. Profile.runner maps to the VARCHAR(40) its migration creates; PostgreSQL and MySQL refused the mismatch. The guide's two city route examples are now tagged code in CityRouteTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * iOS: a watch companion without widgets or NFC builds, and the sample workflows build each target on its own SDK Pulse is the first sample with a watch app, and its iOS job failed three ways: - xcodebuild -sdk iphonesimulator built the watch target on the iPhone SDK, where its watch-only AppIcon has no applicable content. The destination alone picks each target's SDK (pulse.yml, and dash.yml before it gains one). - The watch slice compiles IOSNative.m with the app's switches, and watchOS has no CoreNFC: CN1_INCLUDE_NFC is now off on the watch, as on tvOS. - The surfaces mirror (applyMirroredSurface, the drain on Java ready) called natives that exist only with CN1_USE_WIDGETS; a watch app without widgets failed to compile and then to link. Both are now behind that switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Guide checks: Dash's own class names are real identifiers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash workflow: nightly and on its own tree, as the other samples now run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash guide: contractions and the Oxford comma the prose gate asks for Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs workflows: a change to Dash's code rebuilds the guide and site that include it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash guide: US spellings and the phrasings LanguageTool flags Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * iOS: the safe-area reader compiles on Catalyst and tvOS; goldens follow the safe-area fixes cn1ReadSafeAreaInsets was defined only in the iPhone branch of the size-publishing code, while viewSafeAreaInsetsDidChange calls it on every UIKit slice, so the Catalyst and tvOS builds failed with -Wobjc-method-access. The Metal goldens predate #6129/#6080 (the toolbar now clears the status bar) and the watch Sheet golden predates its rounded presentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Dash: admin tabs without the native capsule, chart dates that fit a phone, openings lead with voice and city runs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * tvOS goldens: the safe area now applies on tvOS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * iOS packaging: a cold Release build no longer fits in 90 minutes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Initializr Dash template synced; the JavaSE verifier waits for the multi-window device to paint A multi-window capture could be taken while the device window was mapped but empty: two menu strips over a blank frame, not flat, so the poll accepted it and the comparison failed. The device-body count now covers multi mode too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>



























































































































































































































































































































































































Problem
A
setSafeArea(true)bottom tab bar (Savor's customer tabs, and customer apps) jumped between two layouts on tab switches: sized for its content plus the bottom inset, or sized for the content alone with the inset padded inside it, clipping the labels.Container.calcPreferredSizejudged the inset from the container's current bounds and cached the result. Measured before its first layout, or mid-layout after its parent moved but before it was placed, the bar got no inset, and nothing made layout reconsider; whichever later pass happened to re-measure it put the inset back.Fix (
Container.java)safeAreaPadding(); the snap applies it.Tests
SafeAreaBottomBarStabilityTest(core-unittests): tab switches, a bar above it appearing/going, safe area removed, bar moved to the top, safe-area bar inside a safe-area pane (one inset), and one layout per revalidate for a settled bar, a stretched pane and a scrollable pane (whose scroll size keeps the inset). Three fail on master (bar 28px instead of 62px).Locally: core-unittests 8249 tests green, SpotBugs 0, PMD 0.
🤖 Generated with Claude Code