Build the Wolfskin TV shell and navigation #29
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fm/tv-slice2-shell"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Outcome
Build slice 2: the independent Wolfskin TV shell and navigation beside the merged Wholphin v1.1.0 UI. This is not a release or TV-install request.
AI-assisted (fork-internal work, not an upstream contribution)
Ready for the requested pill-border one-frame re-check: #29
Tested and pushed head:
74033ee1da402396e61b4e0dc0502ecd43000d74. B1, B2, N1 and N4 passed re-review at2ea9301069d7d2adc5a956487d83ec284897fafe; this head adds only the scoped pill-outline correction, generated tokens and documentation. The upstream-background/native-focus blocker and deterministic-weight blocker from the review of9be55c57are addressed. The plain main integration is retained; no rebase/force-push and no public design-module API rename occurred. This is a re-review handoff, not a claim that the new head has already been approved.Settings pill outline correction (B3)
The dedicated
Shell.settingsPillStrokeis 1 dp / 2 stage px, not the 2 dp focus-ring token. The draw is inset half a stroke inside the unchanged 20 dp / 40 stage px border-box; horizontal padding includes the border. Colours now match white22% at rest and ink40% when focused. Browser dash measurement gives 6 px dash / 4 px gap (3:2 times stroke width); those ratios are tokenized. Draw geometry/path effects are cached. Existing public symbols and the approved source JSON are unchanged.Refreshed
st-091,st-092,st-093pairs and metrics are below; the other eight pairs are byte-identical to the prior reviewed head. Native-resolution border detail: prototype left, Compose right. Nominal geometry is corrected; this is not a pixel-equality sign-off. The supplied RGB<205 focused-frame scan still sees 2 versus 3 dark raster rows and 40 versus 42 px outer dark spans; the existing 1.02 focused-row scale remains. Raw row/coverage scans and the crop are attached for the requested one-frame visual decision rather than hiding the remaining raster-edge difference.Previously passed review corrections
Backdrop/top scrim is composed behind native destinations; the shell is transparent for native content and opaque for owned/picker surfaces. Up first asks the real focus manager to move within content; only failure enters the virtual nav. Native focus cannot accidentally enter the nav's pointer click targets.tokens.jsonSHA256 remains5b939f1f3eb90b428008fda75720f6e3a4e844c486668931db3d52bffb987b53. The previous Regular fallback was a defect, not “font metrics”. All eight affected pairs were re-captured and inspected; the other three are byte-identical to prior inspected pairs.mainfolders.appstore/firetvcombine upstream icons with some legacy text and are not certified by this slice.Refs: approved TV spec, especially sections 2, 4–7 and 10; locked Opus prototype.
Implemented
:wolfskin-design: copied approvedtokens.jsonwith provenance; deterministic Kotlin/CSS/checksum generator and fail-closed drift check; fixed colours/type, focus ring/scales, three durations, 140 ms caption fade and critically damped stiffness-650 nav spring.MainContent.kt; oneDestination.Wolfskinsubtype/renderer branch. Wolfskin code lives underapp/src/wolfskin/java; upstreamApplicationContent.ktandNavigationManager.ktare untouched. Jellyfin reads use a small facade, not Conductarr calls.HomeSettingsPage. Advanced settings → Wolfskin screens is the persistent, Advanced-only rollout switch: off restores upstream Home/library content, on restores the Wolfskin placeholders/shell.WOLFSKIN = 8(numeric slot reserved). Existing legal notices/credit survive.Exit-check evidence
Download raw exit-check evidence, comparison script, all 11 pairs/captures/diff maps and build logs. No APK is attached or distributed.
Each half of a pair is 1920×1080 at density 2.0 (
w960dp-h540dp-xhdpi, native Robolectric SDK 34/Roborazzi); frozen prototype left, Compose right. All eleven pairs were inspected side by side. These are measured differences, not a claim of pixel equality. Platform Roboto is required by the spec. The declared 600/650 → Bold 700 face mapping is listed per frame below; remaining browser/Roboto width, tracking, pill/ellipsis sizing and rasterisation differences remain. Weight is no longer dismissed as antialiasing. Some full-frame numeric errors slightly increase after restoring the intended emphasis; these measurements are not a pixel-equality claim. The repaired picker caption stays on one line; the unavailable panel uses the locked panel dimensions and two-line message.Mean RGB error is averaged over channels/pixels on a 0–255 scale. The percentage counts pixels whose maximum absolute channel error exceeds 12. Nav-band error covers the top 150 pixels, including the underlying background; it is not an isolated geometry mask. Large Home differences are intentional because Home content is explicitly outside this slice.
st-094is the profile hand-off according to the frozen STATES entry, not Playback.Keys and motion
JVM Compose tests replay actual Android key events through the shell adapter: switch person 6, Home hero → Settings 8, Settings → Back → Back 10, Home hero → Playback hand-off 11 (shell portion). They also cover last-person gear reachability, hold/repeat/key-up behavior, nav wrap and independent memory/reselect behavior. Persisted preference tests exercise the actual rollout setter and upstream-vs-Wolfskin destination selector. Two additional native tests cover first-row-only Up/Down restoration with the rollout off, and actual Backdrop visibility/owned-background occlusion across the switch.
Actual Compose spring samples (stiffness 650, damping ratio 1):
The motion test checks no overshoot, monotonic movement and settling within 1 px by 300 ms. Captures exercise the actual native UI; they are not HTML redraws.
Commands and results
python3 wolfskin-design/generate_tokens.py --checkmake testvia unchanged serializeddocker/gradle.shBUILD SUCCESSFUL in 4m 10s.make apk-debugBUILD SUCCESSFUL in 1m 9s. Four default/debug APKs compiled. The real branding task scanned 1013 resource keys/all configurations in each packaged APK and passed. Debug MPV stub/decoder limitations are unchanged; artifacts are compile proof only.Validation history is included rather than hidden: initial new capture/key defects were repaired; Maven Central later returned 429 for Robolectric runtimes, so approved SHA512-verified existing host artifacts seeded the existing cache with a worktree-guarded temporary init script. That temporary init hook was removed after green proof; no wrapper, Dockerfile, volume definition or shared daemon changed. The first APK attempt exposed the private incremental-XML input defect; the next exposed applied-script plugin classpath isolation. The final check consumes public packaged APK artifacts from the existing typed app build script and passed. The approved plain merge retained both the Conductarr-client and Wolfskin-design module wires and passed its combined-tree gates. The first review-fix test run then caught an incorrect new near-black expectation: the approved owned background is #07070a, not RGB <.02. Instruction 012 authorized only correcting that new expectation to the declared token with one 8-bit-quantum tolerance, one extra make test, then the still-unused make apk-debug. Final results on the exact pushed head are 19 design + 555 app tests passed and four debug APKs built/branding-checked. Instruction 013 then authorized exactly one make test and one make apk-debug for the pill correction: both passed on
74033ee1(4m 10s and 1m 9s respectively, excluding any serialized-lock wait). Each additional Gradle invocation was explicitly authorized; no further Gradle run followed this pill-head green APK proof. The temporary offline hook was removed.make apkwas deliberately not used because it is the release target; the approved substitute ismake apk-debug.Every pre-existing tracked file changed
The table includes fork build/docs files as well as upstream files. Launcher paths in
mainare restored to upstream artwork; their existing Wolfskin counterparts now live inapp/src/default/res/. Theme-retirement switch sites are exhaustiveness cleanup, not additional shell hooks.app/build.gradle.ktsapp/src/main/java/com/github/damontecres/wholphin/MainActivity.ktapp/src/main/java/com/github/damontecres/wholphin/MainContent.ktapp/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.ktapp/src/main/java/com/github/damontecres/wholphin/ui/cards/WatchedIcon.ktapp/src/main/java/com/github/damontecres/wholphin/ui/components/SliderBar.ktapp/src/main/java/com/github/damontecres/wholphin/ui/nav/Destination.ktapp/src/main/java/com/github/damontecres/wholphin/ui/nav/DestinationContent.ktapp/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.ktapp/src/main/java/com/github/damontecres/wholphin/ui/preferences/SwitchPreference.ktapp/src/main/java/com/github/damontecres/wholphin/ui/theme/Theme.ktapp/src/main/java/com/github/damontecres/wholphin/ui/theme/colors/WolfskinThemeColors.ktapp/src/main/proto/WholphinDataStore.protoapp/src/main/res/mipmap-anydpi-v26/ic_banner.xmlapp/src/main/res/mipmap-hdpi/ic_launcher.webpapp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webpapp/src/main/res/mipmap-hdpi/ic_launcher_round.webpapp/src/main/res/mipmap-mdpi/ic_launcher.webpapp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webpapp/src/main/res/mipmap-mdpi/ic_launcher_round.webpapp/src/main/res/mipmap-xhdpi/ic_banner.pngapp/src/main/res/mipmap-xhdpi/ic_banner_foreground.pngapp/src/main/res/mipmap-xhdpi/ic_launcher.webpapp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webpapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webpapp/src/main/res/mipmap-xxhdpi/ic_launcher.webpapp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webpapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webpapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webpapp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webpapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webpapp/src/main/res/values/strings.xmlapp/src/main/res/values/wolfskin.xmlapp/src/test/java/com/github/damontecres/wholphin/ui/WolfskinThemeColorsTest.ktdocker/README.mdsettings.gradle.ktsNew files are confined to the design module, Wolfskin UI/read facade, debug fixture, default-flavour overrides and focused native/key/routing tests. No aliases or compatibility palette remain. The review repair changes only existing Wolfskin-owned UI/token generation, its native regressions and fork build documentation; no extra upstream hook/file is introduced.
Not included / deployment boundary
Home, libraries, title pages and Discover content are not implemented in this slice: owned routes are visibly labelled placeholders, or existing upstream Home/library content is selected by the Advanced switch. Requests data/count integration and Conductarr sign-in are not implemented; the count-five screenshot is fixture-only. A complete locale migration/non-default-flavour certification, dedicated reduced-motion verification, pointer scrim consumption and the other non-blocking review suggestions are not included in this repair. No player redesign, release build, release/tag/release-asset publication, signing/version configuration change, ADB/device interaction, installation or TV performance profiling occurred. Cross-family review and any merge belong to firstmate; release and installation still require Justin's word.
Cross-family design review (Claude Sonnet): changes requested. Reviewed head
9be55c570afd863b858484bc3b1b47a8a90dc678; no Gradle was run by the reviewer.Blocking
Backdropcomposable is only called fromApplicationContent.kt, whichWolfskinAppreplaces and never redraws, andShellSurfacepaints an opaque background, so upstream Home/library/title pages show no backdrop when "Wolfskin screens" is off. (b) Inferred from Compose key dispatch: the shell'sonKeyEventturns every unconsumed D-pad Up into "focus the nav", so with upstream content Up from a lower row would jump to the nav instead of the row above; no test uses focusable content. Fix: draw the backdrop in the off path, move focus up first and go to the nav only when it cannot move, and add a two-row focusable-content test.Checked and fine: geometry/colour/focus ring/gear/skeleton/error panel/Settings rows match the prototype; claimed diff numbers reproduce independently (ffmpeg luma check) and
compare.pydoes what the PR says; press counts 6/8/10/11 are asserted by real key-replay tests that would fail on regression; held-OK guard test is real; spring samples 554.5/628.7/665.9/680.2 px at 50/99/150/200 ms match spec; one hook, oneDestination.Wolfskinsubclass, upstream files touched are the spec list plus the branding/theme-retirement reverts (diffed against v1.1.0; icons byte-identical to the old main files); theme slot 8 reserved with a safe fallback; hidden switch only under Advanced settings, default on; token drift check fails on a mutated token (scratch copy, no Gradle) and tokens.json equals the approved spec source; no existing test weakened; no credential handling beyond the debug fixture, no Conductarr calls, no release/signing/version change; commits clean.Non-blocking (nav label colour fade missing, pointer reaches nav under the Requests sheet, MainActivity outer theme not exactly upstream, locale branding half-moved, dead card key-up path, test gaps, build coupling) are in the full report:
/root/.treehouse/firstmate-7bab20/2/firstmate/data/review-wolfskin-29/report.md.Commit status
review/design-claudeset to failure on this head.Design re-review (Claude, scoped to the delta from
9be55c57to2ea93010): changes requested.Reviewed head:
2ea9301069d7d2adc5a956487d83ec284897fafe. Read-only; no Gradle/make run.Fixed:
WolfskinAppnow draws the real upstreamBackdrop(same composable andBackdropServiceflow asApplicationContent) behind native content, and the shell is transparent there.ShellSurfacecallsmoveFocus(Up)first and enters the nav only when focus cannot move; nav items no longer take native focus (canFocus = false). The newUpstreamContentFocusTest(real two-row focusable slot, real key events; real red dynamic backdrop pixel off, owned-background pixel on) would fail if either fix were reverted, by reading the code. Lane: 19 + 555 tests green.transition: color .2s ease-out. Locale branding (N4): honestly documented as not migrated.generate_tokens.py --checkpasses,tokens.jsonsha unchanged. No public design-module symbol removed or renamed. No tests deleted or weakened. Nothing outside the allowed file list changed.Blocking (new, in code the delta did not touch; I missed it in round 1): the dashed outline on the Settings pills ("Wolfskin screen", "Wolfskin", "upstream screen") is twice the prototype's thickness. Spec: 2 px dashed at the 1920 stage = 1 dp.
SettingsPillstrokes with the focus-ring token (2 dp = 4 px). Measured on st-091 at native resolution: prototype 2 px, Compose 4 to 5 px. It is visible in the pairs and the PR table does not name it. Fix: use a 1 dp shell token for the pill outline, re-capture st-091/092/093, and name it in the table.Everything else in the delta is sound. Commit status
review/design-claude= failure on2ea93010.