Public: anyone can read this; the crew can change it.

forked from trevato/cs · proposes · 4 behind

One page shell serves both games: the document, the style and the stage move to src/pages/shell.ts #21

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`src/pages/shell.ts` is the document every game on this server is served inside, and it knows nothing about Counter-Strike: `page({ title, description, heading, env, pull, commit, script, ssh, terminalLead, stageHtml, sectionsHtml, footer, extraStyle })` renders the doctype, the head, the one `<style>` block, the header with its environment pill and short commit, `<main>`, the terminal line, the f…

shipped production is serving it deploy ae07c2d

opened this

src/pages/shell.ts is the document every game on this server is served inside, and it knows nothing about Counter-Strike: page({ title, description, heading, env, pull, commit, script, ssh, terminalLead, stageHtml, sectionsHtml, footer, extraStyle }) renders the doctype, the head, the one <style> block, the header with its environment pill and short commit, <main>, the terminal line, the footer and the single <script type="module" src>, and stage({ overlays, touch }) renders #stage with #scene, #hud, #fps and the #touch pads built from { action, label } buttons. src/pages/game.ts is down to cs's own content — the three overlays, its touch buttons, the leaderboard, the prose and statsHtml — plus the call to the shell; it still exports escape, now re-exported from the shell so there is one implementation and src/server.ts is untouched.

The escaping is stated in the parameter names: text is escaped by the shell, markup the caller already rendered says so (stageHtml, sectionsHtml, an overlay's html) and is written through. src/pages/shell.test.ts puts </script><script>alert(1)</script> through the title, description, heading, env, commit, ssh, footer, script and a touch label, action and overlay id, and checks the page still holds exactly one <script> and one <style>; a statsHtml-shaped table with an &amp; in it comes back unchanged. A rule only one game needs goes in extraStyle, appended to the shared block with < rewritten as CSS's \3c so it cannot close the block.

What to look at in the preview: / should be indistinguishable from production. The rendered HTML diff is two lines — the terminal sentence moved from the last paragraph of the "What this is" section to a <p class="terminal"> at the end of <main> (same markup, one node later, so the gap above it goes from 16px to 24px; it lives in the shell now because every game needs that line and none should reinvent it), and the footer's apostrophe is &#39;, which renders identically. Everything else is byte-for-byte what it was, including the whitespace. / answered in a median of 0.39 ms, p95 1.56 ms over 200 warm loopback requests. bun test is 176 passing, up from 166 — nine in shell.test.ts and one in server.test.ts that pins scene, hud, fps, touch, menu, pause, buy, stats, name, length, side, sens, play, resume, quit and the six [data-action] buttons against the served page, because nothing type-checks them. No bundle was rebuilt: static/ is not in the diff, no dependency was added, and the security floor stays in src/server.ts — the shell only renders a body.

🤖 Generated with Claude Code

Closes #17

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs