diff --git a/.claude/rules/bt-api-getters.md b/.claude/rules/bt-api-getters.md index cdfaa23..110c8a6 100644 --- a/.claude/rules/bt-api-getters.md +++ b/.claude/rules/bt-api-getters.md @@ -11,7 +11,7 @@ Quick rules when changing `src/BlitTech.ts` or demos: ## Getter list | Category | Members | -| --- | --- | +| - | - | | Configure-time (mirror `HardwareSettings` names) | `displaySize`, `canvasDisplaySize`, `targetFPS`, `outputSize` | | Loop timing | `deltaSeconds`, `timeSeconds`, `ticks` | | Runtime state | `activeBackend`, `camera`, `palette` | @@ -19,7 +19,7 @@ Quick rules when changing `src/BlitTech.ts` or demos: `outputSize` = effective buffer (`canvasDisplaySize ?? displaySize`). `Vector2i` getters return a clone per read. `activeBackend` is what actually started after fallback, not `configure().renderer`. -`palette` is a live reference — mutating slots affects rendering on the next frame. +`palette` is a live reference - mutating slots affects rendering on the next frame. ## Naming when adding getters diff --git a/.claude/skills/perf/SKILL.md b/.claude/skills/perf/SKILL.md index 8109b72..67eccfb 100644 --- a/.claude/skills/perf/SKILL.md +++ b/.claude/skills/perf/SKILL.md @@ -10,7 +10,7 @@ Use this skill when the task involves: - benchmarking a new hot method or allocation pattern - working on benchmark CI behavior -For visual correctness verification (not performance), use `pnpm test:visual` — see the Visual Regression Tests section +For visual correctness verification (not performance), use `pnpm test:visual` - see the Visual Regression Tests section in CLAUDE.md. ## CPU Benchmarks diff --git a/.claude/skills/spellcheck/SKILL.md b/.claude/skills/spellcheck/SKILL.md index 15909b8..f579f83 100644 --- a/.claude/skills/spellcheck/SKILL.md +++ b/.claude/skills/spellcheck/SKILL.md @@ -19,8 +19,8 @@ Run the project-wide spellcheck, then fix all reported errors. - Capture the full error output 2. **Analyze each error** For every word flagged by cspell, determine if it is: - - **A typo** – a misspelled word in source code, comments, strings, or content - - **A legitimate term** – a technical term, brand name, abbreviation, or proper noun that cspell doesn’t know + - **A typo** - a misspelled word in source code, comments, strings, or content + - **A legitimate term** - a technical term, brand name, abbreviation, or proper noun that cspell doesn’t know 3. **Fix typos in source files** - Open the file and fix the misspelled word in place diff --git a/CLAUDE.md b/CLAUDE.md index f3c1736..c6fa1ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -241,7 +241,7 @@ pnpm bench:json # Run benchmarks and write benchmark-results.json ### Visual Regression Tests `pnpm test:visual` runs Playwright with Chromium + WebGPU and captures PNG snapshots of actual rendered frames. This is -the primary tool for verifying that visual output is correct — not performance, but pixel-level correctness. +the primary tool for verifying that visual output is correct - not performance, but pixel-level correctness. Use it when implementing or changing: @@ -276,7 +276,7 @@ Use the benchmark system when the user asks about performance, throughput, regre coverage. - Use **CPU benchmarks** for isolated methods, helpers, caches, and allocation patterns -- For rendering correctness, use visual regression tests (`pnpm test:visual`) — they produce PNG snapshots +- For rendering correctness, use visual regression tests (`pnpm test:visual`) - they produce PNG snapshots Recommended commands: diff --git a/README.md b/README.md index 66dac37..2773161 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ with palette indices, animate with palette cycling and fades, and ship authentic ## Inspiration Blit-Tech draws heavy inspiration from [RetroBlit](https://www.badcastle.com/retroblit/docs/doc/index.html) by Martin -Cietwierkowski ([@daafu](https://github.com/daafu)) — a retro pixel demo framework for Unity that replaces the editor +Cietwierkowski ([@daafu](https://github.com/daafu)) - a retro pixel demo framework for Unity that replaces the editor with a clean, low-level demo loop. Blit-Tech brings the same philosophy to the web using WebGPU: no scene graphs, no complex frameworks, just sprites, primitives, and fonts. @@ -26,7 +26,7 @@ complex frameworks, just sprites, primitives, and fonts. - **Palette offset variants**: recolor one sprite sheet into team colors, states, or themes without duplicate textures - **Performance-first data model**: tiny palette uploads (4 KB), smaller sprite textures, and compact primitive vertices - **WebGPU rendering** with dual-pipeline architecture (primitives + sprites); automatic Canvas 2D software fallback -- **Post-process effects**: two-tier system — pixel tier on the `r8uint` index framebuffer; display tier on upscaled +- **Post-process effects**: two-tier system - pixel tier on the `r8uint` index framebuffer; display tier on upscaled RGBA; bundled CRT presets - **Primitive drawing**: pixels, lines, rectangles (outline and filled) - **Sprite system**: palette-indexed textures, palette offset, automatic texture batching @@ -56,7 +56,7 @@ complex frameworks, just sprites, primitives, and fonts. - A **WebGPU-compatible browser** (the engine falls back to Canvas 2D software rendering when WebGPU is unavailable): - Chrome/Edge 113+ (Windows, macOS, Linux, Android) - Firefox 141+ on Windows; 145+/147+ on macOS; Nightly on Linux and Android - - Safari 26+ (macOS Tahoe / iOS 26); or Safari 18–25 with WebGPU enabled via Feature Flags + - Safari 26+ (macOS Tahoe / iOS 26); or Safari 18-25 with WebGPU enabled via Feature Flags **App toolchain** @@ -176,7 +176,7 @@ WebGPU support varies by browser: | ----------- | -------------- | ---------------------------------------------------------------- | | Chrome/Edge | 113+ | Enabled by default | | Firefox | 141+ (Windows) | Enabled by default; 145+/147+ on macOS; Nightly on Linux/Android | -| Safari | 26+ | Enabled by default; Safari 18–25 available via Feature Flags | +| Safari | 26+ | Enabled by default; Safari 18-25 available via Feature Flags | When WebGPU is unavailable the engine falls back to the Canvas 2D software renderer automatically. A dismissible in-canvas "SOFTWARE RENDERER" banner appears to confirm the fallback is active. Use `BT.activeBackend` to detect which diff --git a/docs/api-assets.md b/docs/api-assets.md index 6b3d477..bd11ba0 100644 --- a/docs/api-assets.md +++ b/docs/api-assets.md @@ -47,7 +47,7 @@ if (AssetLoader.isLoaded('sprites.png')) { --- -## Sprite Setup — Preferred Path +## Sprite Setup - Preferred Path Use `SpriteSheet.loadIndexed()` for all standard sprite setup. It combines color registration, image loading, and palette indexization in one call. @@ -60,7 +60,7 @@ const palette = new Palette(256); const indexed = await SpriteSheet.loadIndexed( 'sprites/hero.png', // URL palette, // palette to populate - 10, // startSlot — first palette slot to write colors into + 10, // startSlot - first palette slot to write colors into { sort: 'luminance' }, // optional: color order in palette ('luminance' | 'none') ); @@ -69,16 +69,16 @@ BT.paletteSet(palette); // activate AFTER loadIndexed returns // Draw using the returned sheet and source rectangle: BT.drawSprite(indexed.sheet, indexed.srcRect, new Vector2i(20, 20)); -// indexed.colors — list of Color32 values registered into the palette -// indexed.srcRect — Rect2i spanning the full image +// indexed.colors - list of Color32 values registered into the palette +// indexed.srcRect - Rect2i spanning the full image ``` Colors are sorted by perceived luminance (darkest-first) by default. Pass `{ sort: 'none' }` to preserve row-major scan -order. Slot 0 is never touched — transparent pixels in the image map to slot 0 at draw time. +order. Slot 0 is never touched - transparent pixels in the image map to slot 0 at draw time. --- -## Sprite Setup — Manual Path +## Sprite Setup - Manual Path Use this only when you need fine-grained control over the palette layout or want to load several sheets into the same palette sequentially. @@ -138,7 +138,7 @@ See [Bitmap Fonts Guide](bitmap-fonts.md) for the `.btfont` format specification ## System Font -A built-in 6×14 monospace font covering printable ASCII (characters 32–126). No load step needed. +A built-in 6×14 monospace font covering printable ASCII (characters 32-126). No load step needed. ```ts BT.systemPrint(new Vector2i(10, 10), paletteIndex, 'Score: 100'); diff --git a/docs/api-core.md b/docs/api-core.md index f7b5657..927a6c6 100644 --- a/docs/api-core.md +++ b/docs/api-core.md @@ -12,7 +12,7 @@ The `bootstrap()` function is the recommended entry point. It handles DOM ready, ```ts import { bootstrap, type BootstrapOptions } from 'blit-tech'; -// One-liner — canvas id defaults to 'blit-tech-canvas', container to 'canvas-container' +// One-liner - canvas id defaults to 'blit-tech-canvas', container to 'canvas-container' bootstrap(MyDemo); // With options @@ -31,8 +31,8 @@ bootstrap(MyDemo, { | ----------------- | ------------------------ | -------------------- | ------------------------------ | | `canvasId` | `string` | `'blit-tech-canvas'` | Canvas element id | | `containerId` | `string` | `'canvas-container'` | Container id for error display | -| `onSuccess` | `() => void` | — | Called after successful init | -| `onError` | `(error: Error) => void` | — | Called on any init failure | +| `onSuccess` | `() => void` | - | Called after successful init | +| `onError` | `(error: Error) => void` | - | Called on any init failure | | `waitForDOMReady` | `boolean` | `true` | Wait for `DOMContentLoaded` | **Manual utilities** (for custom initialization flows): @@ -50,10 +50,10 @@ displayError('Init Failed', 'WebGPU unavailable.', 'my-container'); ```ts const ok = await BT.init(demo, canvas); // low-level init; prefer bootstrap() -BT.displaySize; // Vector2i — configured logical resolution (clone per read) -BT.canvasDisplaySize; // Vector2i | null — output buffer when set in configure() -BT.outputSize; // Vector2i — effective drawing-buffer size (clone per read) -BT.targetFPS; // number — target updates per second +BT.displaySize; // Vector2i - configured logical resolution (clone per read) +BT.canvasDisplaySize; // Vector2i | null - output buffer when set in configure() +BT.outputSize; // Vector2i - effective drawing-buffer size (clone per read) +BT.targetFPS; // number - target updates per second BT.activeBackend; // 'webgpu' | 'software' | null ``` @@ -118,11 +118,11 @@ specific snapshot; the default is the engine tick counter. ## Camera -The camera applies a global pixel offset to all subsequent draw calls. Integer only — pass `Vector2i`, never floats. +The camera applies a global pixel offset to all subsequent draw calls. Integer only - pass `Vector2i`, never floats. ```ts BT.cameraSet(new Vector2i(scrollX, scrollY)); // apply offset -BT.camera; // Vector2i — current offset +BT.camera; // Vector2i - current offset BT.cameraReset(); // set back to (0, 0) // Clamp a camera origin so the viewport stays within a world: @@ -147,8 +147,8 @@ v.width; v.height; // aliases for x / y (useful when treating the vector as a size) // Static factories -Vector2i.zero(); // (0, 0) — cached frozen singleton -Vector2i.one(); // (1, 1) — cached frozen singleton +Vector2i.zero(); // (0, 0) - cached frozen singleton +Vector2i.one(); // (1, 1) - cached frozen singleton Vector2i.up(); // (0, -1) Vector2i.down(); // (0, 1) Vector2i.left(); // (-1, 0) @@ -174,16 +174,16 @@ Integer rectangle with `x, y, width, height` fields. const r = new Rect2i(x, y, width, height); // Static factories -Rect2i.zero(); // (0,0,0,0) — cached frozen singleton +Rect2i.zero(); // (0,0,0,0) - cached frozen singleton Rect2i.fromMinMax(min, max); // from corner vectors Rect2i.fromMinMaxXY(minX, minY, maxX, maxY); // zero-allocation variant Rect2i.fromCenterSize(center, size); // centered rectangle Rect2i.fromCenterSizeXY(cx, cy, w, h); // zero-allocation variant // Instance methods -r.contains(point); // boolean — point inside rect -r.intersects(other); // boolean — rects overlap -r.intersection(other); // Rect2i | null — overlap area +r.contains(point); // boolean - point inside rect +r.intersects(other); // boolean - rects overlap +r.intersection(other); // Rect2i | null - overlap area r.center; // Vector2i getter r.min; // Vector2i getter (top-left) r.max; // Vector2i getter (bottom-right) @@ -191,7 +191,7 @@ r.max; // Vector2i getter (bottom-right) ### Color32 -32-bit RGBA color (channels 0–255). +32-bit RGBA color (channels 0-255). ```ts const c = new Color32(r, g, b, a); @@ -200,7 +200,7 @@ const c = new Color32(r, g, b, a); Color32.white Color32.black Color32.transparent Color32.red Color32.green Color32.blue Color32.yellow Color32.cyan Color32.magenta -Color32.gray(value) // grayscale, value 0–255 +Color32.gray(value) // grayscale, value 0-255 // Hex parsing Color32.fromHex('#ff8800') // returns Color32 diff --git a/docs/api-palette.md b/docs/api-palette.md index 82fd82b..938ffe3 100644 --- a/docs/api-palette.md +++ b/docs/api-palette.md @@ -17,7 +17,7 @@ const palette = BT.paletteCreate(256); // equivalent BT-namespace shorthand // Set and get colors palette.set(1, new Color32(255, 0, 0, 255)); // red at slot 1 palette.get(1); // → Color32 (defensive copy) -palette.getRef(1); // → Color32 (live reference — do not store) +palette.getRef(1); // → Color32 (live reference - do not store) // Activate for rendering BT.paletteSet(palette); @@ -46,8 +46,8 @@ Palette.nes(); // NES 64-color Fills six consecutive UI-purpose slots into an existing palette and registers `hud_*` name aliases. ```ts -palette.applyHUD(); // fills slots 1–6 (default startSlot = 1) -palette.applyHUD(10); // fills slots 10–15 +palette.applyHUD(); // fills slots 1-6 (default startSlot = 1) +palette.applyHUD(10); // fills slots 10-15 BT.paletteSet(palette); ``` @@ -109,7 +109,7 @@ const slot = palette.findColor(color); // → index, or -1 if not found Animated effects run automatically each frame in the engine's end-of-frame pass (after `demo.render()`, before the GPU upload). Multiple effects can run simultaneously on different palette ranges and will not conflict. Effects process via -the `dirty` flag — no polling needed. +the `dirty` flag - no polling needed. ```ts // Cycle a range of slots (water, fire, plasma) @@ -152,7 +152,7 @@ Used by `paletteFade` and `paletteFadeRange`. Type: `EasingFunction`. ## Timing Note Effects are applied after `demo.render()` but before the GPU palette upload in `Renderer.endFrame()`. This means user -draw calls and palette effects see the same consistent snapshot within a frame — they never interleave mid-frame. +draw calls and palette effects see the same consistent snapshot within a frame - they never interleave mid-frame. Effects that auto-remove (fade, flash) clean up when their duration elapses. `paletteCycle` runs indefinitely until `paletteClearEffects()` is called. diff --git a/docs/api-rendering.md b/docs/api-rendering.md index d3d799f..1705815 100644 --- a/docs/api-rendering.md +++ b/docs/api-rendering.md @@ -3,7 +3,7 @@ Primitives, sprites, text, post-process effects, and frame capture. All draw calls require a palette to be active (`BT.paletteSet(palette)` before the first `BT.drawSprite`, -`BT.drawRectFill`, etc.). All coordinates are integer pixels — use `Vector2i` and `Rect2i`, never floats. +`BT.drawRectFill`, etc.). All coordinates are integer pixels - use `Vector2i` and `Rect2i`, never floats. --- @@ -21,7 +21,7 @@ BT.drawRect(rect, paletteIndex); // outline only BT.drawRectFill(rect, paletteIndex); // filled ``` -All primitives write palette indices, not RGBA — the active palette resolves colors at frame end. +All primitives write palette indices, not RGBA - the active palette resolves colors at frame end. --- @@ -34,10 +34,10 @@ BT.drawSprite(sheet, srcRect, destPos); BT.drawSprite(sheet, srcRect, destPos, paletteOffset); ``` -- `sheet` — an indexed `SpriteSheet` (must have been prepared via `loadIndexed` or `indexize`). -- `srcRect` — source region within the sheet in pixels. -- `destPos` — top-left destination in display coordinates. -- `paletteOffset` — shift added to every stored pixel index before palette lookup (default `0`). +- `sheet` - an indexed `SpriteSheet` (must have been prepared via `loadIndexed` or `indexize`). +- `srcRect` - source region within the sheet in pixels. +- `destPos` - top-left destination in display coordinates. +- `paletteOffset` - shift added to every stored pixel index before palette lookup (default `0`). **Palette offset semantics:** Stored sprite indices start at `1` (index `0` is always transparent and discarded). With `paletteOffset = N`, a pixel stored at index `1` renders as `palette[1 + N]`, a pixel at index `2` renders as @@ -70,7 +70,7 @@ BT.paletteSet(newLayoutPalette); BT.spritesRefresh(); // re-maps all tracked sheets to the new slot positions ``` -Call `spritesRefresh()` only after a **palette-layout swap** — when the same colors have moved to different slot +Call `spritesRefresh()` only after a **palette-layout swap** - when the same colors have moved to different slot indices. Do NOT call it after a palette-value swap (when you changed what color a slot holds). In the value-swap case the fragment shader picks up the new color automatically; calling `spritesRefresh()` is wasteful and will fail reindexing if original RGBA values are gone. @@ -81,7 +81,7 @@ reindexing if original RGBA values are gone. ### System font -Built-in 6×14 monospace font covering printable ASCII (characters 32–126). +Built-in 6×14 monospace font covering printable ASCII (characters 32-126). ```ts BT.systemPrint(pos, paletteIndex, text); // draw text at pos @@ -106,18 +106,18 @@ See [Bitmap Fonts Guide](bitmap-fonts.md) for the `.btfont` format spec and BMFo Two-tier fullscreen pipeline running between scene render and swap-chain present: -1. **Pixel tier** — operates on the logical `r8uint` framebuffer (one palette index per pixel). Effects here stay +1. **Pixel tier** - operates on the logical `r8uint` framebuffer (one palette index per pixel). Effects here stay palette-native (chunky glitch, mosaic). -2. **Palette resolve + upscale** — `PaletteResolveUpscalePass` converts indices to RGBA through the active palette LUT +2. **Palette resolve + upscale** - `PaletteResolveUpscalePass` converts indices to RGBA through the active palette LUT and upscales to `canvasDisplaySize`. -3. **Display tier** — operates on the RGBA output image. Hosts CRT scanlines, barrel distortion, bloom, etc. Requires +3. **Display tier** - operates on the RGBA output image. Hosts CRT scanlines, barrel distortion, bloom, etc. Requires `canvasDisplaySize` in hardware settings. -Both chains add zero cost when empty. Post-process is unsupported by the Canvas 2D software backend — calling +Both chains add zero cost when empty. Post-process is unsupported by the Canvas 2D software backend - calling `effectAdd` in software mode throws a clear error. ```ts -// Add effect — routed to pixel or display chain by Effect.tier automatically +// Add effect - routed to pixel or display chain by Effect.tier automatically BT.effectAdd(new BarrelDistortion()); BT.effectAdd(new Scanlines()); BT.effectAdd(new Bloom()); diff --git a/docs/bitmap-fonts.md b/docs/bitmap-fonts.md index 38f6894..a3eb26d 100644 --- a/docs/bitmap-fonts.md +++ b/docs/bitmap-fonts.md @@ -229,9 +229,9 @@ If you prefer to convert manually: 3. **Export with padding** between characters to prevent bleeding artifacts. -4. **Use white glyphs** on a transparent background – colors are applied at render time via tinting. +4. **Use white glyphs** on a transparent background - colors are applied at render time via tinting. -5. **Include common symbols** beyond ASCII: `×`, `÷`, `·`, `—`, `…`, etc. +5. **Include common symbols** beyond ASCII: `×`, `÷`, `·`, `-`, `…`, etc. ## API Reference diff --git a/docs/developer-experience-guide.md b/docs/developer-experience-guide.md index 2c3cf57..e794edd 100644 --- a/docs/developer-experience-guide.md +++ b/docs/developer-experience-guide.md @@ -143,7 +143,7 @@ AI-assisted commits add a trailer: `Co-Authored-By: Claude `) -vs RGBA paths — see [Writing a custom effect](#writing-a-custom-effect) below. +vs RGBA paths - see [Writing a custom effect](#writing-a-custom-effect) below. ### `HardwareSettings` @@ -160,7 +160,7 @@ custom `configure()` that omits output sizing. ## Pixel-tier effects -### `PixelGlitch` — chunky band shift +### `PixelGlitch` - chunky band shift Per-row horizontal glitch: every Nth row of source **palette indices** gets a random horizontal shift in index space (integer texel steps), so output stays on valid palette slots without RGB remapping. @@ -171,7 +171,7 @@ Per-row horizontal glitch: every Nth row of source **palette indices** gets a ra | `bandHeight` | `4` | Height of each glitch band in source pixels | | `seed` | `0` | Per-glitch seed; change between bursts to vary the band noise | -### `PixelMosaic` — block down-quantize +### `PixelMosaic` - block down-quantize Replaces each `blockSize x blockSize` group of source pixels with a single sample. Useful for transitions, dream sequences, and "low-res mode" effects. @@ -184,16 +184,16 @@ sequences, and "low-res mode" effects. ## Display-tier effects -### `BarrelDistortion` — pincushion curve +### `BarrelDistortion` - pincushion curve `warp(uv) = uv + delta * d2 * curvature`. Operates at output resolution so the curve has enough pixels to express -smoothly — no stepping artifacts on diagonals. +smoothly - no stepping artifacts on diagonals. | Field | Default | Purpose | | ----------- | ------- | ------------------------------------------------------------------- | | `curvature` | `0.05` | Curve strength. `0.02` flat panel, `0.05` desktop, `0.10` pocket TV | -### `Scanlines` — bright/dark horizontal bands +### `Scanlines` - bright/dark horizontal bands Gaussian-weighted scanline pattern matched to source pixel rows. @@ -203,7 +203,7 @@ Gaussian-weighted scanline pattern matched to source pixel rows. | `strength` | `-8` | Negative gaussian falloff; more negative = sharper bands | | `density` | `240` | Cycles per view (set to your logical vertical resolution) | -### `RGBMask` — CRT shadow mask +### `RGBMask` - CRT shadow mask R/G/B vertical-stripe pattern with darkened cell borders, simulating an aperture-grille CRT. @@ -213,7 +213,7 @@ R/G/B vertical-stripe pattern with darkened cell borders, simulating an aperture | `size` | `6` | Mask cell pitch in source pixels | | `border` | `0.5` | Border darkening within each cell | -### `Vignette` — edge darkening +### `Vignette` - edge darkening Smooth radial fade. `pow(edge.x * edge.y, amount)`. @@ -221,7 +221,7 @@ Smooth radial fade. `pow(edge.x * edge.y, amount)`. | -------- | ------- | ----------------------------------------------- | | `amount` | `0.35` | Darkening exponent. Higher = stronger / sharper | -### `ChromaticAberration` — RGB channel offset +### `ChromaticAberration` - RGB channel offset Red samples left of the fragment, blue samples right. Cheap CRT optics produce a tiny version of this naturally. @@ -229,7 +229,7 @@ Red samples left of the fragment, blue samples right. Cheap CRT optics produce a | ------------ | ------- | ------------------------------- | | `aberration` | `1.0` | Channel offset in source pixels | -### `Flicker` — brightness multiplier +### `Flicker` - brightness multiplier The simplest CRT animation knob. `color *= amount`. Demo drives it per-frame. @@ -237,7 +237,7 @@ The simplest CRT animation knob. `color *= amount`. Demo drives it per-frame. | -------- | ------- | ------------------------------------ | | `amount` | `1.0` | Brightness multiplier. 1 unmodulated | -### `RollLine` — scrolling interference band +### `RollLine` - scrolling interference band A horizontal bright stripe slowly scrolls down the screen. @@ -247,7 +247,7 @@ A horizontal bright stripe slowly scrolls down the screen. | `speed` | `1.0` | Scroll velocity multiplier | | `time` | `0` | Wall-clock seconds; demos drive this each frame | -### `Interference` — per-row analog jitter +### `Interference` - per-row analog jitter Each row gets a random horizontal offset reseeded each frame. @@ -256,7 +256,7 @@ Each row gets a random horizontal offset reseeded each frame. | `amount` | `0.06` | Maximum offset as a UV fraction | | `time` | `0` | Wall-clock seconds; reseeds each frame | -### `Noise` — additive pseudo-random noise +### `Noise` - additive pseudo-random noise Per-pixel film grain. Reseeds each frame from `time`. @@ -265,7 +265,7 @@ Per-pixel film grain. Reseeds each frame from `time`. | `amount` | `0.025` | Noise amplitude as `[-amount, +amount]` perturbation | | `time` | `0` | Wall-clock seconds | -### `Bloom` — soft phosphor glow +### `Bloom` - soft phosphor glow Single-pass 5x5 box blur (25 taps) mixed with the original color. @@ -292,7 +292,7 @@ Demos that want the full kitchen-sink effect should use this. ### `BT.preset.amber()` -Amber monochrome PC monitor (think IBM 5151 / Hercules). Currently ships as a parameter-only set — the actual amber tint +Amber monochrome PC monitor (think IBM 5151 / Hercules). Currently ships as a parameter-only set - the actual amber tint quantization will land with [VV-479](https://linear.app/vancura/issue/VV-479/monochrome-re-quantization-display-effect). ### `BT.preset.green()` @@ -358,9 +358,9 @@ Extend `FullscreenPixelEffect` for logical `r8uint` chains. Provide **both** `fr format is ever passed to `init`; keep it minimal). Implement `writeUniforms` like display-tier effects. - Integer clears use index `0`; treat palette slot `0` as transparent where appropriate. -- For data-dependent control flow that calls `textureSample`, switch to `textureSampleLevel(..., 0.0)` — WGSL forbids +- For data-dependent control flow that calls `textureSample`, switch to `textureSampleLevel(..., 0.0)` - WGSL forbids `textureSample` outside uniform control flow because of mip derivative requirements. -- Reuse the inherited `uniformData` `Float32Array` — never allocate per frame. +- Reuse the inherited `uniformData` `Float32Array` - never allocate per frame. --- @@ -396,7 +396,7 @@ codebase derive from the same shader. The glitch / flicker / roll-line / chromatic-aberration extensions and the uniform set the pre-decomposition `PipBoyEffect` replicated come from a community PipBoy fork written for p5.js's `createFilterShader`. The fork's source -URL and author are **unknown** to us at the time of writing, and **no license has been confirmed** — the only header it +URL and author are **unknown** to us at the time of writing, and **no license has been confirmed** - the only header it carried was a Fallout-themed "RobCo Industries (Unlicensed Wasteland Fork)" comment, which is character flavour rather than a license grant. Treat the upstream provenance as unverified; do not assume permissive rights for the borrowed extensions until the upstream source and license have been identified. diff --git a/docs/testing.md b/docs/testing.md index 844ee55..588cc9b 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -64,12 +64,12 @@ documented in [Performance Testing](performance-testing.md). Public types are rolled up during `pnpm build` via `vite-plugin-dts` and API Extractor. The workspace pins TypeScript to the same version API Extractor bundles (see `docs/developer-experience-guide.md`). -- **`pnpm test:declarations`** — Node test runner for `scripts/check-declaration-tooling.mjs` (drift patterns and +- **`pnpm test:declarations`** - Node test runner for `scripts/check-declaration-tooling.mjs` (drift patterns and alignment log parsing). Included in `pnpm preflight`. -- **CI** — after `pnpm build`, `node scripts/check-declaration-tooling.mjs build.log` runs in both +- **CI** - after `pnpm build`, `node scripts/check-declaration-tooling.mjs build.log` runs in both `.github/workflows/ci.yml` (build-library job) and `.github/workflows/pr-checks.yml` (bundle-size job) to fail on drift warnings and version mismatch. -- **Manual** — `pnpm build 2>&1 | tee build.log && node scripts/check-declaration-tooling.mjs build.log` +- **Manual** - `pnpm build 2>&1 | tee build.log && node scripts/check-declaration-tooling.mjs build.log` ## Commands diff --git a/docs/tooling.md b/docs/tooling.md index 511b6df..2decd71 100644 --- a/docs/tooling.md +++ b/docs/tooling.md @@ -23,7 +23,7 @@ CI details. CI runs the checker after `pnpm build` in: -- `.github/workflows/ci.yml` — `build-library` job -- `.github/workflows/pr-checks.yml` — `bundle-size` job +- `.github/workflows/ci.yml` - `build-library` job +- `.github/workflows/pr-checks.yml` - `bundle-size` job -More context: [Testing — Declaration tooling checks](testing.md#declaration-tooling-checks). +More context: [Testing - Declaration tooling checks](testing.md#declaration-tooling-checks). diff --git a/docs/voice.md b/docs/voice.md index 354ba4e..fed3888 100644 --- a/docs/voice.md +++ b/docs/voice.md @@ -12,7 +12,7 @@ read the relevant section below. ## Two Audience Tiers -### Tier 1 — User-facing (canvas-visible or public API errors) +### Tier 1 - User-facing (canvas-visible or public API errors) Audience: demo authors using the `BT` namespace, beginners who may not know TypeScript internals. @@ -30,14 +30,14 @@ Where it appears: `showBeginnerRuntimeError()`, `displayError()`, canvas banners API path (anything reachable from demo code via `BT.*` or a public constructor). All such messages must live in `src/utils/errorMessages.ts`. -### Tier 2 — Internal invariants (developer-only) +### Tier 2 - Internal invariants (developer-only) Audience: engine contributors reading a stack trace. Rules: - Terse. Include the relevant values. -- No need to explain next steps — the reader can read the source. +- No need to explain next steps - the reader can read the source. - Still no emoji. Where it appears: `throw new Error()` inside private engine methods, guard checks inside `src/core/`, `src/render/`, and @@ -68,7 +68,7 @@ non-public `src/assets/` paths. - Use `console.error` for failures, `console.warn` for degraded-but-continuing states. - Never use `console.log` in production code paths (benchmarks and debug utilities excepted). - Canvas-visible errors (`displayError`) and console output must say the same thing. Do not show a terse console message - and a friendly canvas message — they must match. + and a friendly canvas message - they must match. --- @@ -87,7 +87,7 @@ Steps when adding a new Tier 1 message: 2. Import and call it at the throw or display site. 3. Do not inline the string at the call site. -Tier 2 strings (internal invariants) may be inlined — they are never user-visible and do not need the same stability +Tier 2 strings (internal invariants) may be inlined - they are never user-visible and do not need the same stability guarantees. --- @@ -97,7 +97,7 @@ guarantees. 1. **Plain English?** Could a junior developer understand it without reading engine source? 2. **Next action?** Does it tell the reader what to do, not just what failed? 3. **No trailing period?** Thrown strings land mid-sentence in stack traces. -4. **No emoji?** Nowhere in the engine — not in throws, not in banners, not in console output. +4. **No emoji?** Nowhere in the engine - not in throws, not in banners, not in console output. 5. **Sentence case?** "Slot 0 is always transparent" not "Slot 0 Is Always Transparent". 6. **Centralized?** Is the string in `errorMessages.ts`, or must it go there? 7. **Tier correct?** Is this reachable from demo code? If yes, it must be Tier 1. diff --git a/src/BlitTech.ts b/src/BlitTech.ts index de24f12..2eef7dd 100644 --- a/src/BlitTech.ts +++ b/src/BlitTech.ts @@ -186,7 +186,7 @@ function executeDrawCall(methodName: string, callback: () => void): void { /** * Returns runtime keyboard `KeyboardEvent.code` list for a face button and player, - * or `null` when there is no keyboard fallback (e.g. players 2–3 for face buttons). + * or `null` when there is no keyboard fallback (e.g. players 2-3 for face buttons). * * @param button - Face button constant (`BT.BTN_UP` … `BT.BTN_SELECT`). * @param player - Zero-based player index. @@ -503,7 +503,7 @@ export const BT = { * Renderer backend that is currently active. * * `'webgpu'` or `'software'` after successful init; `null` before init or on failure. - * Useful when adjusting demo behavior — for example, skipping post-process effects + * Useful when adjusting demo behavior - for example, skipping post-process effects * that only work under WebGPU. * * @returns `'webgpu'` or `'software'` after successful init; `null` before init or on failure. @@ -531,7 +531,7 @@ export const BT = { * * **Palette-value swap (change what a slot looks like):** mutate the current * palette with `palette.set(slot, newColor)` and then call `paletteSet()`. The - * sprite sheets' stored indices are unchanged — no {@link BT.spritesRefresh} + * sprite sheets' stored indices are unchanged - no {@link BT.spritesRefresh} * needed. * * **Palette-layout swap (same colors, different slot positions):** build a new @@ -554,7 +554,7 @@ export const BT = { }, /** - * Active engine palette (live reference — not a copy). + * Active engine palette (live reference - not a copy). * * Mutating slots updates colors on the next frame without {@link BT.paletteSet}. * @@ -1428,7 +1428,7 @@ export const BT = { * **Out-of-range behavior:** No CPU-side validation is performed. `paletteOffset` is passed to * the GPU as a `u32`. If `storedIndex + paletteOffset` exceeds the last palette index, WebGPU's * robust buffer access returns 0 for every component; because the fragment shader forces alpha - * to 1.0, the affected pixels render as opaque black. Negative values are forbidden — a negative + * to 1.0, the affected pixels render as opaque black. Negative values are forbidden - a negative * JS number written into a `u32` vertex attribute wraps to a large unsigned integer, which also * produces out-of-bounds black pixels. * @@ -1455,7 +1455,7 @@ export const BT = { /** * Re-indexizes all tracked sprite sheets against the current active palette. * - * Only call this after a **palette-layout swap** — when the same colors have + * Only call this after a **palette-layout swap** - when the same colors have * moved to different slot positions and existing sprite indices now point to * the wrong slots. Each sheet re-runs exact RGBA-to-index matching against the * active palette via `SpriteSheet.reindexize()`. If any opaque pixel's original @@ -1465,7 +1465,7 @@ export const BT = { * * **Do not call this after a palette-value swap.** If you changed what color a * slot holds (e.g. palette animation, theme tinting), the stored indices are - * still correct — the fragment shader picks up the new color automatically. + * still correct - the fragment shader picks up the new color automatically. * Calling `spritesRefresh()` in that case is wasteful at best; at worst, if the * original RGBA values are gone from the palette, sheets with missing colors * will fail reindexing and be removed from the registry. diff --git a/src/__test__/webgpu-mock.ts b/src/__test__/webgpu-mock.ts index f109ac9..26a854d 100644 --- a/src/__test__/webgpu-mock.ts +++ b/src/__test__/webgpu-mock.ts @@ -140,7 +140,7 @@ export function createMockGPUCanvasContext(): GPUCanvasContext { /** * Creates a mock GPUBuffer matching the real 4096-byte palette uniform layout. - * Returns a plain object — does not call device.createBuffer. + * Returns a plain object - does not call device.createBuffer. * * @returns Mock GPUBuffer stub. */ diff --git a/src/assets/BitmapFont.ts b/src/assets/BitmapFont.ts index 36fa1bb..3474a4d 100644 --- a/src/assets/BitmapFont.ts +++ b/src/assets/BitmapFont.ts @@ -264,7 +264,7 @@ export class BitmapFont { const { data, glyphEntries } = BitmapFont.parseBtfontFile(url, await response.text()); - // Load texture – embedded `data:` URIs and relative PNG paths are both allowed. + // Load texture - embedded `data:` URIs and relative PNG paths are both allowed. const image = await BitmapFont.loadTexture(data.texture, url); const spriteSheet = new SpriteSheet(image); const atlasWidth = spriteSheet.width; @@ -648,7 +648,7 @@ export class BitmapFont { // eslint-disable-next-line security/detect-object-injection -- Index is bounds-checked above glyph = this.asciiGlyphs[code] ?? null; } else { - // Unicode fallback – index is guaranteed valid within loop bounds. + // Unicode fallback - index is guaranteed valid within loop bounds. // eslint-disable-next-line security/detect-object-injection -- Index is within loop bounds const char = text[i]; diff --git a/src/assets/Palette.test.ts b/src/assets/Palette.test.ts index eb1448c..0c5cdc1 100644 --- a/src/assets/Palette.test.ts +++ b/src/assets/Palette.test.ts @@ -59,7 +59,7 @@ describe('Palette', () => { expect(() => palette.set(-1, color)).toThrow('0 or higher'); }); - it('get() returns a defensive copy — mutating the result does not change the stored entry', () => { + it('get() returns a defensive copy - mutating the result does not change the stored entry', () => { const palette = new Palette(16); palette.set(3, new Color32(10, 20, 30, 255)); diff --git a/src/assets/Palette.ts b/src/assets/Palette.ts index 79f680c..a721bea 100644 --- a/src/assets/Palette.ts +++ b/src/assets/Palette.ts @@ -150,7 +150,7 @@ export class Palette { * * Set by {@link set} and {@link copyFrom}. Cleared by {@link clearDirty} after * the renderer has uploaded the updated palette uniform buffer. Not set by the - * constructor — initial upload is always triggered by {@link Renderer.setPalette}. + * constructor - initial upload is always triggered by {@link Renderer.setPalette}. */ private _dirty: boolean = false; @@ -312,12 +312,12 @@ export class Palette { * without hardcoding numbers. * * Default colors (matching common demo usage): - * - `hud_white` — `#ffffff` pure white - * - `hud_bg` — `#1e1428` dark purple background - * - `hud_label` — `#c8c8c8` medium gray labels - * - `hud_header` — `#ffdc64` golden header text - * - `hud_dim` — `#646464` dim gray (FPS, secondary info) - * - `hud_code` — `#6496c8` slate blue code snippets + * - `hud_white` - `#ffffff` pure white + * - `hud_bg` - `#1e1428` dark purple background + * - `hud_label` - `#c8c8c8` medium gray labels + * - `hud_header` - `#ffdc64` golden header text + * - `hud_dim` - `#646464` dim gray (FPS, secondary info) + * - `hud_code` - `#6496c8` slate blue code snippets * * @param startSlot - First palette index to write. Must be a positive integer >= 1 * and leave room for all six entries within the palette size. Defaults to `1`. @@ -507,7 +507,7 @@ export class Palette { * * The renderer checks this flag each frame and re-uploads the palette uniform * buffer when it is set, then calls {@link clearDirty} to reset it. Palette - * animation works automatically — no per-frame {@link BT.paletteSet} required. + * animation works automatically - no per-frame {@link BT.paletteSet} required. * * @returns `true` if any color has been written via {@link set} or {@link copyFrom} * since the last call to {@link clearDirty}. diff --git a/src/assets/SpriteSheet.test.ts b/src/assets/SpriteSheet.test.ts index 88af14d..b1f8ea7 100644 --- a/src/assets/SpriteSheet.test.ts +++ b/src/assets/SpriteSheet.test.ts @@ -198,7 +198,7 @@ describe('SpriteSheet', () => { }); it('maps transparent pixels (alpha=0) to palette index 0', () => { - // All pixels fully transparent — expect no throws and all indices to be 0. + // All pixels fully transparent - expect no throws and all indices to be 0. const palette = new Palette(16); const sheet = new SpriteSheet({ width: 2, height: 2 } as HTMLImageElement); @@ -226,7 +226,7 @@ describe('SpriteSheet', () => { it('throws when an opaque pixel color is not in the palette', () => { const w = 1; const h = 1; - // Solid red pixel — not in an empty palette. + // Solid red pixel - not in an empty palette. const pixels = new Uint8ClampedArray([200, 100, 50, 255]); vi.stubGlobal('OffscreenCanvas', makeOffscreenCanvasMock(pixels, w, h)); @@ -260,7 +260,7 @@ describe('SpriteSheet', () => { sheet.indexize(palette); const first = sheet.getTexture(device); - // Call indexize again — should destroy the existing texture. + // Call indexize again - should destroy the existing texture. sheet.indexize(palette); const second = sheet.getTexture(device); @@ -488,7 +488,7 @@ describe('SpriteSheet', () => { }); it('forces alpha to 255 even when source alpha is partial', async () => { - // Source has alpha=128 — should be stored as 255 to match indexize() lookup. + // Source has alpha=128 - should be stored as 255 to match indexize() lookup. const pixels = new Uint8ClampedArray([200, 100, 50, 128]); stubLoad(pixels, 1, 1); diff --git a/src/assets/SpriteSheet.ts b/src/assets/SpriteSheet.ts index ed2fb54..ce1a108 100644 --- a/src/assets/SpriteSheet.ts +++ b/src/assets/SpriteSheet.ts @@ -157,7 +157,7 @@ export class SpriteSheet { * Walks a PNG's pixels and registers every unique opaque color into the * supplied palette starting at `startSlot`. * - * Pixels with alpha 0 are skipped — they map to the engine's transparent + * Pixels with alpha 0 are skipped - they map to the engine's transparent * sentinel slot 0 at draw time. Opaque pixels are deduplicated on RGB and * stored with alpha forced to 255, matching the lookup performed by * `indexize()` so a subsequent `sheet.indexize(palette)` call resolves @@ -266,7 +266,7 @@ export class SpriteSheet { /** * Creates a sprite sheet from pre-computed palette-indexed pixel data. * - * The resulting sheet is immediately indexized -- its `getTexture()` call + * The resulting sheet is immediately indexized - its `getTexture()` call * will produce an `r8uint` GPU texture without needing `indexize()`. This * is used for embedded assets like the built-in system font where the pixel * data is already expressed as palette indices. @@ -332,7 +332,7 @@ export class SpriteSheet { const a = rgba[base + 3]; if (a === 0) { - // Index 0 is the transparent sentinel — the shader discards any pixel with rawIndex == 0. + // Index 0 is the transparent sentinel - the shader discards any pixel with rawIndex == 0. // eslint-disable-next-line security/detect-object-injection indexed[i] = 0; continue; @@ -373,14 +373,14 @@ export class SpriteSheet { /** * Re-converts retained RGBA pixels to palette indices using a new palette. * - * Use this only when the **slot layout** of the palette has changed — that is, + * Use this only when the **slot layout** of the palette has changed - that is, * when the same colors now live at different index positions. It works by * replaying the original RGBA data through `palette.findColor()` and assigning * each opaque pixel the index of its matching color in the new palette. * * **Do not call this to change what color a slot displays.** If you only want * to swap the visible color at a slot (e.g. animate a fire effect), mutate the - * palette entry directly — the stored indices remain valid and the fragment + * palette entry directly - the stored indices remain valid and the fragment * shader picks up the new color automatically on the next frame: * * ```ts @@ -430,7 +430,7 @@ export class SpriteSheet { const a = rgba[base + 3]; if (a === 0) { - // Index 0 is the transparent sentinel — the shader discards any pixel with rawIndex == 0. + // Index 0 is the transparent sentinel - the shader discards any pixel with rawIndex == 0. // eslint-disable-next-line security/detect-object-injection indexed[i] = 0; continue; diff --git a/src/assets/SystemFont.ts b/src/assets/SystemFont.ts index 87a1d3a..0389ec8 100644 --- a/src/assets/SystemFont.ts +++ b/src/assets/SystemFont.ts @@ -3,7 +3,7 @@ * * Expands the embedded glyph bitmaps from {@link systemFontData} into a * palette-indexed texture atlas and wraps the result in a {@link BitmapFont}. - * The font is fully synchronous to create -- no `fetch()`, no image decode. + * The font is fully synchronous to create - no `fetch()`, no image decode. * * The glyph data lives in `src/assets/fonts/systemFontData.ts`. To edit it * visually, export the current bitmaps to a PNG, redraw in a pixel editor, diff --git a/src/core/BTAPI.test.ts b/src/core/BTAPI.test.ts index e41e4bb..13ad988 100644 --- a/src/core/BTAPI.test.ts +++ b/src/core/BTAPI.test.ts @@ -36,7 +36,7 @@ import type { IBlitTechDemo } from './IBlitTechDemo'; // #region Helpers function resetSingleton(): void { - // BTAPI._instance is private; the cast is intentional — there is no public + // BTAPI._instance is private; the cast is intentional - there is no public // reset API, and this is the least-invasive way to isolate singleton state // between tests without modifying production code. (BTAPI as unknown as { _instance: BTAPI | null })._instance = null; diff --git a/src/core/BTAPI.ts b/src/core/BTAPI.ts index e95f0ca..924b05c 100644 --- a/src/core/BTAPI.ts +++ b/src/core/BTAPI.ts @@ -552,7 +552,7 @@ export class BTAPI { public drawSystemText(pos: Vector2i, paletteIndex: number, text: string): void { this.assertPaletteIndex(paletteIndex); - // Palette index 0 is transparent -- nothing to draw. + // Palette index 0 is transparent - nothing to draw. if (paletteIndex === 0) { return; } @@ -855,7 +855,7 @@ export class BTAPI { // #endregion - // #region Private — Initialization Helpers + // #region Private - Initialization Helpers /** * Constructs and initializes the renderer for the active hardware settings. diff --git a/src/core/GameLoop.test.ts b/src/core/GameLoop.test.ts index 9a3a5eb..9090cb1 100644 --- a/src/core/GameLoop.test.ts +++ b/src/core/GameLoop.test.ts @@ -144,7 +144,7 @@ describe('GameLoop', () => { // Execute outer RAF callback. rafCallbacks[0]?.(); - // Execute inner RAF callback — sets lastUpdateTime and schedules first tick. + // Execute inner RAF callback - sets lastUpdateTime and schedules first tick. rafCallbacks[1]?.(); expect(p.lastUpdateTime).toBeGreaterThan(0); @@ -204,7 +204,7 @@ describe('GameLoop', () => { p.isRunning = true; p.lastUpdateTime = 0; - p.tick(10000); // huge pause — MAX_STEPS = 8 caps at 8 updates + p.tick(10000); // huge pause - MAX_STEPS = 8 caps at 8 updates expect(onUpdate).toHaveBeenCalledTimes(8); }); diff --git a/src/core/IBlitTechDemo.ts b/src/core/IBlitTechDemo.ts index 0ac4889..c00ab60 100644 --- a/src/core/IBlitTechDemo.ts +++ b/src/core/IBlitTechDemo.ts @@ -88,8 +88,8 @@ export interface HardwareSettings { * stutters during development. Defaults to `false`. * * Detection runs in `GameLoop.detectFrameDrop()` and uses an - * auto-calibrated baseline -- the shortest `requestAnimationFrame` delta - * observed in a rolling window of recent frames -- rather than a fixed + * auto-calibrated baseline - the shortest `requestAnimationFrame` delta + * observed in a rolling window of recent frames - rather than a fixed * `1.5 / targetFPS` threshold. A frame is reported as dropped when its * rAF delta exceeds 1.5x that baseline, which makes detection work on * any display refresh rate (60 / 120 / 144 Hz, etc.) and on browsers diff --git a/src/core/WebGPUContext.test.ts b/src/core/WebGPUContext.test.ts index 1358c78..e76d8aa 100644 --- a/src/core/WebGPUContext.test.ts +++ b/src/core/WebGPUContext.test.ts @@ -45,7 +45,7 @@ describe('initWebGPU', () => { // #region No WebGPU support it('should return null when navigator.gpu is absent', async () => { - // Use Object.defineProperty to install a navigator without .gpu — direct + // Use Object.defineProperty to install a navigator without .gpu - direct // property deletion throws in Node.js when navigator is null/non-writable. Object.defineProperty(globalThis, 'navigator', { value: { userAgent: 'test' }, diff --git a/src/core/WebGPUContext.ts b/src/core/WebGPUContext.ts index 616ffcd..6c486e4 100644 --- a/src/core/WebGPUContext.ts +++ b/src/core/WebGPUContext.ts @@ -28,7 +28,7 @@ export interface WebGPUContextResult { * The WebGPU drawing buffer is sized to `canvasDisplaySize` when provided so * the engine can run display-tier post-processing (CRT scanlines, barrel * distortion, etc.) at output resolution. The CSS size of the canvas matches - * the drawing-buffer size — when the demo wants different on-screen and + * the drawing-buffer size - when the demo wants different on-screen and * GPU-internal sizes it must apply CSS itself after init. * * When `canvasDisplaySize` is omitted, the drawing buffer matches the logical diff --git a/src/input/KeyboardInput.ts b/src/input/KeyboardInput.ts index da03db9..2cb315d 100644 --- a/src/input/KeyboardInput.ts +++ b/src/input/KeyboardInput.ts @@ -347,7 +347,7 @@ export class KeyboardInput { for (let i = 0; i < data.length; i++) { const cp = data.charCodeAt(i); - // Printable ASCII 32–127, plus Tab (9) and ESC (27) when present as text. + // Printable ASCII 32-127, plus Tab (9) and ESC (27) when present as text. if ((cp >= 32 && cp <= 127) || cp === 9 || cp === 27) { this.inputBuffer += String.fromCharCode(cp); } diff --git a/src/render/PrimitivePipeline.ts b/src/render/PrimitivePipeline.ts index 582db04..4b5530c 100644 --- a/src/render/PrimitivePipeline.ts +++ b/src/render/PrimitivePipeline.ts @@ -580,7 +580,7 @@ export class PrimitivePipeline { /** * Records the current vertex batch and resets the vertex count. * Used for early flush when the buffer is full mid-frame. - * Does not write to GPU -- encodePass() uploads all batches at once. + * Does not write to GPU - encodePass() uploads all batches at once. */ private earlyFlush(): void { if (this.vertexCount === 0) { diff --git a/src/render/SoftwareRenderer.ts b/src/render/SoftwareRenderer.ts index 3175f5d..7987877 100644 --- a/src/render/SoftwareRenderer.ts +++ b/src/render/SoftwareRenderer.ts @@ -408,7 +408,7 @@ export class SoftwareRenderer implements IRenderer { // #region Effects /** - * Not supported — always throws. + * Not supported - always throws. * * @param _effect - Ignored. */ @@ -417,7 +417,7 @@ export class SoftwareRenderer implements IRenderer { } /** - * Not supported — always throws. + * Not supported - always throws. * * @param _effect - Ignored. */ @@ -425,7 +425,7 @@ export class SoftwareRenderer implements IRenderer { throw new Error(SoftwareRenderer.EFFECTS_UNSUPPORTED_MESSAGE); } - /** Not supported — always throws. */ + /** Not supported - always throws. */ clearEffects(): void { throw new Error(SoftwareRenderer.EFFECTS_UNSUPPORTED_MESSAGE); } @@ -797,7 +797,7 @@ export class SoftwareRenderer implements IRenderer { if (typeof this.canvas.toBlob !== 'function') { this.pendingCapture.reject( new Error( - "Can't save this frame — your browser doesn't support canvas image export. Try Chrome or Edge.", + "Can't save this frame - your browser doesn't support canvas image export. Try Chrome or Edge.", ), ); this.pendingCapture = null; @@ -810,7 +810,7 @@ export class SoftwareRenderer implements IRenderer { if (!blob) { request.reject( new Error( - "Can't save this frame — something went wrong exporting the canvas image. Try again on the next frame.", + "Can't save this frame - something went wrong exporting the canvas image. Try again on the next frame.", ), ); return; diff --git a/src/render/SpritePipeline.ts b/src/render/SpritePipeline.ts index 03d3e05..9e43c6c 100644 --- a/src/render/SpritePipeline.ts +++ b/src/render/SpritePipeline.ts @@ -58,10 +58,10 @@ export class SpritePipeline { /** Backing buffer for all vertex data. */ private readonly vertexArrayBuffer: ArrayBuffer; - /** Float32 view over vertexArrayBuffer — for x, y, u, v (indices i*5+0..+3). */ + /** Float32 view over vertexArrayBuffer - for x, y, u, v (indices i*5+0..+3). */ private readonly vertexFloats: Float32Array; - /** Uint32 view over vertexArrayBuffer — for paletteOffset (index i*5+4). */ + /** Uint32 view over vertexArrayBuffer - for paletteOffset (index i*5+4). */ private readonly vertexUints: Uint32Array; /** Number of vertices in the current (unflushed) batch. */ @@ -368,7 +368,7 @@ export class SpritePipeline { usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, }); - // Shared bind group (group 0): uniforms + palette — created once and reused for all textures. + // Shared bind group (group 0): uniforms + palette - created once and reused for all textures. this.sharedBindGroup = device.createBindGroup({ label: 'Sprite Shared Bind Group', layout: (this.pipeline as GPURenderPipeline).getBindGroupLayout(0), @@ -424,7 +424,7 @@ export class SpritePipeline { this.flushCurrentBatch(); } - // Check again after flush — if still no space, the buffer is full for this frame. + // Check again after flush - if still no space, the buffer is full for this frame. if (!this.hasSpaceForQuad()) { console.warn('[SpritePipeline] Sprite buffer capacity exceeded for this frame, quad dropped'); diff --git a/src/render/UpscalePass.ts b/src/render/UpscalePass.ts index 1f11a4d..1e02e89 100644 --- a/src/render/UpscalePass.ts +++ b/src/render/UpscalePass.ts @@ -153,7 +153,7 @@ export class UpscalePass { // #endregion - // #region Helpers — texture allocation + // #region Helpers - texture allocation /** * Allocates a texture for the upscale pass output at the supplied size and diff --git a/src/render/WebGpuRenderer.test.ts b/src/render/WebGpuRenderer.test.ts index 476cf4b..53f0f9b 100644 --- a/src/render/WebGpuRenderer.test.ts +++ b/src/render/WebGpuRenderer.test.ts @@ -407,7 +407,7 @@ describe('with initialized renderer', () => { it('drawBitmapText delegates without throwing', () => { renderer.beginFrame(); - // Empty text — loop does not execute so glyph methods are never called. + // Empty text - loop does not execute so glyph methods are never called. const mockFont = { getSpriteSheet: () => ({}), getGlyphByCode: () => null, @@ -433,7 +433,7 @@ describe('resolveClearColor fallbacks', () => { await r.init(); - // No setPalette() call — endFrame() resolves clear color to black via fallback. + // No setPalette() call - endFrame() resolves clear color to black via fallback. expect(() => r.endFrame()).not.toThrow(); uninstallMockNavigatorGPU(); @@ -742,7 +742,7 @@ describe('palette dirty-flag auto-propagation', () => { renderer.setPalette(palette); - // Mutate the original after setPalette — the renderer must see the change. + // Mutate the original after setPalette - the renderer must see the change. palette.set(1, new Color32(99, 99, 99, 255)); // getPalette() returns a clone, so compare by value rather than reference. @@ -805,7 +805,7 @@ describe('palette dirty-flag auto-propagation', () => { renderer.setPalette(palette); - // First frame — initial upload due to paletteDirty. + // First frame - initial upload due to paletteDirty. renderer.beginFrame(); renderer.endFrame(); @@ -814,7 +814,7 @@ describe('palette dirty-flag auto-propagation', () => { // Mutate palette without calling BT.paletteSet() again. palette.set(1, new Color32(255, 0, 128, 255)); - // Second frame — must re-upload because palette.dirty is true. + // Second frame - must re-upload because palette.dirty is true. renderer.beginFrame(); renderer.endFrame(); @@ -839,13 +839,13 @@ describe('palette dirty-flag auto-propagation', () => { renderer.setPalette(palette); - // First frame — initial upload. + // First frame - initial upload. renderer.beginFrame(); renderer.endFrame(); const callsAfterFirstFrame = writeBufferSpy.mock.calls.length; - // No mutation — second frame should NOT upload. + // No mutation - second frame should NOT upload. renderer.beginFrame(); renderer.endFrame(); diff --git a/src/render/WebGpuRenderer.ts b/src/render/WebGpuRenderer.ts index 6f75627..432f8f3 100644 --- a/src/render/WebGpuRenderer.ts +++ b/src/render/WebGpuRenderer.ts @@ -247,7 +247,7 @@ export class WebGpuRenderer implements IRenderer { /** * Sets the active palette used for rendering. * - * Stores a reference to the supplied palette — no clone is made. Subsequent + * Stores a reference to the supplied palette - no clone is made. Subsequent * calls to {@link Palette.set} or {@link Palette.copyFrom} on the same object * will be detected via {@link Palette.dirty} and uploaded automatically at the * start of the next frame. {@link paletteDirty} is set to guarantee the initial @@ -272,7 +272,7 @@ export class WebGpuRenderer implements IRenderer { * Returns a clone to prevent callers from accidentally mutating the active * palette through the returned reference in ways that may be surprising. * To intentionally update palette colors, mutate the original palette object - * that was passed to {@link setPalette} — changes will auto-propagate via the + * that was passed to {@link setPalette} - changes will auto-propagate via the * dirty flag on the next frame. * * @returns Clone of the active palette instance, or null. @@ -547,7 +547,7 @@ export class WebGpuRenderer implements IRenderer { // #endregion - // #region Private — frame encoding + // #region Private - frame encoding /** * Tries to acquire the swap-chain texture and validate its dimensions. @@ -746,7 +746,7 @@ export class WebGpuRenderer implements IRenderer { // #endregion - // #region Private — drawing + // #region Private - drawing /** * Fast-path pixel draw using raw integer coordinates. diff --git a/src/render/effects/FullscreenEffect.ts b/src/render/effects/FullscreenEffect.ts index b6b642a..62f7752 100644 --- a/src/render/effects/FullscreenEffect.ts +++ b/src/render/effects/FullscreenEffect.ts @@ -34,7 +34,7 @@ export abstract class FullscreenEffect implements Effect { abstract readonly tier: EffectTier; /** - * Sampler filter mode. Defaults to `'linear'` (smooth — appropriate for + * Sampler filter mode. Defaults to `'linear'` (smooth - appropriate for * display-tier effects). Pixel-tier effects can override to `'nearest'` to * preserve palette colors during sampling. */ diff --git a/src/render/effects/display/Flicker.ts b/src/render/effects/display/Flicker.ts index 21f6a14..394ee90 100644 --- a/src/render/effects/display/Flicker.ts +++ b/src/render/effects/display/Flicker.ts @@ -2,7 +2,7 @@ import type { Vector2i } from '../../../utils/Vector2i'; import { FullscreenEffect } from '../FullscreenEffect'; /** - * Brightness multiplier — the simplest CRT animation knob. + * Brightness multiplier - the simplest CRT animation knob. * * Demos drive {@link amount} per frame to simulate flicker (e.g. with * `0.95 + sin(t) * 0.05`). The effect is intentionally trivial so the demo diff --git a/src/render/effects/presets/amber.ts b/src/render/effects/presets/amber.ts index 8a72961..83df677 100644 --- a/src/render/effects/presets/amber.ts +++ b/src/render/effects/presets/amber.ts @@ -9,7 +9,7 @@ import type { Effect } from '../Effect'; /** * Amber monochrome PC monitor look (think IBM 5151 / Hercules). * - * Ships as a parameter-only set — the underlying colors stay full-RGB until + * Ships as a parameter-only set - the underlying colors stay full-RGB until * the {@link MonochromeQuantize} effect lands (tracked in * [VV-479](https://linear.app/vancura/issue/VV-479/monochrome-re-quantization-display-effect)). * For now this preset gives you the *feel* (CRT curvature + scanlines + diff --git a/src/render/effects/presets/green.ts b/src/render/effects/presets/green.ts index 8f1ecd2..9d2df30 100644 --- a/src/render/effects/presets/green.ts +++ b/src/render/effects/presets/green.ts @@ -9,7 +9,7 @@ import type { Effect } from '../Effect'; /** * Green monochrome PC monitor look (think original IBM monochrome / VT100). * - * Ships as a parameter-only set — see the {@link amber} preset for the + * Ships as a parameter-only set - see the {@link amber} preset for the * caveat about re-quantization (VV-479). Same effect stack as `amber()`, * tuned slightly toward a cooler / more flickery aesthetic. * diff --git a/src/utils/Bootstrap.test.ts b/src/utils/Bootstrap.test.ts index 2935afe..fd7077f 100644 --- a/src/utils/Bootstrap.test.ts +++ b/src/utils/Bootstrap.test.ts @@ -133,7 +133,7 @@ describe('bootstrap', () => { describe('canvas validation', () => { it('should return false and call onError when canvas is not found', async () => { - // No canvas in DOM — only the container. + // No canvas in DOM - only the container. const container = document.createElement('div'); container.id = DEFAULT_CONTAINER_ID; diff --git a/src/utils/Color32.ts b/src/utils/Color32.ts index d67f185..e4dc769 100644 --- a/src/utils/Color32.ts +++ b/src/utils/Color32.ts @@ -101,7 +101,7 @@ export class Color32 { /** * Pure white color (255, 255, 255, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared white Color32 instance. */ static get white(): Color32 { @@ -110,7 +110,7 @@ export class Color32 { /** * Pure black color (0, 0, 0, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared black Color32 instance. */ static get black(): Color32 { @@ -119,7 +119,7 @@ export class Color32 { /** * Fully transparent color (0, 0, 0, 0). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared transparent Color32 instance. */ static get transparent(): Color32 { @@ -128,7 +128,7 @@ export class Color32 { /** * Pure red color (255, 0, 0, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared red Color32 instance. */ static get red(): Color32 { @@ -137,7 +137,7 @@ export class Color32 { /** * Pure green color (0, 255, 0, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared green Color32 instance. */ static get green(): Color32 { @@ -146,7 +146,7 @@ export class Color32 { /** * Pure blue color (0, 0, 255, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared blue Color32 instance. */ static get blue(): Color32 { @@ -155,7 +155,7 @@ export class Color32 { /** * Yellow color (255, 255, 0, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared yellow Color32 instance. */ static get yellow(): Color32 { @@ -164,7 +164,7 @@ export class Color32 { /** * Cyan color (0, 255, 255, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared cyan Color32 instance. */ static get cyan(): Color32 { @@ -173,7 +173,7 @@ export class Color32 { /** * Magenta color (255, 0, 255, 255). - * Cached frozen singleton — do not modify. + * Cached frozen singleton - do not modify. * @returns The shared magenta Color32 instance. */ static get magenta(): Color32 { diff --git a/src/utils/FrameCapture.test.ts b/src/utils/FrameCapture.test.ts index 722c366..6858eb6 100644 --- a/src/utils/FrameCapture.test.ts +++ b/src/utils/FrameCapture.test.ts @@ -153,7 +153,7 @@ describe('FrameCapture', () => { it('should set a pending flag after requestCapture', () => { const capture = new FrameCapture(); - // Don't await -- just queue the request. + // Don't await - just queue the request. void capture.requestCapture(); expect(capture.hasPendingCapture()).toBe(true); diff --git a/src/utils/Vector2i.ts b/src/utils/Vector2i.ts index 41bc8f3..4904b43 100644 --- a/src/utils/Vector2i.ts +++ b/src/utils/Vector2i.ts @@ -112,7 +112,7 @@ export class Vector2i { } /** - * Returns an up direction vector (0, –1). + * Returns an up direction vector (0, -1). * In screen coordinates, Y increases downward, so up is negative. * Returns a cached frozen singleton - do not modify. * @@ -173,7 +173,7 @@ export class Vector2i { * Creates an integer vector from floating-point coordinates. * Both values are truncated to integers using |0. * - * Note: |0 truncates toward zero (e.g., –1.7 becomes –1, not –2). + * Note: |0 truncates toward zero (e.g., -1.7 becomes -1, not -2). * * @param x - Floating-point x coordinate. * @param y - Floating-point y coordinate. diff --git a/src/utils/errorMessages.ts b/src/utils/errorMessages.ts index fd9ed4a..7eb441d 100644 --- a/src/utils/errorMessages.ts +++ b/src/utils/errorMessages.ts @@ -44,7 +44,7 @@ export const WEBGPU_ADAPTER_MESSAGE = export const WEBGPU_DEVICE_MESSAGE = "Couldn't connect to the graphics card. Try closing other tabs or restarting the browser."; -// #region Runtime — Render configuration +// #region Runtime - Render configuration /** * Returns the error message for a render dimension that is not a positive whole-number pixel size. @@ -104,7 +104,7 @@ export function renderDimensionGpuLimitError(field: string, size: string, maxTex // #endregion -// #region Runtime — Assets +// #region Runtime - Assets /** * Returns the error message for an asset whose width or height is not a positive whole number. @@ -370,7 +370,7 @@ export function btfontGlyphAreaTooLargeError(charLabel: string): string { // #endregion -// #region Runtime — Palette +// #region Runtime - Palette /** * Returns the "no active palette" error message used whenever a palette must @@ -431,7 +431,7 @@ export function hudRangeError(startSlot: number, count: number, size: number): s // #endregion -// #region Runtime — Sprites +// #region Runtime - Sprites /** * Returns the error message for a sprite pixel whose color is absent from the diff --git a/tests/visual/fixtures/post-process.html b/tests/visual/fixtures/post-process.html index a80ae51..585c805 100644 --- a/tests/visual/fixtures/post-process.html +++ b/tests/visual/fixtures/post-process.html @@ -37,7 +37,7 @@ // many snapshot variants. const mode = (window.location.hash || '#baseline').replace('#', ''); - // Modes that need a display-tier output buffer — anything testing a + // Modes that need a display-tier output buffer - anything testing a // display effect or the upscale pass needs canvasDisplaySize set. const NEEDS_DISPLAY = new Set([ 'barrel',