Field report 2026-08-13, Bazzite host in gaming mode to an iPad: Steam's Big Picture UI looked glaring and over-saturated while HDR game content looked washed out, both on the same stream. Those are one error. gamescope maps everything that is not an HDR game -- the desktop, the Steam overlay, an SDR title -- into the session's PQ container at --hdr-sdr-content-nits, and we only passed that flag when an operator had set PUNKTFUNK_GAMESCOPE_SDR_NITS. Unset, gamescope used its own default of 400, while every first-party client anchors diffuse white at 203 (BT.2408 reference white; the Apple presenter hands exactly that to CAEDRMetadata.hdr10's opticalOutputScale). The two ends were nearly a stop apart, so the UI landed above SDR white and the client's tone-mapper worked from a reference point the host had never used, flattening the content around it. The flag is now always passed, defaulting to 203. The knob still overrides it for anyone who wants a brighter or dimmer desktop. Separately, and visible in the same log: the two HDR decisions in a gamescope session are made independently. hdr_args() never consults wsi_layer_matches_our_gamescope(), so when the WSI-layer version check fires -- which it does on essentially every Bazzite box, since we pin our own gamescope rev and the check compares version triples -- the session launches advertising HDR while having made an HDR10 swapchain unreachable for every game in it. That layer is the only route to one, so a title told to render HDR renders it into an SDR swapchain and looks washed out, with nothing anywhere saying why. It now warns. The behaviour of the check itself is deliberately left alone: re-enabling a genuinely mismatched layer black-screens every Vulkan client, which is worse than losing HDR, and that trade needs a real box to retest. Verified: scripts/xcheck.sh linux clippy clean on pf-vdisplay (-D warnings), rustfmt clean. Not yet verified on glass.
punktfunk-docs
The Punktfunk documentation site: Fumadocs on TanStack Start (Vite + Nitro/bun preset).
Content lives in content/docs/ as .md/.mdx. This site is the source of truth
for the user-facing guides; design rationale lives in the internal punktfunk-planning repo.
API reference
/api renders the host's management REST API as an interactive
Scalar reference (linked from the top nav, the docs
sidebar, and the landing page). It reads public/openapi.json — a
snapshot of the repo's generated spec. Refresh it after a management-API change:
# from the repo root — regenerate the spec, then copy the snapshot in:
cargo run -p punktfunk-host -- openapi > api/openapi.json
cp api/openapi.json docs-site/public/openapi.json
Nothing in CI diffs the two, so the snapshot goes stale silently — that manual cp is the only
thing keeping them in sync. Before publishing docs, check that they match:
diff <(jq -S . api/openapi.json) <(jq -S . docs-site/public/openapi.json)
That should print nothing. Right now it doesn't: the committed snapshot predates the
/api/v1/update/check, /api/v1/update/apply and /api/v1/update/status endpoints, so the
published /api reference is missing the host self-update surface — re-copy it.
Develop
bun install
bun run dev # http://localhost:3001 (docs at /docs)
CI gates every change on bun run build followed by bun run lint (the TypeScript typecheck), in
that order — the build emits the .source typegen the typecheck imports. Run both before you push.
Build & serve
bun run build
bun run start # serves .output/ via Bun
Layout
source.config.ts Fumadocs MDX collection (content/docs)
content/docs/ the docs content (.md/.mdx) + meta.json nav
src/
routes/
__root.tsx RootProvider + html shell
index.tsx landing page
docs/$.tsx catch-all docs renderer (Fumadocs DocsLayout)
api/index.tsx Scalar API reference (reads public/openapi.json)
api/search.ts Orama search endpoint
lib/source.ts Fumadocs loader over the generated collection
lib/layout.shared.tsx shared nav chrome
components/mdx.tsx MDX component map
styles/app.css Tailwind 4 + Fumadocs preset