The fleet has ~700 devices and no way to see them: the device console manages one machine from its own keyboard, and the question "which devices are installed, and which wave is each in" had no surface at all. This adds it as the first screen. Python standard library only -- http.server, html.escape, string.Template. No framework and no third-party import, because the fleet installs offline from a committed package pool that contains no web framework, so fastapi/uvicorn/flask would have made the panel uninstallable on the devices it manages. That was decided with the user over the FastAPI they first picked, and a test asserts it: the module imports only stdlib plus its own siblings. What it shows: the dark header with the target and the privilege level; a summary of counts by state with one loud figure; the device list with a state badge per row, monospace ids and hostnames so columns line up, non-ready devices sorted first, and a filter bar; the selected device's detail with its four-channel verification, since READY is refused until every channel is verified (docs/27). Nothing is colour-only: every badge carries its Turkish state word. Data has no live source yet, so a missing inventory file falls back to synthetic records that the page labels as demo in two places -- the panel never presents invented devices as real ones. Two defects found by reviewing a real render, both fixed here with a test that fails when the bug returns (proven by mutation: each assertion goes red against the reintroduced bug, green after): - --state-provisioned resolved to var(--info), and --info is --accent, so a KURULDU badge and the selected row were the same blue. The state now resolves to its own colour, and the test resolves var() aliases before comparing -- comparing the raw declarations does not detect this, which is how the first version of the test passed against the bug. - the wave cell printed the bare code and then a label that already contains it, so one device read "P2 P2 — PILOT-2". Each wave is named once, now. Also fixes two entries in docs/27 that had become untrue: it claimed a clone carries the packages and the installer downloads nothing, which stopped being the case when the pool moved to Git LFS and bf-bootstrap began cloning without smudge. The real flow -- light clone, payload from a local mirror or a targeted `git lfs pull` -- is documented instead. Tests: tests/test_web_static.py, 112 tests, collected by tests/run-all.sh (12/12 PASS overall).
3.3 KiB
UI work — remaining, measured
What is left after the first pass on the two operator surfaces. Each item was observed on a real render, not inferred. Kept as a list so a gap is recorded rather than remembered.
Device console (bfos/ui_gui.py)
Reviewed against a screenshot of the running window (spectacle, 1440x900),
with the token values read back out of bfos/ui_theme.py.
Landed — grouping into headed regions; the privilege state moved out of a
monospace paragraph into the header and a banner; the duplicate Çık removed;
the output well styled as read-only; status colours from the shared tokens. The
theme file carries the same values as web/static/tokens.css (#1d4ed8,
#15803d, #b45309) — verified by comparison, not by assertion.
Still true at the time of writing:
- The widgets still read as default ttk. Inputs, comboboxes, checkbuttons
and the scrollbar keep the renderer's native chrome.
ui_theme.pyselectsclambecause it honoursbackground/bordercoloron the elements it restyles, but a themed element is not a redesigned control. Closing this needsStyle.element_creatework per control, or hand-drawn widgets, and it is the single largest remaining gap between this console and the web panel. - The product name appears twice.
root.title("Blueforce Field OS — Yönetim")and the in-window header band carry the same words. One of them should go; the window title is what the taskbar and window switcher show, so the header band is the better candidate. Cihaz durumu: YOKis ambiguous when no target is selected: it reads as a state a device is in rather than "nothing selected yet".HAZIRin the action bar looks like a disabled button — it is a status label, but it is styled in the same register as the controls beside it.- The operation list has no unambiguous selected/disabled distinction. The first row carries a focus ring while being unavailable; the ring reads as "actionable" and the reason is only in the row's trailing text.
Central panel (web/)
Reviewed against served HTML and captured pages at 1440x900.
Fixed in this pass — the accent and the KURULDU state were the same blue
(--state-provisioned: var(--info), and --info equals --accent), so a
selected row and a state badge were indistinguishable; both now resolve to
different colours, asserted by a test that resolves aliases before comparing.
The wave cell printed the bare code and then a label that already contains it
("P2 P2 — PILOT-2"); it now names each wave once.
Still true:
- No scroll affordance on the device table. The page reports 13/13 while only part of the list is in view, and nothing signals that more rows exist.
- The read-only privilege is repeated in the header note and a second line at the far right; one is enough.
- The
HAZIRcount appears in the loud figure and again in the breakdown. Defensible (summary then detail), but worth a decision rather than an accident. - Timestamps are ISO-8601 (
2026-09-16T06:20:00+03:00). Precise and sortable, but slow to scan for a human at a glance. - A
HATAdevice offers no next action — the panel says a person is needed and stops. For a fleet panel this is the workflow gap to close next.