Split The code is public; the app answers only to the crew.

`renderer.ts` is four concepts in 855 lines: lift the shaders and the viewmodel out, behaviour byte-for-byte unchanged #211

open told opened this
opened this

Split the largest module in the repository along the seams it already has, so the next change to the gun in your hands does not mean reading 855 lines.

src/client/renderer.ts holds, in one file: four GLSL sources as template literals (SCENE_VS/SCENE_FS/LINE_VS/LINE_FS, lines 45–137), the GL plumbing that compiles and uploads them (compile, link, upload, build), and one ~470-line createRenderer closure that is the camera, the level, the players, the effects, the tracer ring and the viewmodel — bob, kick, raise, reload dip, muzzle flash. AGENTS.md names this exact case: "small modules named after one concept... when src/server.ts stops fitting in one read, split it." Every other client module in the repo already follows it — trace.ts is 63 lines, zoom.ts is 50, stride.ts is 178.

This is a shape change, not a behaviour change. renderer.test.ts already drives a recording WebGL2 context and counts draw calls: it is the guard that says nothing moved.

Done looks like

  • [ ] src/client/shaders.ts holds the four sources and compile/link — no DOM, no world, plain strings and a GL context in. A test asserts each source declares the attributes and uniforms build looks up, so a renamed uniform fails a test rather than a black screen.
  • [ ] src/client/viewmodel.ts holds what the hands do: given the held weapon, the time, the velocity, lastShotAt, reloadUntil and the raise clock, it returns the offsets and rolls the renderer applies. Pure numbers in, numbers out, tested without a GL context at all — the bob at a standstill and at a sprint, the kick's decay, the raise at 0 / half / done, the reload dip zero at both ends.
  • [ ] renderer.ts keeps createRenderer, the frame, and the draw calls, and is meaningfully shorter — under about 550 lines — with each remaining block still named by a // --- header the way it is today.
  • [ ] Nothing else imports differently: main.ts and the tests call createRenderer exactly as before, and the module's public type (Renderer, RendererHooks, Camera, LINE_CAPACITY) is unchanged.
  • [ ] renderer.test.ts passes untouched except for imports, and its draw-call counts are identical before and after — quote both in the pull.
  • [ ] bun run check green. No new dependency; the bundle's size before and after is in the pull (it should not move).

Files I expect to change

  • src/client/shaders.ts, src/client/shaders.test.ts, src/client/viewmodel.ts, src/client/viewmodel.test.ts (all new).
  • src/client/renderer.ts, src/client/renderer.test.ts.
  • README.md — the Shape section's module list.

Risks

  • A refactor that changes a pixel is a failed refactor: if a number has to move to make the split clean, stop and say so in the pull rather than folding it in quietly.
  • #208 rewrites the raise to run on the weapon's deployTime and #196 adds a draw budget to this file; both land in the lines this moves, so this waits on both and inherits their tests.
  • The viewmodel reads world.time so a paused match does not animate — keep time an argument, not a clock the new module owns.

Crew

scout — shape, module boundaries and the tests that hold them.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs