Robot documentation for Team 2102 Paradox, published at https://docs.team2102.org.
Device icons: public/icons/ is a collection of SVG drawings of common FRC devices (controllers, power, motor controllers, motors, sensors, vision, pneumatics and more), in <type>/<vendor>/ folders at one SVG unit per millimetre. They are public domain (CC0 1.0): use them for anything, no credit needed; we only ask that you do not present them as your own work. The site shows them all at docs.team2102.org/icons, read from public/icons/index.json.
The site shows each robot's wiring (CAN, power and network sheets), a 3D model with every CAN loop, power cable and network link, and a searchable device table. It opens on the latest robot; the selector in the side rail (or the top bar on a phone) switches robots.
npm ci
npm run dev # http://localhost:5173
npm run check # typecheck, unit tests and the content test
npm run build # static site in dist/Node 24 or newer.
The export writes public/content/ and public/icons/. Do not edit their files by hand, except mechanical.json and controls.json (see below), which the export leaves alone. robots.json lists the robots in selector order and names the latest one; the export keeps any page kinds it does not write, such as "mechanical" and "controls".
-
Rebuild the wiring outputs in the wiring tools (
~/systemcore/wiring): after a change todevices.json, or after a new CAD export (build_scene.py). -
Export into this repo:
python3 -I wiring/tools/export_prettydocs.py --out ~/PrettyDocsOptions:
--robot <id>(lowercase, digits and dashes),--name,--season,--summary, and--no-latestto add an older robot without making it the default. -
Run
npm run check, then commitpublic/contentandpublic/icons, and push tomain.
Each robot has a folder public/content/robots/<id>/ with wiring.json, poster.svg, top-view.svg and scene.json.
public/content/robots/<id>/mechanical.json is kept by hand (the export does not write it). Each subsystem lists:
mechanisms: registrymechanismvalues; the page's devices table comes fromwiring.jsonby these.groups:scene.jsongroup ids shown solid in the 3D view.facts: each with asource(where the value comes from); adduncertainwith the reason when a value is not sure. Gear and belt reductions come from the CAD:python3 -I wiring/tools/gear_trains.pyin the wiring tools writeswiring/gear-trains.json(tooth counts from part names, links from part positions).notes: service steps, one sentence each.
Change updated when you edit it, and add "mechanical" to the robot's docs in robots.json. npm run check fails if a mechanism or group name does not exist in the export.
public/content/robots/<id>/controls.json is kept by hand. Each controller names a model (an id in src/controllers/models.ts: razer-wolverine-v2, logitech-extreme-3d-pro), its driver station port, and its bindings: input (an input id of the model), label (one to three words), how (hold, press or axis), action (one plain sentence) and code (the binding as written). Check the bindings against RobotContainer.java and set source to the commit you read. A new controller model needs a data module in src/controllers/ and a 3D builder in src/three/controllers/; npm run check fails if an input has no drawing or no 3D part.
The Wolverine V2 in 3D is a stand-in: the free Sketchfab model Xbox controller free by AT design studio (CC BY 4.0; the page shows the credit under the 3D view). Sketchfab merges a model into one mesh per material, so scripts/controllers/split_xbox_glb.py splits it into one node per input, by material and position, and the file is then shrunk without WebAssembly (the site's CSP blocks it, so no meshopt or Draco):
uv run --no-project --with numpy --with scipy python -I scripts/controllers/split_xbox_glb.py xbox_controller_free.glb split.glb
npx -y @gltf-transform/cli@4.5.1 weld split.glb a.glb
npx -y @gltf-transform/cli@4.5.1 simplify a.glb b.glb --ratio 0.2 --error 0.0004
npx -y @gltf-transform/cli@4.5.1 quantize b.glb src/three/controllers/xbox-controller.glbM1, M2, Share and the stick presses are not in the Xbox model; src/three/controllers/xbox-stand-in.ts adds them. If the file does not load, the page uses the built-in model.
Railway builds the Dockerfile on every push to main: Node 24 runs npm run check and npm run build, then Caddy serves dist/. A failed check stops the deploy, and the old version stays live.
Caddyfile: single-page fallback, compression, cache headers (/assetsone year,/contentand/iconsfive minutes, pages no-cache) and security headers..railway/railway.ts: Railway service settings as code (Dockerfile builder, health check on/, restart on failure, sourcemain, thedocs.team2102.orgdomain). Railway does not read it on deploy. After an edit, runrailway config plan, check that the plan destroys nothing, then runrailway config apply.npm run checktypechecks it.scripts/check-headers.sh <url>: checks a running server (fallback, caching, compression, headers).
docs.team2102.org is a custom domain on the Railway service. Cloudflare (the team2102.org zone) holds two records that Railway gives with railway domain docs.team2102.org:
- a CNAME
docsto the Railway target, DNS only (grey cloud), so the zone's SSL setting for the main site does not change; - a TXT record for the Railway domain check.
Railway issues the TLS certificate.
src/
content/ types, validators, loaders and hooks for public/content
logic/ rules with no browser dependency (search, panel rows, view math, 3D styles, facts, table)
shell/ side rail, tab bar, robot selector, routes, theme
pages/ Overview, Wiring, 3D, Devices, Not found
components/ wiring sheet, device panel, segmented control, hero, error boundary
three/ React Three Fiber scene: model, cables, labels, camera
docs/ design spec and implementation plan