`HostRow.os` has been plumbed since the model landed, with a comment saying the drawing was a follow-up because "the Skia glyph set doesn't exist yet". It does exist: assets/os-icons ships thirteen licensed masters, and `pf_client_core::os::os_icon_tokens` already resolves a chain to them - walking most-specific-first and applying the brand aliases (`macos` -> `apple`, `steamos` -> `steam`). Every other front-end walks that same list. So the console takes the shared resolver rather than inventing one, and gets its table GENERATED from the masters (`scripts/gen_os_mark_table.py`, hooked into the existing `gen-os-icons.sh`) rather than hand-transcribed. Thirteen paths of up to 3.5 kB where one mangled character is a silently wrong logo is not work for a human, which is precisely the reasoning the launcher-icon tables already carry. A new master now reaches the console for free; the script's closing note says so. Two corrections to the plan this implements, both found in the code: - The chain is SLASH-separated and resolves most-specific-FIRST, not "the first known token of a `;`-chain". A `linux/fedora/bazzite` host draws Bazzite, and falls back through Fedora to Tux - so the console is right about thirteen distros rather than the four the plan scoped. - The hint bar was already a glass pill, not "ink on the field". What it was missing is that it mixed its OWN glass (a flat wash and a hand-rolled stroke), making it the one floating surface that ignored the palette; it now goes through `theme::panel` like the chip and the toast, and picks up the lit edge. `draw_monogram` becomes `draw_badge`: the OS mark when the chain resolves, the initial when it doesn't. A substitution, not an addition - a badge showing both a Tux and an "L" says the same thing twice - and an older host that advertises no `os` keeps its monogram pixel for pixel. The controller chip gains a pad silhouette and a battery pip. `PadInfo` gets an additive `battery: Option<PadBattery>`; nothing crosses the wire, this is local SDL state. The plan expected to poll "on the existing pad-refresh cadence" - there isn't one, `publish()` is entirely event-driven (hotplug, pin change). And `pad_info` is deliberately open-free because an open GRABS the hardware, while SDL only reports power for an OPEN device. So the level is read from the ONE pad the service already holds open - `menu_open`, the nav pad, which is open exactly while a console is on screen and is the only pad any UI asks about - on a 15 s poll inside the loop that already wakes every 10 ms. Every other pad publishes `None`, which is the honest answer. `None` renders as no battery at all, never 0 %: a wired pad, a Steam virtual pad and SDL's `-1` "powered, level unknown" are all the same non-answer, and 0 % is the one reading that sends someone hunting for a charger. Charging outranks the low-charge red, because a pad at 4 % on the cable is not the problem a pad at 4 % off it is. Finally, Rescan: a second sentinel tile trailing Add Host, sending the `ConsoleCmd::Probe` that has existed unsent by any screen since it was written. A controller surface has no pull-to-refresh, so the affordance has to be a tile. The two trailing tiles are actions rather than hosts, so `hosts.get(i)` answering both "which host" and "which action" with one `None` became a `Slot` enum - with a second action tile that ambiguity is a bug waiting, and the test that matters is that an accidental A on the end of the strip can never start a session. Verified in the pf-gtkflow container: fmt, clippy --all-targets -D warnings, plain build, 104 tests green.
109 lines
4.4 KiB
Bash
Executable File
109 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Derive the per-client host-OS-icon assets from the assets/os-icons masters.
|
|
#
|
|
# The masters are monochrome `fill="currentColor"` SVGs, one per icon token of the host's
|
|
# OS-identity chain (see assets/os-icons/README.md). Three clients need a baked derivative
|
|
# because they cannot consume the master directly:
|
|
#
|
|
# GTK shell symbolic SVG, black fill -> clients/linux/data/icons/scalable/actions/
|
|
# Windows shell PNG, h=32, mid-grey -> clients/windows/assets/os/
|
|
# Apple clients vector PDF, black fill -> clients/apple/.../OsIcons.xcassets/
|
|
#
|
|
# The web console, the Decky plugin and the Android client transcribe the master's path
|
|
# data inline instead — those are hand-kept, and this script prints them at the end so a
|
|
# new token can be pasted straight in.
|
|
#
|
|
# Idempotent. Usage: bash scripts/gen-os-icons.sh [token ...] (default: every master)
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
MASTERS=assets/os-icons
|
|
GTK=clients/linux/data/icons/scalable/actions
|
|
WIN=clients/windows/assets/os
|
|
APPLE=clients/apple/Sources/PunktfunkKit/Resources/OsIcons.xcassets
|
|
|
|
# The Windows shell has no vector element and no theme-aware tint, so its PNGs are baked in
|
|
# one mid-grey that stays legible on both the light and the dark WinUI theme.
|
|
WIN_GREY='#8A8F98'
|
|
WIN_HEIGHT=32
|
|
|
|
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
|
|
|
command -v rsvg-convert >/dev/null 2>&1 || {
|
|
echo "rsvg-convert not found (brew install librsvg / apt install librsvg2-bin)" >&2
|
|
exit 1
|
|
}
|
|
|
|
tokens=("$@")
|
|
if [ ${#tokens[@]} -eq 0 ]; then
|
|
for f in "$MASTERS"/*.svg; do tokens+=("$(basename "$f" .svg)"); done
|
|
fi
|
|
|
|
tmp=$(mktemp -d)
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
for t in "${tokens[@]}"; do
|
|
src="$MASTERS/$t.svg"
|
|
[ -f "$src" ] || { echo "no master for token '$t' ($src)" >&2; exit 1; }
|
|
log "$t"
|
|
|
|
# GTK: the master with the fill resolved to black — Adwaita recolors a `-symbolic` icon
|
|
# from the fill it finds, so the value only has to be a real colour, not the final one.
|
|
sed 's/currentColor/#000000/' "$src" > "$GTK/pf-os-$t-symbolic.svg"
|
|
|
|
# Windows: same black-to-grey substitution, rasterized at a fixed height so every mark
|
|
# shares an optical size and keeps its own aspect ratio.
|
|
sed "s/currentColor/$WIN_GREY/" "$src" > "$tmp/$t.grey.svg"
|
|
rsvg-convert -h "$WIN_HEIGHT" -f png -o "$WIN/$t.png" "$tmp/$t.grey.svg"
|
|
|
|
# Apple: a vector PDF at the master's natural size, in a template imageset — SwiftUI
|
|
# tints it from foregroundStyle, so the baked colour is irrelevant.
|
|
sed 's/currentColor/#000000/' "$src" > "$tmp/$t.black.svg"
|
|
mkdir -p "$APPLE/os-$t.imageset"
|
|
rsvg-convert -f pdf -o "$APPLE/os-$t.imageset/$t.pdf" "$tmp/$t.black.svg"
|
|
cat > "$APPLE/os-$t.imageset/Contents.json" <<JSON
|
|
{
|
|
"images" : [
|
|
{ "filename" : "$t.pdf", "idiom" : "universal" }
|
|
],
|
|
"info" : { "author" : "xcode", "version" : 1 },
|
|
"properties" : {
|
|
"preserves-vector-representation" : true,
|
|
"template-rendering-intent" : "template"
|
|
}
|
|
}
|
|
JSON
|
|
done
|
|
|
|
echo
|
|
# The Skia console parses SVG path data at RUNTIME, so it needs no baked derivative — it
|
|
# needs the path string, and gets it as a generated Rust table rather than a hand-kept
|
|
# inline registry. Thirteen paths of up to 3.5 kB where one mangled character is a silently
|
|
# wrong logo is not transcription work for a human. Always regenerated from EVERY master,
|
|
# whatever tokens this script was invoked with: it is one file, and a partial rewrite would
|
|
# drop the rest.
|
|
log "console Rust table (crates/pf-console-ui/src/os_marks.rs)"
|
|
python3 scripts/gen_os_mark_table.py
|
|
|
|
echo
|
|
log "Inline path data (web/src/components/os-icon.tsx, clients/decky/src/os-icon.tsx,"
|
|
log " clients/android/.../components/OsIcons.kt — hand-kept, paste from here)"
|
|
for t in "${tokens[@]}"; do
|
|
python3 - "$MASTERS/$t.svg" "$t" <<'PY'
|
|
import re, sys
|
|
svg = open(sys.argv[1]).read()
|
|
box = re.search(r'viewBox="([^"]+)"', svg).group(1)
|
|
d = re.search(r'<path[^>]* d="([^"]+)"', svg).group(1)
|
|
w, h = box.split()[2:]
|
|
print(f'\n {sys.argv[2]}: viewBox "{box}" (viewport {w} x {h})\n {d}')
|
|
PY
|
|
done
|
|
|
|
echo
|
|
log "Remember: a NEW token also has to be added to each client's shipped-token list —"
|
|
log " clients/linux/src/ui_hosts.rs, clients/linux/data/resources.gresource.xml,"
|
|
log " clients/windows/src/app/os_icons.rs, clients/apple/.../PunktfunkKit/OsIcon.swift,"
|
|
log " plus the three inline registries above. (The console's table is generated above and"
|
|
log " needs no list — it ships whatever masters exist.)"
|