Collapse mode and layer onto one picker row; fix renderer diagnosis

Only one layer is ever active, so neither mode nor layer needs a scrolling
row. Two pickers side by side cost one 44px row instead of two.

The panel needed that space: .pads uses minmax(34px,1fr) rows and sat
exactly at the 34px floor, so the SP-404 illustration clipped at the
bottom. Measured on device:

  strip   96px -> 44px
  panel  433px -> 485px
  pads   142px -> 194px, rows 34px -> 44.75px
  unit overflow 1px -> 0

The layer list is read out of the reference's own chips at render time,
so its set, ordering and counts cannot drift from renderExplore().

Also corrects the renderer-denial root cause. It is not screen state: a
denial was observed with Reason[screen_on & caller care]. Every failure
followed 'adb install -r' and an immediate relaunch has always succeeded,
so device.sh now retries the force-stop/start cycle instead of blaming
the lockscreen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 23:04:13 +03:00
parent f8565c35cd
commit 5f9d89d9fb
4 changed files with 149 additions and 52 deletions

View File

@@ -41,11 +41,15 @@ ANDROID_HOME=$HOME/Android/Sdk
## Driving the device
**The screen must be awake and unlocked before launching, every time.** While
the display dozes, TCL denies the WebView renderer and the app comes up blank
with no crash, no JS error and an empty `document.title`. The failure is sticky
for the life of the process, so waking the phone afterwards does not repair it —
the app has to be force-stopped and started again.
**Launch through `scripts/device.sh`, never a bare `am start`.** TCL denies the
WebView renderer in the window just after `adb install -r`, and the app then
comes up blank with no crash, no JS error and an empty `document.title`. The
failure is sticky for the life of the process, so the app must be force-stopped
and started again — which is exactly what the helper retries.
Keep the phone unlocked while working. That is not what causes the denial (see
Device notes), but nothing on screen can be verified through a lockscreen and
`input keyevent` will not reach the app.
`scripts/device.sh` handles this so it cannot be forgotten:
@@ -142,9 +146,21 @@ they will not come along with the reference:
them is `display:none` on phones and still in the DOM, so the adjacent-sibling
selector never matches. Measured gap: 0px. Both selected chips are also
light-filled and left-aligned, so they visually merge into one shape.
2. **The 7 context chips collapse into one 44px control** that opens the existing
sheet, freeing a row for the panel. The sheet rows show `CTXHINT`, which the
phone layout otherwise never displays.
2. **Both chip rows collapse into two pickers side by side on one 44px row.**
Only one layer is ever active, so neither the mode nor the layer needs a
scrolling row of its own. Each opens the existing sheet. The mode sheet shows
`CTXHINT`, which the phone layout otherwise never displays; the layer sheet
shows each layer's function count.
This matters for the panel, not just for tidiness: `.pads` uses
`minmax(34px, 1fr)` rows and on this device it sits exactly at that 34px
floor, so the SP-404 illustration clips at the bottom if anything takes more
room. Two rows cost 96px; one costs 44px, and the 52px goes to the panel.
The layer list is read out of the reference's own chips at render time rather
than recomputed, so the set, ordering and counts cannot drift from what
`renderExplore()` decided. In React this becomes shared derived state
instead.
3. **Chip targets go from 34.9px to 44px.** They scroll horizontally, so an
undersized target turns a mis-hit into a scroll instead of a tap.
4. **Overflowing rows get a right-edge fade.** Scrollbars are hidden, and both
@@ -179,22 +195,32 @@ sudo udevadm control --reload-rules && sudo udevadm trigger
It lives in `/etc/udev` so apt upgrades don't overwrite it. The `uaccess` tag
grants the logged-in session access, so no logout is needed.
**A blank grey window means the screen was off when the app started.** TCL's
`AppBootManagerService` refuses to let the app bind the WebView's sandboxed
renderer process while the display is dozing:
**A blank grey window means the renderer was denied — relaunch.** TCL's
`AppBootManagerService` sometimes refuses to let the app bind the WebView's
sandboxed renderer process:
```
W/AppBootManager: Type[TYPE_SERVICE];Result[false];
Target[org.chromium.content.app.SandboxedProcessService0];
Caller[dev.ncode.sp404layers];Reason[screen_off & caller care]
Caller[dev.ncode.sp404layers];Reason[screen_on & caller care]
E/cr_ChildProcessConn: Failed to establish the service connection.
```
With no renderer the page never parses, so there is no crash, no JS error, an
empty `document.title`, and DevTools `Runtime.evaluate` hangs. The failure is
sticky for the life of the process — reopening from Recents keeps the grey
window. Wake and unlock the phone first, then `am force-stop` and relaunch.
This is an OEM policy, not an app bug, and it only bites when launching over adb.
window. Only `am force-stop` plus a fresh start clears it.
**The trigger is the window just after `adb install -r` replaces the package.**
Every observed failure followed an install; an immediate relaunch with no
install has always succeeded on the first try. Do not trust the `Reason` string:
it reports the current screen state, and both `screen_off` and `screen_on` have
been seen denying, so screen state is not the cause. An earlier version of this
file claimed it was — that was wrong.
`scripts/device.sh launch` therefore retries the whole force-stop/start cycle up
to 3 times. This is an OEM policy, not an app bug, and it only bites when
launching over adb — tapping the icon is fine.
## Gotchas