docs(webui): describe the single-page layout and updated numbers

The Web UI is now 8 files at 25,302 bytes gzip (23.0 % of the raw
109,814); section switching measured on a real device.
This commit is contained in:
eraiza0816 committed 2026-08-11 06:38:55 +09:00
1 parent 67ae4598d9
commit c4928e5546
1 file changed
+33 -23
+33 -23
View File
@@ -11,18 +11,24 @@ html/ --(minify.py)--> output/html_min/ --(fileadder -z)--> flash image
```
- `tools/minify.py` removes comments (`<!-- -->`, `//`, `/* */`) and
line-leading whitespace without touching string literals, so the output
is functionally identical to the input. Binary files (`.ico`) are
copied through unchanged. The raw sources in `html/` stay untouched
for development; the minified copy in `output/` is a pure build
artifact. Comments and indentation are pure overhead for the served
bytes, so minification alone already reduces the transfer size from
111,280 to 99,715 bytes, independent of gzip.
- The per-page scripts are consolidated into a single `html/main.js`
bundle (i18n, shared helpers, navigation and the page scripts). Every
page loads only this one script, so the browser fetches it once and
gzip can compress across all of it. The page-specific initialisation
runs only when the matching page elements are present.
line-leading whitespace without touching string or regex literals, so
the output is functionally identical to the input. Binary files
(`.ico`) are copied through unchanged. The raw sources in `html/` stay
untouched for development; the minified copy in `output/` is a pure
build artifact. Comments and indentation are pure overhead for the
served bytes, so minification alone already reduces the transfer size
from 109,814 to 97,730 bytes, independent of gzip.
- The Web UI is a single-page application: `index.html` holds all pages
as sections (`#/ports`, `#/vlan`, ...) that the sidebar switches
between via the URL hash. `login.html` stays separate because it is
the authentication gate. Only these two pages plus `main.js`,
`style.css`, the three SVG port images and the favicon are embedded.
- All JavaScript (i18n, shared helpers, navigation and the per-section
scripts) lives in one `html/main.js` bundle that the pages load as
their only script, so the browser fetches it once and gzip can
compress across all of it. A section's initialisation runs when the
section is shown, its polling intervals run only while it is visible,
and re-entering a section refetches the data.
- `fileadder -z` gzip-compresses every file (zlib, gzip format,
`Z_BEST_COMPRESSION`) when it generates the file table
(`html_data.c`/`html_data.h`) and when it embeds the files into the
@@ -41,27 +47,31 @@ Build on any machine (the Web UI is machine-independent):
| | bytes |
|---|---:|
| raw sources | 111,280 |
| minified (comments and indentation removed) | 99,715 |
| gzip (flash + wire) | 28,200 (25.3 %) |
| raw sources | 109,814 |
| minified (comments and indentation removed) | 97,730 |
| gzip (flash + wire) | 25,302 (23.0 %) |
The minified assets are also what the browser receives when gzip support
is missing or disabled, so the transfer size drops in both stages:
111,280 → 99,715 bytes from minification, then → 28,200 bytes from gzip.
109,814 → 97,730 bytes from minification, then → 25,302 bytes from gzip.
The embedded data block ends at `0x5b977` without compression and at
`0x46e28` with it, freeing ~83 KB of the 512 KB image.
`0x462d6` with it, freeing ~86 KB of the 512 KB image.
Merging the JS files into one bundle is worth more than the minifier and
gzip combined: served per file, the 17 scripts gzip to 23,302 bytes; as
one `main.js` bundle they gzip to 16,948 bytes, because the gzip
dictionary spans all scripts and every page fetches the bundle only
once.
The consolidation helps at every step. The 17 scripts gzip to 23,302
bytes as separate files but to 17,231 bytes as one `main.js` bundle,
because the gzip dictionary spans all of them. The single-page layout
then removes the per-page HTML and, more importantly, the repeated
downloads: with the multi-page UI the browser fetched `main.js` on every
navigation; the SPA fetches everything once and section switches are
pure in-page JavaScript (measured on a real device: cold load ~450 ms,
section switch ~60-160 ms, the only network traffic afterwards is the
JSON polling of the visible section).
## Notes
- The files stay well below the `uint16_t` size limit of the file table
(largest gzip output: 16.9 KB for the merged `main.js`).
(largest gzip output: 17.2 KB for the merged `main.js`).
- `fileadder` terminates the embedded files with a NUL directly after the
content (previously at `data_read + 1`), so the `strlen()`-based size
computation no longer depends on uninitialised buffer content.