neser 1.2.0

NESER - Nintendo Emulation Systems Engine (Rust). Desktop and WebAssembly frontends.
Documentation
# NESER Web (WASM)

## Prerequisites
- Rust toolchain with `wasm32-unknown-unknown` target (`rustup target add wasm32-unknown-unknown`)
- `wasm-bindgen-cli` (or `wasm-pack`) installed, or use `cargo install wasm-bindgen-cli`
- Node.js 20+ with npm

## Build
```bash
# 1. Build WASM
cargo build --release --target wasm32-unknown-unknown --no-default-features --features wasm
wasm-bindgen target/wasm32-unknown-unknown/release/neser.wasm --out-dir web/pkg --target web --omit-default-module-path --no-typescript

# 2. Bundle with Vite (outputs to dist/)
npx vite build

# Or use the convenience script which does both:
bash scripts/build_web.sh
```

## Run locally
```bash
# Development server with hot reload
npm run dev

# Or production preview (after build)
bash scripts/run_web.sh
# then open http://localhost:8000 in your browser
```

## Testing
```bash
# Unit tests (Vitest)
npm test

# Integration tests (Playwright)
npm run test:integration:web
```

## GBA performance benchmark
Build the WASM package, copy a local GBA ROM into `web/roms/`, start the dev
server, then open the benchmark page:

```bash
bash scripts/build_web.sh
cp roms/games/metroid-zero-mission.gba web/roms/
npm run dev
# open http://localhost:8000/gba-bench.html?rom=metroid-zero-mission.gba
```

Optional query parameters:
- `frames` (default `600`)
- `warmup` (default `60`)
- `stabilityRuns` (default `5`)
- `includeBiosIntro=true` to benchmark from power-on instead of skipping BIOS intro
- `continueStabilityRuns=true` to keep advancing instead of reloading the ROM
  before each stability run

## Project structure
```
web/
├── index.html              # Entry point
├── gba-bench.html          # GBA WASM frame benchmark
├── styles.css              # Custom styles
├── pkg/                    # Generated WASM artifacts (git-ignored)
├── src/                    # Application modules
│   ├── app.js              # Main entry point
│   ├── audio/              # Audio resampling, frame timing
│   ├── benchmark/          # Benchmark parsing and frame statistics
│   ├── debugger/           # Debugger UI components
│   ├── display/            # Canvas, zoom, cursor management
│   ├── input/              # Gamepad, mouse, keyboard input
│   ├── rom/                # ROM loading, autorun
│   ├── save-state/         # Save/load state management
│   ├── shortcuts/          # Keyboard shortcuts
│   └── ui/                 # Toast overlays, sine scroller
└── integration/            # Playwright e2e tests
```
- Optional gamepad input uses the Gamepad API (toggle in UI).
- Audio is supported via Web Audio.
- Rendering runs on the browser main thread; heavy frames or slow hosts can briefly block UI/event handling.