SIGN IN SIGN UP

refactor(web): split the app into index.html + app.css + ten scripts

index.html was 3501 lines: 179 of markup with 3300 lines of CSS and JavaScript wrapped
around it. It is now 213, and the seams are the ones already drawn in the file -- the
`// ---- section ----` comments the author had been using as chapter headings become
web/js/*.js, and the two <style> blocks become web/app.css.

This is a MOVE and nothing else. The script that performed it asserts that: the files
it writes, concatenated in load order with no separator and nothing stripped, must equal
character for character the blocks they were cut from. A blank line changing position
fails it. What proves the behaviour is unchanged is the 190-test browser suite, which
passes without a single test edit.

Three things about the layout are deliberate and easy to "improve" into breakage:

They are NOT ES modules. 77 inline on* handlers -- in the markup and in the template
strings the renderers build -- call these functions by name, so they have to stay on
the global scope. `type="module"` gives each file its own and breaks all 77 at once.
The scripts share one scope exactly as the single <script> block did, which is why
run.sh's duplicate-function check now reads across all of them instead of per file.

The src paths are relative. The browser tests open the app from a file:// URI, where
`/js/state.js` is the filesystem root. app.css's @font-face rule was already relative
for the same reason.

And the relay now serves web/ as a directory rather than from a table of filenames. That
table was a standing bug, not a list: a file committed to web/ is public on Cloudflare
Pages immediately, but over the relay it 404s until someone remembers two more lines in
two different places -- so a missing asset only ever showed for the people on a tunnel.
Ten new files would have meant twenty more lines of it.

web_asset() resolves by extension, and carries two things worth reading:

  Cache-Control is split. Fonts and rasters keep the year of immutable the old table
  gave its one entry. Stylesheets and scripts get no-cache, because they change under a
  FIXED name on every deploy -- handing app.css a year of immutable would pin every
  returning browser to whatever JavaScript it saw first, with no way out but renaming.

  `.html` is absent on purpose. Static assets are exempt from HERDR_RELAY_TOKEN, since a
  browser fetches the stylesheet and the scripts before it can authenticate; index.html
  is served further up, behind that token. Listing `.html` here would turn the exemption
  into a way past it. tests/test_web_assets.py asserts both, along with the containment
  checks -- every segment a plain name, and the resolved path re-checked to be inside
  web/, which is what catches a symlink pointing out of the tree.

Splitting the file also breaks every test that asserted on the app's SOURCE TEXT, and
breaks them in the worst way: `assertIn(..., index.html)` does not fail when the thing
moved to web/js/, it passes vacuously, having looked in the wrong file. Two did.
tests/web_source.py is now the one place that knows the app is more than one file, and
both tests read the whole thing through it. While there, one of those assertions --
`assertIn("function ansiFragment", page)` -- also matched `ansiFragmentX`, so a rename
slipped through the check meant to pin the renderer down; it now includes the paren.
N
Nick007 committed
40909d6b02f81f393fdc4f48c397ea96b84caca4
Parent: ea0892c