Files
punktfunk/crates/pf-console-ui/Cargo.toml
T
enricobuehler 0b550dad84 feat(android): the console tables stop drifting in silence, and the stats overlay gets a pad route
WP8 and WP9 of `punktfunk-planning/design/android-console-ui-visual-refresh.md`, in part.

**WP9.3 — shared parity vectors.** The console's background palettes, its settings section names
and its screen-transition motion each existed in three hand-written copies (`pf-console-ui`, this
client, the Apple client) held together by a comment asking the next person to keep them in step.
`clients/shared/console-vectors.json` now holds them, read the way `deeplink-vectors.json` already
is: `include_str!` in Rust, a relative path in Kotlin, `#filePath` in Swift — never a copy, because
a copy is a fourth contract free to go stale. It carries the DERIVED tables too, the 16-cell mesh
and the 4 blob colours per palette, which is the half that reaches the screen and the half Android
never checked: `GamepadPaletteTest` only ever measured the `stops` they are computed from.

Two drifts it immediately caught, both now closed:

* **The easing was the wrong curve.** `ConsoleMotion.EaseOutCubic` shipped as
  `cubic-bezier(0.215, 0.61, 0.355, 1)` while claiming to be the desktop's `ease_out_cubic`. It is
  not: that is the Penner/Ceaser table's curve, ~0.80 at the midpoint where `1 − (1−t)³` is 0.875 —
  visibly slacker over a 260 ms transition. Compose's `Easing` is a plain function, so it now
  evaluates the real thing analytically rather than approximating it at all. (Apple approximates
  with a different bezier only because SwiftUI's `timingCurve` cannot take a closure; the vectors
  sample the curve with a tolerance so all three can meet it.)
* **The desktop has a seventh tab.** Input — touch mode, mouse, invert-scroll, shortcuts — with
  nothing to set on a phone or a TV. `settings.rs` claims in prose that a setting is found under
  the same word on every client; that was true modulo an omission nobody could see. The vectors
  model it with `desktop_only` rather than picking a side, so neither client has to be wrong.

Rust reads it from three tests placed in the files that own the constants, so nothing had to be
made `pub` to be checkable. Verified green under Linux (the crate is `cfg(linux|windows)` throughout
— `cargo test` on a Mac compiles nothing and passes vacuously): 77 passed, 0 failed. Android's side
gates in CI as a FILTERED task; a plain `:app:testDebugUnitTest` would drag the ~20 Roborazzi
screenshot scenes into every push, and those are a release-artifact job.

**WP8.1 — a pad route to the stats overlay.** The tier could only be cycled by a three-finger tap,
which does not exist on a TV, on a gamepad-only session, or under touch passthrough — while the
settings row promised a live cycle. `Select + X` now cycles it, byte-identical to the Apple
client's `GamepadWire.back | GamepadWire.x`, implemented as the mic chord's twin in `GamepadRouter`
and edge-triggered on the button that completes the mask. The buttons still reach the game, as both
existing chords do. `GamepadChordTest` pins eight cases the kit had no cover for at all, including
that the three chords intersect only on Select and that none is reachable through another.

**WP8.5 — a start-of-stream banner.** The desktop's `skia_overlay` banner, ported with its timing
(opaque 5.4 s, then a 0.6 s fade) and its rule of naming only shortcuts that exist: pad chords when
a pad is present, the touch gesture when there is a touchscreen and the mode can use it. Nothing
`Ctrl+Alt+Shift` is advertised, because Android has none of it. It yields to the motion-unreachable
notice rather than stacking with it — that one reports something broken about *this* session.

**WP8.6 — the home card says which profile it connects with.** `HomeTile` carried a
`pinnedProfileId` the card never drew, so a pinned host+profile card was distinguishable from the
host's own only by a subtitle that had been quietly repurposed to hold the profile name. Both now
show the address like every other card and wear a tinted profile chip — the touch grid's own
convention and the Apple client's, inked from the console palette. Unsaved tiles (discovered, Add
Host) take a dashed edge, which is what the other two surfaces already use to say "not yours yet".

⚠ Not a detail panel: the Apple client REMOVED its own and moved the status onto the card, which is
where the lock and the online pip already were here.

**WP8.7 — accessibility, in part.** The console screens carried three `contentDescription`s and no
`semantics`, `Role` or `stateDescription` at all. A settings row now announces once, merged —
label, value, and the description that lives in the floating band far from it — with `Role.Switch`
and a real toggle state, because a toggle row's on/off string was drawn by nothing at all: the
switch replaces the value text, and the switch was two undescribed `Box`es. Decoration is silenced
rather than labelled (the chevrons were read aloud as punctuation on every focused row). The hint
bar's glyphs, the tab strip and the home tiles are done; `GamepadAddHostScreen` and `LibraryScreen`
are not yet.
2026-08-10 19:20:40 +02:00

45 lines
1.9 KiB
TOML

[package]
name = "pf-console-ui"
description = "The Skia console UI for the Vulkan session binary — stats OSD, capture HUD, and (next) the gamepad library; renders on the presenter's device via the Overlay contract"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
# Same Linux+Windows gating as the rest of the client stack.
[target.'cfg(any(target_os = "linux", windows))'.dependencies]
pf-presenter = { path = "../pf-presenter" }
# MenuEvent/MenuPulse (the gamepad service's menu mode drives the library).
pf-client-core = { path = "../pf-client-core" }
# Skia on the presenter's VkDevice (`vulkan`); `textlayout` = skparagraph/harfbuzz for
# the typography the console library needs (~15 MB stripped, prebuilt binaries exist for
# this feature set on x86_64-unknown-linux-gnu AND x86_64-pc-windows-msvc — a source
# build is never triggered on either).
skia-safe = { version = "0.87", features = ["vulkan", "textlayout"] }
ash = { version = "0.38", features = ["loaded"] }
anyhow = "1"
tracing = "0.1"
# `config::GamepadPref` keys the button-glyph style (PlayStation shapes vs ABXY).
punktfunk-core = { path = "../punktfunk-core" }
# Linux links the system SDL3; Windows builds it from source (same choice as the rest
# of the workspace's Windows SDL consumers).
[target.'cfg(target_os = "linux")'.dependencies]
sdl3 = { version = "0.18", features = ["hidapi", "ash"] }
[target.'cfg(windows)'.dependencies]
sdl3 = { version = "0.18", features = ["hidapi", "ash", "build-from-source"] }
# The shared console parity vectors (`clients/shared/console-vectors.json`) are read by three
# tests here — the palette table, the tab names and the transition motion. Dev-only: nothing in the
# shipping crate parses JSON. `pf-client-core` reads its own deeplink vectors the same way.
[dev-dependencies]
serde_json = "1"
[lints]
workspace = true