With several switches open, every tab was called "Switch". The
dashboard tab now shows the hostname, and the login page shows the
hostname seen at the last login on that address (kept in localStorage,
which is per switch), or the address itself before the first login.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a German dictionary to LANG in app.js (all 317 keys, placeholders
unchanged), the language selector entry in index.html and the login
strings in login.html. Uses Swiss spelling (ss instead of ß), which
reads correctly for all German speakers.
Checked against a mock of the httpd endpoints: every section renders
with the German strings; no layout changes compared to English.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pages become one index.html holding every section, one app.js and a
self-contained login.html. The layout is a sidebar with light, dark and
Selenized themes (or follow the OS), a live dashboard with per-port
packet rates, and confirmation dialogs in front of the actions that can
lock the operator out.
Behaviour changes against the previous pages:
- every request goes through one queue that waits for the previous
response BODY rather than its headers: the firmware shares a single
output buffer across connections and delimits responses by closing,
so a second request in flight corrupts the first
- "Save to flash" merges the command log into the startup configuration
with the grammar the console accepts, shows the result before writing,
verifies the sector by reading it back and clears the command log only
after that; the header shows an "unsaved changes" badge while the
running configuration differs from flash
- the console section shows the output the firmware returns for a
command, including the parser's explanation on a rejected one
- the firmware update section checks size, bank header and CRC16 of the
image in the browser before uploading, shows upload progress and waits
for the switch to come back after the reset
- the ports table shows the learned MAC of a port when there is exactly
one and the count otherwise; the MAC table sorts per column, filters,
and names the LAG a learned address sits behind
- the spanning tree section sends only the settings that changed on
Apply instead of one command per control event
- link aggregation groups are numbered 1-4 like the command line, and
applying an empty group clears it
The translation table is inline in app.js with English, Japanese and
Chinese. The language selector sits in the page header and reloads the
page so that dynamically built tables are rebuilt in the new language.
Only login.html and the favicon are served before authentication; the
stylesheet, the port images and the old script bundle no longer exist.
The simulator's allowlist follows the firmware's.
All 17 page scripts (i18n, shared helpers, navigation and the per-page
scripts) are merged into one html/main.js that every page loads as its
only script, so the browser fetches it once and gzip can compress
across all of it.
The page-specific initialisation is guarded by the presence of the
matching page elements (it used to be selected implicitly by which
script a page loaded); the sidebar fill and the stat popup wiring moved
into DOMContentLoaded handlers because the bundle loads in <head>,
before the body exists.
tools/httpd_sim.h: BUFFER_SIZE 24000 -> 131072 so the simulator can
serve the larger bundle (the firmware is unaffected, it serves the
gzip'd bundle of ~17 KB).
Measured (make MACHINE=KP_9000_6XHML_X2): the JS drops from 23,302 to
16,948 bytes gzip; the whole Web UI from 34,784 to 28,200 bytes (25.3 %
of the raw 111,280); the data block ends at 0x46e28. Verified with a
headless browser against httpd_sim and against a real PCB-K0402WS-V3:
all pages render, the sidebar and the port strip are drawn, no
JavaScript errors.
Two hygiene fixes for the web UI, prompted by a login that appeared to fail
under privacy shields (Brave Shields / NoScript-family extensions):
- httpd: replace the partial "style-src 'self' 'unsafe-inline'" CSP with a
complete, first-party policy (default-src 'self'; script-src 'self'
'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
connect-src 'self'; form-action 'self'). Everything the UI needs is
same-origin; the explicit policy stops shields injecting their own
restrictive report-only probes (the noisy script-src-elem 'none' console
spam) and passes a strict-CSP audit. Verified: no CSP violations in-browser.
- login.html: add autocomplete="current-password" so password managers
recognise the field (they showed "unknown password" without it).
NOTE: these do NOT bypass a browser's LAN-device protection (NoScript "lan" /
Brave Shields), which strips the POST body of requests to a LAN address and is
why the login can fail in-browser while the same credentials work over curl.
That is a deliberate browser security feature; the user must allow the site in
their shields to log in. The backend password (default 1234) is unchanged and
correct.