Fixes from live debugging on the Deck:
- check_update() was dead on-device: Decky Loader's embedded (PyInstaller)
Python has no usable default CA paths, so every HTTPS fetch failed with
CERTIFICATE_VERIFY_FAILED. Build the SSL context explicitly: default paths
first, then the known system bundles (SteamOS/Arch, Debian, Fedora/Bazzite,
openSUSE), then certifi if importable. Verification stays on; the check
stays offline-tolerant with its 30-min cache.
- "could not chmod runner" on every use: Decky extracts plugin zips without
exec bits into a root-owned dir the unprivileged backend can't chmod. The
Steam shortcut now launches the runner through /bin/sh with the script as a
%command% argument — no exec bit needed, existing shortcuts migrate on
reuse, the chmod attempt is gone.
UI/structure:
- index.tsx (660 lines) split into page/pair/settings/hooks/boundary modules;
PluginErrorBoundary kept guarding every surface.
- New About section/tab: visible version + channel, explicit check-for-updates
(forces past the cache, always toasts an outcome), setup-guide link, leave-
chord help, and a Force-stop backstop for a wedged stream.
- Host rows open a details modal (address, protocol, pairing policy, paired
state, fingerprint). Settings gain 1280×800 (Deck native), Xbox One and
DualShock 4 pad types, and a host-compositor picker.
- Update flows note the Decky store contact can stall a couple of minutes on
networks that blackhole plugins.deckbrew.xyz (observed live).
- "Punktfunk" in all user-facing strings; plugin id/paths/env unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Decky Loader plugin so a Steam Deck / SteamOS box can launch the punktfunk
client from Gaming Mode using REAL Steam UI components (it runs inside Steam's
CEF, so the panel is built from @decky/ui — the literal Big Picture primitives,
not a replica).
- Frontend (src/index.tsx, @decky/api + @decky/ui): a Quick Access Menu panel —
Refresh → discover hosts, a native list (name, ip:port, pairing flag), tap to
connect with a status toast, Disconnect.
- Backend (main.py): discover() shells `avahi-browse -rpt _punktfunk._udp` and
parses the host's advertised TXT keys (proto/fp/pair/id from discovery.rs),
dedup by id preferring IPv4; connect() resolves + spawns
`punktfunk-client --connect host:port` (gamescope composites its video like a
game), tracking the child; disconnect() terminates it.
- Mirrors the current official Decky template (the API moved to @decky/ui +
@decky/api). Frontend builds clean (pnpm build → dist/index.js); main.py
py_compiles. dist/ + node_modules gitignored — build on the Deck per README.
Spike scope: launcher only, runtime untested (no Deck here). Next on this track:
the in-stream Quick-Access overlay (volume/disconnect/stats over the running
stream) and a fuller real-components UI. Client decode on the AMD Deck is the
existing VAAPI path; the host-encode VAAPI gap is separate (NVIDIA host = NVENC).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>