Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Repository guidance for coding agents

## Project shape

- This is a build-free JavaScript arcade plus one Node.js service. Browser pages load source files directly; do not introduce a bundler or framework without an explicit architectural decision.
- Modern games share accounts, results, achievements, appearance, navigation, and room UI through `arcade.js`, `arcade.css`, `theme-init.js`, and `scripts/`.
- Game-specific browser code lives in each game directory. Keep testable mechanics separate from DOM/rendering code when practical.
- The Node service in `server/` serves static assets, REST APIs, SQLite persistence, and authoritative WebSocket rooms.
- Classic games are intentionally preserved beside modern versions. Avoid broad rewrites of `classic/` directories.
- Coding-challenge directories such as `leetcode/`, `codeforces/`, and `codewars/` are independent scripts, not arcade runtime components.

Read `docs/architecture.md` before changing subsystem boundaries. Review the relevant record in `docs/adr/` for themes, persistence, security, results, online rooms, Battle Tanks, or Tetris. Add an ADR when a change makes a durable architectural choice rather than quietly overriding an accepted decision.

## Working conventions

- Use Node.js 22 or newer.
- Preserve the dependency-light, no-build browser architecture and existing plain JavaScript style.
- Match the formatting of the file being edited. Much of the CSS and browser markup is intentionally compact.
- Keep shared behavior in shared files and game-specific behavior in the game directory. Do not make game scripts branch on named experience themes.
- Experience themes (`playful`, `cabinet`, `calm`) and color modes (`system`, `light`, `dark`) are independent. Theme palettes and layout contracts belong in CSS custom properties and shared theme selectors.
- Maintain accessibility: semantic controls, useful labels, keyboard support, visible focus, live regions for status, and touch targets that remain usable at phone sizes.
- Treat narrow and short viewports as first-class. For mobile game changes, verify the board/canvas and controls together, account for `env(safe-area-inset-*)`, and test at least one phone-sized viewport such as 390 x 844. For layout changes, also check a laptop/desktop viewport and every experience theme affected.
- Keep mechanics deterministic and independently testable. Rendering and client prediction must not become sources of authoritative online state.

## Security and data boundaries

- Never trust client-authored scores. `server/accounts.js` validates bounded result facts and derives scores server-side.
- Online room managers own authoritative multiplayer state. Validate commands, preserve reconnect semantics, and serialize only viewer-safe state.
- Do not expose passcodes, session tokens, resume tokens, concealed opponent state, private implementation files, or database paths.
- Preserve origin checks, payload limits, rate limits, heartbeat handling, and secure cookie behavior when changing HTTP or WebSocket code.
- Add schema changes as the next ordered SQL file in `server/migrations/`. Never edit a migration that may already have shipped.

## Tests and local verification

Run from the repository root:

```sh
npm test
npm run check
```

Use focused tests while iterating, for example:

```sh
node --test tests/tetris.test.js
node --test tests/profile-page.test.js
```

Before handing off a change:

1. Add or update regression tests for the behavior.
2. Run the focused tests, then the full test suite and syntax checks.
3. Run `git diff --check` and review the final diff for unrelated changes.
4. For visual or interaction changes, start `node server/index.js`, open `http://localhost:8080/`, and verify the rendered behavior at relevant desktop and mobile viewports. Reload after source changes because there is no build or hot-reload layer.
5. For online changes, test both local and online paths plus disconnect/resume and rematch behavior where applicable.

## Git hygiene

- Inspect `git status` before editing and preserve user-owned changes.
- Base requested feature/fix branches on the latest `master` unless the user specifies another base.
- Keep commits focused and use concise imperative subjects consistent with the repository history.
- Do not commit, push, rewrite history, or open a pull request unless the user asks.
5 changes: 3 additions & 2 deletions profile.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ h2 { margin-top: 0; font-size: clamp(25px, 4vw, 34px); }
.stat-metrics div + div { padding-left: 14px; border-left: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); }
.stat-metrics strong { display: block; font-size: 26px; line-height: 1.1; }
.stat-metrics small { display: block; margin-top: 5px; color: var(--muted); font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
.shell form { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 10px; }
.shell form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) auto; gap: 10px; }
.shell form input { min-width: 0; }
.shell :where(input, select, button) { min-height: 44px; padding: 11px 13px; color: inherit; background: var(--field); border: 2px solid var(--ink); border-radius: 12px; font: inherit; }
.shell button { color: #fff; background: var(--button); box-shadow: 3px 3px 0 var(--ink); font-weight: 900; cursor: pointer; }
.shell button:hover:not(:disabled) { background: var(--button-hover); box-shadow: 1px 1px 0 var(--ink); transform: translate(2px, 2px); }
Expand Down Expand Up @@ -109,7 +110,7 @@ th { color: var(--muted); font-size: 11px; letter-spacing: .08em; text-transform
.profile-achievement small, .profile-achievement p { color: var(--muted); }
.profile-achievement p { margin: 4px 0; font-size: 13px; }
.profile-achievement progress { width: 100%; accent-color: var(--accent); }
@media (max-width: 820px) { .shell form { grid-template-columns: 1fr 1fr; } .shell form button { grid-column: 1 / -1; } }
@media (max-width: 820px) { .shell form { grid-template-columns: repeat(2, minmax(0, 1fr)); } .shell form button { grid-column: 1 / -1; } }
@media (max-width: 650px) {
.shell { width: min(100% - 20px, 560px); }
.hero { margin-top: 50px; }
Expand Down
5 changes: 5 additions & 0 deletions tests/profile-page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ test('profile controls do not restyle the shared top bar', () => {
assert.doesNotMatch(styles, /(?:^|\n)button\s*\{/);
});

test('profile management fields fit the compact Cabinet column', () => {
assert.match(styles, /\.shell form\s*\{[^}]*grid-template-columns:\s*repeat\(2, minmax\(0, 1fr\)\) auto;/s);
assert.match(styles, /\.shell form input\s*\{[^}]*min-width:\s*0;/s);
});

test('profile includes accessible game-history pagination', () => {
assert.match(profile, /id="history-pagination" aria-label="Game history pages"/);
assert.match(profile, /data-page-action="previous"/);
Expand Down
7 changes: 7 additions & 0 deletions tests/tetris.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,10 @@ test('Tetris preserves elapsed time while bounding simulation steps and ignores
assert.match(app, /event\.defaultPrevented \|\| interactive \|\| document\.querySelector\('dialog\[open\]'\)/);
assert.match(app, /button,a,input,select,textarea,dialog/);
});

test('Tetris keeps phone controls visible alongside the board', () => {
const styles = read('tetris/styles.css');
assert.match(styles, /@media\(max-width:780px\)[^{]*\{[\s\S]*?\.touch-controls\{[^}]*position:fixed;[^}]*bottom:max\(8px,env\(safe-area-inset-bottom\)\)/);
assert.match(styles, /padding-bottom:calc\(160px \+ env\(safe-area-inset-bottom\)\)/);
assert.match(styles, /\.game-layout \.tetris-stage\{width:min\([^}]*calc\(\(100dvh - 160px - env\(safe-area-inset-bottom\)\)\/2\)\)/);
});
4 changes: 2 additions & 2 deletions tetris/styles.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading