/** * Type declarations for `` — the public API of `HexLifeElement.js`. * * `src/embed/` is plain JS (see the note atop EmbedSim.js for why it is not `@ts-check`ed), so this * file is the one place the element's *documented* surface is written down in a form a compiler can * enforce. It also gives TypeScript hosts, including the separate Devvit repository, a declaration * that cannot silently drift from the element maintained here. * * **This file is documentation with teeth: it must match `docs/embed/hexlife-world.md`**, which is * what consumers of the published package actually read. The embed's API is frozen (additive * changes only), so adding to it means editing that doc, the element, and this file together. * * There is no `hexlife-world.js` — the runtime lives in `HexLifeElement.js` and registers itself * via `index.js`. Import this module for types only (`import type`), alongside the side-effecting * `import '.../embed/index.js'` that actually defines the element. */ /** The `EmbedSim` surface `` exposes through its readonly `sim` property. */ export interface HexLifeSim { readonly rows: number; readonly cols: number; readonly numCells: number; /** 32-char hex ruleset currently loaded. */ readonly rulesetHex: string; /** Generations elapsed since the last reset. */ readonly tickCount: number; /** Live cells in the current generation. Maintained by ticks, draw strokes and `clear`. */ readonly activeCount: number; /** Cells that changed on the final tick of the most recent batch. */ readonly lastChangedCount: number; /** Whether the current generation maps to itself and automatic playback is parked. */ readonly isSettled: boolean; /** Target ticks/second once playing. */ speed: number; /** * Live view of the current generation's cells (`rows * cols`, 1 = alive), or null once freed. * This is a **view into wasm linear memory**, not a copy — see `snapshotCells`. */ readonly state: Uint8Array | null; /** Advance exact generations in one native batch. @returns Active cells in the final generation. */ tick(count?: number): number; /** Re-seed the initial state and rewind to tick 0. */ reset(seed?: number | null): void; /** Blank every cell and forget the rule history. @returns Whether the sim was live. */ clear(): boolean; /** Rolling hash of the current state — the determinism cross-check hook. */ checksum(): number; /** Native `rule * 2 + state` layer for `@hexlife/embed/spacetime`. */ packRenderLayer(): Uint8Array; /** * A private copy of the current cells, safe to hold across allocating wasm calls (which detach * the `state` view) and across ticks. Null once the sim is freed. */ snapshotCells(): Uint8Array | null; /** * Invert cells under a brush stroke. `strokeAffected` is the per-stroke "already painted" set * and is mutated. @returns Whether any cell changed. */ invertBrushLine( col0: number, row0: number, col1: number, row1: number, brushSize: number, strokeAffected: Set, ): boolean; } /** `hexlife-ready` detail — the world booted and is renderable. */ export interface HexLifeReadyDetail { rows: number; cols: number; numCells: number; brushSize: number; } /** `hexlife-playstate` detail — the `{playing, userPaused}` tuple changed. */ export interface HexLifePlayStateDetail { playing: boolean; userPaused: boolean; } /** `hexlife-error` detail — the element entered its styled error state and will not run. */ export interface HexLifeErrorDetail { message: string; detail: string; } /** * `hexlife-contextlost` / `hexlife-contextrestored` carry no detail. * * A lost GPU context is not an error state — the element has asked for the context back and will * rebuild the world if it arrives, so `error` stays null and no `hexlife-error` fires. A host that * paints its own chrome may want to say something in the gap; one that doesn't can ignore both and * still be correct, because a recovery ends in a fresh `hexlife-ready` and a permanent failure ends * in `hexlife-error` as usual. */ export type HexLifeContextDetail = undefined; /** * Events the element dispatches. All are `bubbles` + `composed`, so they escape the shadow root and * a host can listen on the element itself. */ export interface HexLifeElementEventMap { 'hexlife-ready': CustomEvent; 'hexlife-playstate': CustomEvent; 'hexlife-error': CustomEvent; 'hexlife-contextlost': CustomEvent; 'hexlife-contextrestored': CustomEvent; } /** * `` — see `docs/embed/hexlife-world.md` § Attributes for the full contract: * `ruleset` · `seed` · `density` · `rows` · `speed` · `palette` · `palette-on`/`off` · * `hue-shift` (0–359 degrees; rotates chromatic colors) · * `flicker-proof` (suppress the birth/death flash; preset palettes only) · `code` * (`HXW1.…`, wins over the individual attrs) · `paused` · `max-dpr` · `link` (`on`/`off`) · * `draw` · `wheel-zoom` (`free` | `ctrl`) · `preview` (poster burst tick count, 1–60) · * `torus` (wrap the world onto its own 3D surface; value = auto-rotation °/s, 0–45, default 14, * `torus="0"` for a still one). Drag orbits, wheel/pinch dollies; like `draw`, it takes the pointer, * so the poster play overlay steps aside and the host owns play. * `brush` (draw radius, 0–40; overrides a code's own value — it is a tool setting, not part of the * world) · `zoom` (flat camera, 1–8; 1 is the fitted view). * * `palette` / `palette-on` / `palette-off` **override** the colors a `code` carries; `hue-shift` * modifies whichever palette is active. All apply to a * live world without re-booting it. Removing them restores the world's own colors — which is the * only way back, since decoded colors have no preset name to ask for. While overridden, * {@link HexLifeElement.worldCode} encodes what is on screen rather than what arrived. */ export declare class HexLifeElement extends HTMLElement { /** The live sim, or null before boot / after teardown / in the error state. */ readonly sim: HexLifeSim | null; /** Non-null while the element is in its styled error state; the message shown. */ readonly error: string | null; /** Start (or resume). An explicit call also overrides `prefers-reduced-motion`. */ play(): void; /** Pause. The current generation stays on screen. */ pause(): void; /** * Re-seed the initial state and rewind to tick 0. * @param seed Defaults to the `seed` attribute (so `reset()` replays the same run). A falsy * seed is nondeterministic, as in the app. */ reset(seed?: number): void; /** * Blank the world — every cell dead, no rule history — leaving the ruleset, speed, play state * and camera alone. Unlike {@link reset} this does not rewind: `tickCount` keeps counting, * because the sim has not gone back in time, it has been painted over. With `draw`, this is * what turns a remix into an empty canvas rather than an edit of someone else's world. */ clear(): void; /** * Advance exactly `n` generations now, independent of `speed` and the play state. * @returns The new tick count. */ tick(n?: number): number; /** Set the brush / neighborhood radius used for draw strokes (clamped 0–40). */ setBrushSize(size: number): void; /** * Set the flat camera's zoom (1–8) about the centre of the view. `setZoom(1)` also clears the * pan, so it is the reliable way back to the fitted world after a pinch. The torus keeps its * own camera and ignores this. */ setZoom(zoom: number): void; /** * The world as it stands right now — exact cells, painted ones included — as an `HXW1.` code, * or null when there is nothing to encode (error state, or not booted). * * Never encodes a generator: a remix is the dish, not the recipe, so decoding this reproduces * exactly the world on screen rather than re-rolling a new one. */ worldCode(): Promise; /** Generations elapsed since the last reset. */ readonly tickCount: number; /** Hash of the current state — equal to the app's for equal params + ticks. */ readonly checksum: number; /** Whether the animation loop is currently running. */ readonly playing: boolean; /** Whether the user has paused (attribute or `pause()`), ignoring the viewport/visibility gates. */ readonly userPaused: boolean; /** Brush / neighborhood radius used for draw strokes. */ readonly brushSize: number; /** Flat-camera zoom; 1 is the fitted "whole world" view. */ readonly zoom: number; /** * Whether the world is actually on the torus right now — not merely whether `torus` was asked * for. The projection is built on first use and the element stays flat if that build fails, so * a host painting a pressed-state toggle should read this rather than its own intent. */ readonly torusEnabled: boolean; addEventListener( type: K, listener: (this: HexLifeElement, ev: HexLifeElementEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ): void; addEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions, ): void; removeEventListener( type: K, listener: (this: HexLifeElement, ev: HexLifeElementEventMap[K]) => void, options?: boolean | EventListenerOptions, ): void; removeEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions, ): void; } declare global { interface HTMLElementTagNameMap { 'hexlife-world': HexLifeElement; } }