/*

System Font Glyphs: browse every glyph the built-in system font covers.
@description Browse every glyph the built-in system font defines, and click one to copy it to your clipboard.

Part of the BLIT386 demo series.

Prerequisites:
  - Fonts: https://demos.blit386.dev/fonts
  - Bitmap Font: https://demos.blit386.dev/bitmap-font

Live version: https://demos.blit386.dev/system-font-glyphs

Guides:
  - Bitmap Fonts: https://blit386.dev/docs/guides/bitmap-fonts#built-in-system-font
  - Input: https://blit386.dev/docs/guides/input#pointer-slot-model
  - Overlay: https://blit386.dev/docs/guides/overlay#visibility-model

Reference:
  - Assets: https://blit386.dev/docs/api/assets#system-font
  - Rendering: https://blit386.dev/docs/api/rendering#system-font
  - Core: https://blit386.dev/docs/api/core#hardware-settings

---

WHAT YOU WILL SEE
-----------------

A 600x315 screen with a title bar across the top that reads "174 glyphs - Click one to
copy to the clipboard" (the number is read from the font, so it follows whatever the
engine ships). Below it sits a grid of 16 columns of small button-shaped cells, each
holding one white glyph of the built-in 6x14 system font, in the order the font lists
them: printable ASCII first, then the extra characters (dashes, accented letters, arrows,
Greek capitals, and so on). The last row is only partly filled.

Move the pointer over a cell and it lights up, with a tooltip above it showing the code
point, like "U+00E9 (233) é". Click it and the tooltip switches to "Copied é" with a
green rim on the cell for three quarters of a second, or to "Copy failed" with an orange
rim when the browser refused. The engine overlay is switched off for this demo.

WHAT YOU WILL LEARN
-------------------

- BT.systemFont is the real BitmapFont object BT.systemPrint() draws with, so everything
  the Bitmap Font demo did with a loaded font works on it too.
- font.codePoints lists every Unicode code point the font has a glyph for, in ascending
  order. The grid is built from that list, so nothing here hardcodes the glyph count.
- BT.printFont(BT.systemFont, pos, char, 0) draws one glyph anywhere. The system font's
  pixels live at palette index 1, so offset 0 draws slot 1 - which is why init() paints
  that slot white.
- BT.systemPrintMeasure('M') gives the glyph size instead of hardcoding 6x14.
- Pointer input across all four slots: BT.isPointerActive(slot), BT.pointerPos(slot), and
  BT.isPressed(BT.BTN_POINTER_A, slot) for the press edge, read in update() rather than
  render().
- navigator.clipboard.writeText() - the browser's own clipboard API, called from an async
  method whose result arrives later and is shown through a short status message.
- The shared UI kit's ui.drawButtonFace(), ui.tooltip(), and ui.overWidget().
- configure() returning displaySize and isOverlayEnabled: false.

WHY 600 BY 315?
---------------

That is exactly half of the 1200x630 social card the demo site generates, so the card
shows the screen at a clean 2x scale with no blurry resampling. It also leaves room for 16
columns of 23-pixel cells with 2-pixel gaps.

TRY THIS
--------

- Hover any cell to read its code point in both hex and decimal.
- Click a cell (or tap it on a touch screen - touches use pointer slots 1-3), then paste
  somewhere: the character itself lands on the clipboard, not its number.
- Click a cell in the top row: the tooltip has no room above, so it drops below the cell
  instead of covering the title bar.
- Copying can fail when the page has not been focused or clipboard permission was denied -
  the cell rim turns orange and the tooltip says "Copy failed".

*/

import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
,
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
, class Color32
Mutable 32-bit RGBA color value with 8-bit channels.
@since0.1.0
Color32
, class Rect2i
Integer rectangle for pixel-perfect bounds and regions. Used throughout the engine for sprite regions, display-space bounds, and zero-allocation geometry helpers. Both convenience getters and allocation-free `*To()` helpers are provided so callers can choose between readability and hot-path efficiency.
@since0.1.0
Rect2i
, class Vector2i
Integer 2D vector for pixel-perfect positioning. Used for points, sizes, directions, and camera offsets throughout the engine. The API includes both allocation-free `*To()` / `*InPlace()` variants and convenience methods that return new vectors.
@since0.1.0
Vector2i
} from 'blit386';
import { import applyThemeapplyTheme, import BUTTON_LOOKBUTTON_LOOK, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js'; /** @typedef {import('blit386').Palette} Palette */ /** @typedef {import('blit386').BitmapFont} BitmapFont */ // The screen is 600x315 display pixels: exactly half of the 1200x630 social card, so the // card shows it at a clean 2x scale with no blurry resampling. const const DISPLAY_W: 600DISPLAY_W = 600; const const DISPLAY_H: 315DISPLAY_H = 315; // The built-in system font stores every glyph pixel as palette index 1 (see the Fonts // demo). A fresh palette starts that slot black, so the glyphs would vanish on the dark // button faces unless it is painted white. const const C_GLYPH: 1C_GLYPH = 1; // The grid layout. GRID_COLS = 16 deliberately matches the font's own texture atlas (16 // glyphs per row), so the grid echoes how the font stores its pixels. The row count is // not fixed - see gridRows below, computed from however many glyphs the font reports. const const GRID_COLS: 16GRID_COLS = 16; const const CELL_SIZE: 23CELL_SIZE = 23; const const CELL_GAP: 2CELL_GAP = 2; // gap between cells, so borders do not touch const const CELL_PITCH: numberCELL_PITCH = const CELL_SIZE: 23CELL_SIZE + const CELL_GAP: 2CELL_GAP; // distance from one cell's corner to the next const const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y = 28; // top of the grid, just below the title bar // How long the "Copied" / "Copy failed" message stays up before the tooltip goes back to // following the pointer. const const COPY_STATUS_SECONDS: 0.75COPY_STATUS_SECONDS = 0.75; // The three states of the copy feedback. Naming them once means a typo in a comparison // below fails loudly (an undefined property) instead of silently never matching. const const COPY_STATUS: anyCOPY_STATUS = Object.freeze({ type IDLE: stringIDLE: 'idle', // nothing copied recently: the tooltip follows the pointer type COPIED: stringCOPIED: 'copied', // the last click copied its glyph type FAILED: stringFAILED: 'failed', // the last click could not reach the clipboard }); // Tests whether a display-pixel position lands inside a real grid cell. `localX` and // `localY` are already offset so 0 is the grid's top-left corner. Returns the glyph index // at that position, or -1 when the position is outside every cell - including the thin // gaps between cells and the empty cells past the last real glyph in the final row. function function hitTestGlyphCell(localX: any, localY: any, glyphCount: any): anyhitTestGlyphCell(localX: anylocalX, localY: anylocalY, glyphCount: anyglyphCount) { // A negative position is above or to the left of the grid entirely. if (localX: anylocalX < 0 || localY: anylocalY < 0) { return -1; } // Dividing by the pitch and rounding down tells which column and row we landed in, // like working out which square of a checkerboard a finger is over. const const col: anycol = Math.floor(localX: anylocalX / const CELL_PITCH: numberCELL_PITCH); const const row: anyrow = Math.floor(localY: anylocalY / const CELL_PITCH: numberCELL_PITCH); // A column past the last one means we are off the right edge of the grid. if (const col: anycol >= const GRID_COLS: 16GRID_COLS) { return -1; } // How far into this column's pitch did we land? Past CELL_SIZE means the pointer is // in the gap between two cells, not on a cell. Then the same check for the row. if (localX: anylocalX - const col: anycol * const CELL_PITCH: numberCELL_PITCH >= const CELL_SIZE: 23CELL_SIZE) { return -1; } if (localY: anylocalY - const row: anyrow * const CELL_PITCH: numberCELL_PITCH >= const CELL_SIZE: 23CELL_SIZE) { return -1; } // Cells are numbered left to right, top to bottom - the order codePoints lists them. const const index: anyindex = const row: anyrow * const GRID_COLS: 16GRID_COLS + const col: anycol; // The last row may have empty cells past the final real glyph - reject those. return const index: anyindex < glyphCount: anyglyphCount ? const index: anyindex : -1; } // Draws every glyph BT.systemFont lists in a grid and mirrors the engine overlay's // palette-swatch click-to-copy using only public API: BT.pointerPos, BT.isPressed with // BT.BTN_POINTER_A, BT.printFont, and the browser's navigator.clipboard. class class DemoDemo { /** @type {Palette | null} */ Demo.palette: Palette | null
@type{Palette | null}
palette
= null;
// Palette slot numbers of the shared UI kit colors, filled in by applyTheme() in // init(). theme.bg clears the screen. Demo.theme: nulltheme = null; // Filled in by init() from BT.systemFont - see there for why. /** @type {BitmapFont | null} */ Demo.font: BitmapFont | null
@type{BitmapFont | null}
font
= null;
/** @type {readonly number[]} */ Demo.codePoints: {}
@type{readonly number[]}
codePoints
= [];
// How many rows the grid needs to fit every glyph. Computed in init() from // codePoints.length, never hardcoded - see the comment on GRID_COLS above. Demo.gridRows: numbergridRows = 0; // Pixel size of one glyph, measured in init() with BT.systemPrintMeasure('M') instead // of hardcoded, so the demo cannot drift out of sync with the font's real dimensions. Demo.glyphWidth: numberglyphWidth = 6; Demo.glyphHeight: numberglyphHeight = 14; // A running clock in seconds, used to time how long the "Copied" / "Copy failed" // message stays up. BT.deltaSeconds is the time one fixed update covers - adding it // up every tick gives a stopwatch. Demo.elapsed: numberelapsed = 0; // Whether a glyph was just copied, and whether it worked. IDLE means nothing was // copied recently, so the tooltip shows whatever is hovered. Demo.copyStatus: anycopyStatus = const COPY_STATUS: anyCOPY_STATUS.IDLE; Demo.copyStatusIndex: numbercopyStatusIndex = -1; // which glyph index the status message is about Demo.copyStatusExpiry: numbercopyStatusExpiry = 0; // this.elapsed value at which the status message clears // One rectangle, reused every frame, telling ui.tooltip() which cell to sit above. // Building a new one each frame would be wasteful - the widget only reads the // numbers. Demo.tooltipAnchor: Rect2itooltipAnchor = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(0, 0, const CELL_SIZE: 23CELL_SIZE, const CELL_SIZE: 23CELL_SIZE);
// Picks a display big enough for the whole glyph grid. Runs before init().
Demo.configure(): {
    displaySize: Vector2i;
    isOverlayEnabled: boolean;
}
configure
() {
return { displaySize: Vector2idisplaySize: new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(const DISPLAY_W: 600DISPLAY_W, const DISPLAY_H: 315DISPLAY_H),
// Turn the engine's debug overlay off, along with its small corner toggle // mark. This demo has no use for it. isOverlayEnabled: booleanisOverlayEnabled: false, }; } // Sets up the palette and reads BT.systemFont's glyph list. async Demo.init(): unknowninit() { this.Demo.palette: Palette | null
@type{Palette | null}
palette
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteCreate: (size?: number) => Palette
Creates a standalone palette instance.
@since1.0.3@paramsize - Palette size. Defaults to 256 colors.@returnsNew mutable palette.
paletteCreate
(256);
this.Demo.palette: Palette
@type{Palette | null}
palette
.Palette.set(index: number, color: Color32): void
Writes a color into a palette slot.
@paramindex - Palette index to overwrite.@paramcolor - Color to store.@throwsError if the index is invalid or if index `0` is set opaque.
set
(const C_GLYPH: 1C_GLYPH, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32
Creates a clamped 8-bit RGBA color.
@paramr - Red channel (0-255, defaults to 255).@paramg - Green channel (0-255, defaults to 255).@paramb - Blue channel (0-255, defaults to 255).@parama - Alpha channel (0-255, defaults to 255 = opaque).
Color32
(255, 255, 255));
// Install the shared UI kit colors (slots 240-251) before BT.paletteSet(). // ui.tooltip() and the button faces draw with those. this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palette
@type{Palette | null}
palette
);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.paletteSet: (palette: Palette) => void
Stores the active engine palette. Use this to swap the **entire palette** (e.g. switch between a day and night theme). After this call the renderer uploads the new palette uniform on the next frame. **Palette-value swap (change what a slot looks like):** mutate the live {@link BT.palette } in place with `palette.set(slot, newColor)`. The renderer uploads dirty slots on the next frame; no `paletteSet()` or {@link BT.spritesRefresh } needed. **Palette-layout swap (same colors, different slot positions):** build a new palette with the same colors at new indices, call `paletteSet()`, then call {@link BT.spritesRefresh } so every sprite sheet re-maps its original RGBA pixels against the new slot layout.
@since1.0.3@parampalette - Palette to make active.
paletteSet
(this.Demo.palette: Palette
@type{Palette | null}
palette
);
// BT.systemFont hands back the exact BitmapFont object BT.systemPrint() draws // with - a real object we can ask questions of. codePoints is the point of this // demo: it lists every Unicode character the font can draw, live, so the grid // stays accurate even if the font's glyph set changes later. this.Demo.font: BitmapFont | null
@type{BitmapFont | null}
font
=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemFont: BitmapFont
Built-in 6x14 system font used by {@link BT.systemPrint } (live reference, not a copy). Read `codePoints` on the returned font to see every Unicode code point it covers, or `hasGlyph(char)` to test one character. Pass it to {@link BT.printFont } to draw with the exact glyphs and metrics {@link BT.systemPrint } uses internally.
@since1.7.0@throwsError if read before {@link BT.init} finishes creating the system font.@returnsThe engine's built-in system font.
systemFont
;
this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
= this.Demo.font: BitmapFont
@type{BitmapFont | null}
font
.BitmapFont.codePoints: {}
Returns every Unicode code point this font defines a glyph for, ascending. Derived live from the same {@link glyphs } map {@link getGlyph } / {@link hasGlyph } read - it can never drift from what actually renders. Includes ordinary ASCII (the ASCII fast-path array is populated from this same map at construction time, not a second, disjoint source) and the fallback glyph when the font defines one (see `FALLBACK_GLYPH_CHAR`). Skips a key that spans more than one Unicode scalar value (for example a multi-character string from a malformed `.btfont` file) rather than reporting just its first scalar - nothing validates glyph keys down to a single scalar on load, so this getter defends its own "one code point per real glyph" guarantee instead of risking a duplicate or misleading entry. `codePoints.length` can therefore be lower than {@link glyphCount } for such a font.
@returnsSorted array of Unicode code points covered by this font's glyph map.
codePoints
;
// Math.ceil rounds up: 174 glyphs / 16 columns = 10.875, which becomes 11 rows // because the last, partly empty row still needs to exist. this.Demo.gridRows: numbergridRows = Math.ceil(this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
.length / const GRID_COLS: 16GRID_COLS);
// Measure one glyph so it can be centered inside its cell, instead of hardcoding // the font's pixel dimensions here. const const glyphSize: Vector2iglyphSize =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.systemPrintMeasure: (text: string) => Vector2i
Measures the pixel dimensions of a string rendered with the built-in system font.
@since1.0.3@paramtext - Text string to measure.@returnsWidth and height in pixels, or `Vector2i.zero()` before engine initialization.
systemPrintMeasure
('M');
this.Demo.glyphWidth: numberglyphWidth = const glyphSize: Vector2iglyphSize.Vector2i.x: number
Horizontal component (defaults to 0).
x
;
this.Demo.glyphHeight: numberglyphHeight = const glyphSize: Vector2iglyphSize.Vector2i.y: number
Vertical component (defaults to 0).
y
;
return true; } // Runs at a fixed rate (60 times per second). Advances the copy-status clock and // handles clicks and taps. Reading the press edge here, not in render(), matches how // every other pointer demo in the series handles clicks. Demo.update(): voidupdate() { this.Demo.elapsed: numberelapsed +=
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.deltaSeconds: number
Fixed-step seconds per update tick. Equivalent to `1 / BT.targetFPS` when `BT.targetFPS` is finite and positive. Falls back to `1 / 60` when target FPS is non-finite or non-positive.
@since1.0.4@returnsSeconds advanced by one fixed update tick.
deltaSeconds
;
// Once the "Copied" / "Copy failed" message has been up long enough, clear it so // the tooltip goes back to showing whatever the pointer is hovering. if (this.Demo.copyStatus: anycopyStatus !== const COPY_STATUS: anyCOPY_STATUS.IDLE && this.Demo.elapsed: numberelapsed >= this.Demo.copyStatusExpiry: numbercopyStatusExpiry) { this.Demo.copyStatus: anycopyStatus = const COPY_STATUS: anyCOPY_STATUS.IDLE; this.Demo.copyStatusIndex: numbercopyStatusIndex = -1; } // Walk every pointer slot. Slot 0 is the mouse; slots 1-3 are up to three // simultaneous touches. BTN_POINTER_A "just pressed" means a mouse click or a // touch just landed on any of them. for (let let slot: numberslot = 0; let slot: numberslot < 4; let slot: numberslot++) { if (
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isPressed: (button: number, player?: number, repeatRate?: number) => boolean
Checks whether a button was pressed on the current frame. Same parameter semantics as {@link isDown } ; returns `true` only on the frame the button transitions from up to down. Call from `update()`, not `render()`, for reliable detection: for keyboard-mapped face buttons (players 0 and 1), the press edge clears once per fixed-update tick, which always runs before that frame's `render()`. `BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).
@since1.1.1@changed1.8.0 Also accepts BTN_L2 / BTN_R2 / BTN_TRIGGER (gamepad only).@parambutton - Button constant from the `BTN_*` set.@paramplayer - Zero-based player index for gamepads, or pointer slot (0-3) for `BTN_POINTER_*`.@paramrepeatRate - Optional repeat interval in fixed ticks (`0`/omitted = edge only).@returns`true` on the transition frame.
isPressed
(
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.type BTN_POINTER_A: number
Primary pointer button code. Maps to mouse left for slot 0; touch contact for slots 1-3.
@since0.1.0
BTN_POINTER_A
, let slot: numberslot)) {
this.Demo.tryCopyAt(slot: any): voidtryCopyAt(let slot: numberslot); } } } // If this pointer slot landed on a real glyph cell (and not on the title panel above // the grid), starts copying that glyph to the clipboard. Demo.tryCopyAt(slot: any): voidtryCopyAt(slot: anyslot) { if (!
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isPointerActive: (pointerIndex?: number) => boolean
Reports whether the given pointer slot has a live pointer. For slot 0 (mouse) this is true while the mouse is hovering inside the canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is true while the contact is down.
@since1.1.1@parampointerIndex - Pointer slot (defaults to 0 = mouse).@returns`true` while the slot has live position data.
isPointerActive
(slot: anyslot)) {
return; } const const pos: Vector2ipos =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.pointerPos: (pointerIndex?: number) => Vector2i
Returns the position of the pointer in the given slot, in display coordinates. Slot 0 is the mouse; slots 1 through 3 are touch / pen contacts assigned in arrival order. Returns `Vector2i.zero()` when the engine has not been initialized, the slot index is out of `[0, 3]`, or the slot has no live pointer.
@since1.0.3@parampointerIndex - Pointer slot (defaults to 0 = mouse).@returnsPointer position in display coordinates.
pointerPos
(slot: anyslot);
// Do not let a click on the title panel fall through to the grid underneath it. if (import uiui.overWidget(const pos: Vector2ipos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, const pos: Vector2ipos.Vector2i.y: number
Vertical component (defaults to 0).
y
)) {
return; } const const index: anyindex = function hitTestGlyphCell(localX: any, localY: any, glyphCount: any): anyhitTestGlyphCell(const pos: Vector2ipos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- this.Demo.gridOriginX: anygridOriginX, const pos: Vector2ipos.Vector2i.y: number
Vertical component (defaults to 0).
y
- const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y, this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
.length);
if (const index: anyindex !== -1) { // `void` tells readers (and linters) we are deliberately not waiting for this // promise before update() moves on - copyGlyph() updates copyStatus itself // once the clipboard write settles. void this.Demo.copyGlyph(index: any): anycopyGlyph(const index: anyindex); } } // Writes one glyph's actual character to the system clipboard. `index` points into // this.codePoints. async Demo.copyGlyph(index: any): anycopyGlyph(index: anyindex) { // String.fromCodePoint turns a numeric code point (like 233) back into the real // character (like 'é'), so the letter itself lands on the clipboard, not a // number. const const char: anychar = String.fromCodePoint(this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
[index: anyindex]);
try { // Older or unusual browsers may not implement the Clipboard API at all. if (!navigator.clipboard?.writeText) { throw new Error('Clipboard API unavailable'); } await navigator.clipboard.writeText(const char: anychar); this.Demo.setCopyStatus(status: any, index: any): voidsetCopyStatus(const COPY_STATUS: anyCOPY_STATUS.COPIED, index: anyindex); } catch { // Browsers can also refuse the call outright - for example when the page was // never focused, or the user denied clipboard permission. this.Demo.setCopyStatus(status: any, index: any): voidsetCopyStatus(const COPY_STATUS: anyCOPY_STATUS.FAILED, index: anyindex); } } // Records the outcome of a copy attempt (COPY_STATUS.COPIED or COPY_STATUS.FAILED) // and schedules when its tooltip message disappears. Demo.setCopyStatus(status: any, index: any): voidsetCopyStatus(status: anystatus, index: anyindex) { this.Demo.copyStatus: anycopyStatus = status: anystatus; this.Demo.copyStatusIndex: numbercopyStatusIndex = index: anyindex; this.Demo.copyStatusExpiry: numbercopyStatusExpiry = this.Demo.elapsed: numberelapsed + const COPY_STATUS_SECONDS: 0.75COPY_STATUS_SECONDS; } // Runs once per screen refresh: clears the screen, draws the title panel, works out // which cell (if any) the pointer is over, then draws the grid and its tooltip. Demo.render(): voidrender() {
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.clear: (paletteIndex: number) => void
Sets the frame clear color using a palette index. The renderer uses this color when clearing the full display at the start of the next frame.
@since0.1.0@parampaletteIndex - Palette index for the full-screen clear pass.
clear
(this.Demo.theme: nulltheme.bg);
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_BAR); import uiui.panel(`${this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
.length} glyphs - Click one to copy to the clipboard`);
import uiui.end(); // Reading the pointer position (as opposed to a press edge) is safe from // render(). let let hoveredIndex: numberhoveredIndex = -1; for (let let slot: numberslot = 0; let slot: numberslot < 4; let slot: numberslot++) { if (!
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.isPointerActive: (pointerIndex?: number) => boolean
Reports whether the given pointer slot has a live pointer. For slot 0 (mouse) this is true while the mouse is hovering inside the canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is true while the contact is down.
@since1.1.1@parampointerIndex - Pointer slot (defaults to 0 = mouse).@returns`true` while the slot has live position data.
isPointerActive
(let slot: numberslot)) {
continue; } const const pos: Vector2ipos =
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.pointerPos: (pointerIndex?: number) => Vector2i
Returns the position of the pointer in the given slot, in display coordinates. Slot 0 is the mouse; slots 1 through 3 are touch / pen contacts assigned in arrival order. Returns `Vector2i.zero()` when the engine has not been initialized, the slot index is out of `[0, 3]`, or the slot has no live pointer.
@since1.0.3@parampointerIndex - Pointer slot (defaults to 0 = mouse).@returnsPointer position in display coordinates.
pointerPos
(let slot: numberslot);
if (import uiui.overWidget(const pos: Vector2ipos.Vector2i.x: number
Horizontal component (defaults to 0).
x
, const pos: Vector2ipos.Vector2i.y: number
Vertical component (defaults to 0).
y
)) {
continue; } const const index: anyindex = function hitTestGlyphCell(localX: any, localY: any, glyphCount: any): anyhitTestGlyphCell(const pos: Vector2ipos.Vector2i.x: number
Horizontal component (defaults to 0).
x
- this.Demo.gridOriginX: anygridOriginX, const pos: Vector2ipos.Vector2i.y: number
Vertical component (defaults to 0).
y
- const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y, this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
.length);
if (const index: anyindex !== -1) { let hoveredIndex: numberhoveredIndex = const index: anyindex; break; // first active pointer that lands on a cell wins } } this.Demo.drawGrid(hoveredIndex: any): voiddrawGrid(let hoveredIndex: numberhoveredIndex); // While a copy status message is active, the tooltip talks about that glyph (the // one just clicked) instead of whatever the pointer is over now. It is drawn // after the grid, so it covers the cells and glyphs underneath it. const const tooltipIndex: numbertooltipIndex = this.Demo.copyStatus: anycopyStatus === const COPY_STATUS: anyCOPY_STATUS.IDLE ? let hoveredIndex: numberhoveredIndex : this.Demo.copyStatusIndex: numbercopyStatusIndex; if (const tooltipIndex: numbertooltipIndex !== -1) { this.Demo.placeTooltipAnchor(index: any): voidplaceTooltipAnchor(const tooltipIndex: numbertooltipIndex); // minY keeps the box out of the title panel. On the top row, where there is // no room above the cell, ui.tooltip() drops the box down over the grid // instead. import uiui.tooltip(this.Demo.tooltipAnchor: Rect2itooltipAnchor, this.Demo.tooltipLabel(index: any): stringtooltipLabel(const tooltipIndex: numbertooltipIndex), { minY: numberminY: const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y }); } } // X position of the grid's top-left corner, centered on the display. A getter (worked // out each time it is read, like a tiny function) rather than a number saved in // init(), so it always matches the current CELL_SIZE and display width - even while // you tweak those numbers with hot reload, which does not re-run init() for a // method-only change. get Demo.gridOriginX: anygridOriginX() { // Subtracting one CELL_GAP accounts for the last column having no gap after it. const const gridWidth: numbergridWidth = const GRID_COLS: 16GRID_COLS * const CELL_PITCH: numberCELL_PITCH - const CELL_GAP: 2CELL_GAP; return Math.floor((
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.displaySize: Vector2i
Active logical render resolution in pixels. This is the game/simulation coordinate space configured by the demo, not the canvas element's CSS size. Each read returns a clone.
@since1.0.4@returnsConfigured logical size, or `Vector2i.zero()` before initialization.
displaySize
.Vector2i.x: number
Horizontal component (defaults to 0).
x
- const gridWidth: numbergridWidth) / 2);
} // Draws every glyph cell: a kit button face (lighter under the pointer, with a // colored rim right after a copy) and the glyph itself via BT.printFont() with the // font BT.systemFont handed us. `hoveredIndex` is the cell under the pointer, or -1. Demo.drawGrid(hoveredIndex: any): voiddrawGrid(hoveredIndex: anyhoveredIndex) { for (let let index: numberindex = 0; let index: numberindex < this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
.length; let index: numberindex++) {
const const col: numbercol = let index: numberindex % const GRID_COLS: 16GRID_COLS; const const row: anyrow = Math.floor(let index: numberindex / const GRID_COLS: 16GRID_COLS); const const cellX: anycellX = this.Demo.gridOriginX: anygridOriginX + const col: numbercol * const CELL_PITCH: numberCELL_PITCH; const const cellY: numbercellY = const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y + const row: anyrow * const CELL_PITCH: numberCELL_PITCH; const const cellRect: Rect2icellRect = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2i
Creates an integer rectangle, truncating all inputs toward zero.
@paramx - Left-edge X coordinate (defaults to 0).@paramy - Top-edge Y coordinate (defaults to 0).@paramwidth - Width in pixels (defaults to 0).@paramheight - Height in pixels (defaults to 0).
Rect2i
(const cellX: anycellX, const cellY: numbercellY, const CELL_SIZE: 23CELL_SIZE, const CELL_SIZE: 23CELL_SIZE);
// Each cell wears the same rounded face as a kit button (ui.drawButtonFace), // picked by state. Right after a copy attempt the rim lights up - accent // green when it worked, warm orange when it failed - so clicking gives // visible feedback even without reading the tooltip. Otherwise the cell under // the pointer gets the lighter hover face, and every other cell the resting // one. let let look: anylook = import BUTTON_LOOKBUTTON_LOOK.REST; if (let index: numberindex === this.Demo.copyStatusIndex: numbercopyStatusIndex) { let look: anylook = this.Demo.copyStatus: anycopyStatus === const COPY_STATUS: anyCOPY_STATUS.COPIED ? import BUTTON_LOOKBUTTON_LOOK.HELD : import BUTTON_LOOKBUTTON_LOOK.WARN; } else if (let index: numberindex === hoveredIndex: anyhoveredIndex) { let look: anylook = import BUTTON_LOOKBUTTON_LOOK.HOVER; } import uiui.drawButtonFace(const cellRect: Rect2icellRect, let look: anylook); // BT.printFont(font, position, text, paletteOffset) draws with any BitmapFont // - here BT.systemFont, the same font BT.systemPrint() uses internally. // Centering: the cell size minus the glyph size, halved and rounded down to // whole pixels. const const char: anychar = String.fromCodePoint(this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
[let index: numberindex]);
const const glyphX: anyglyphX = const cellX: anycellX + Math.floor((const CELL_SIZE: 23CELL_SIZE - this.Demo.glyphWidth: numberglyphWidth) / 2); const const glyphY: anyglyphY = const cellY: numbercellY + Math.floor((const CELL_SIZE: 23CELL_SIZE - this.Demo.glyphHeight: numberglyphHeight) / 2);
const BT: {
    FLIP_H: number;
    FLIP_V: number;
    ROT_90_CW: number;
    ROT_180_CW: number;
    ROT_270_CW: number;
    BTN_UP: number;
    BTN_DOWN: number;
    BTN_LEFT: number;
    BTN_RIGHT: number;
    BTN_A: number;
    BTN_B: number;
    BTN_X: number;
    BTN_Y: number;
    BTN_L: number;
    BTN_R: number;
    BTN_START: number;
    BTN_SELECT: number;
    BTN_POINTER_A: number;
    BTN_POINTER_B: number;
    BTN_POINTER_C: number;
    BTN_POINTER_D: number;
    PLAYER_ONE: number;
    PLAYER_TWO: number;
    PLAYER_THREE: number;
    PLAYER_FOUR: number;
    AXIS_LEFT_X: number;
    AXIS_LEFT_Y: number;
    AXIS_RIGHT_X: number;
    AXIS_RIGHT_Y: number;
    AXIS_TRIGGER_L: number;
    ... 124 more ...;
    spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.
BT
.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => void
Draws text with a bitmap font through the indexed sprite pipeline. Supports proportional glyph widths and glyph-level offsets defined by the supplied {@link BitmapFont } . The font's underlying sprite sheet must have been indexized before calling this. Palette offset semantics and out-of-range behavior are identical to {@link BT.drawSprite } .
@since0.1.0@paramfont - Font asset used for rendering.@parampos - Text origin in display coordinates.@paramtext - String to render.@parampaletteOffset - Shift added to every stored glyph index before palette lookup (default 0).
printFont
(this.Demo.font: BitmapFont | null
@type{BitmapFont | null}
font
, new new Vector2i(x?: number, y?: number): Vector2i
Creates an integer 2D vector, truncating inputs toward zero.
@paramx - Horizontal component (defaults to 0).@paramy - Vertical component (defaults to 0).
Vector2i
(const glyphX: anyglyphX, const glyphY: anyglyphY), const char: anychar, 0);
} } // Writes this.tooltipAnchor so it covers the cell for `index`. ui.tooltip() docks its // box above whatever rectangle it is given. Demo.placeTooltipAnchor(index: any): voidplaceTooltipAnchor(index: anyindex) { const const col: numbercol = index: anyindex % const GRID_COLS: 16GRID_COLS; const const row: anyrow = Math.floor(index: anyindex / const GRID_COLS: 16GRID_COLS); this.Demo.tooltipAnchor: Rect2itooltipAnchor.Rect2i.set(x: number, y: number, width: number, height: number): Rect2i
Sets all components of this rectangle. Modifies this rectangle directly for maximum performance. WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.
@paramx - New x position.@paramy - New y position.@paramwidth - New width.@paramheight - New height.@returnsThis rectangle for chaining.
set
(
this.Demo.gridOriginX: anygridOriginX + const col: numbercol * const CELL_PITCH: numberCELL_PITCH, const GRID_ORIGIN_Y: 28GRID_ORIGIN_Y + const row: anyrow * const CELL_PITCH: numberCELL_PITCH, const CELL_SIZE: 23CELL_SIZE, const CELL_SIZE: 23CELL_SIZE, ); } // The words inside the tooltip for one glyph: its code point, or a short note about // the copy that just happened. Demo.tooltipLabel(index: any): stringtooltipLabel(index: anyindex) { const const codePoint: anycodePoint = this.Demo.codePoints: {}
@type{readonly number[]}
codePoints
[index: anyindex];
const const char: anychar = String.fromCodePoint(const codePoint: anycodePoint); if (this.Demo.copyStatus: anycopyStatus === const COPY_STATUS: anyCOPY_STATUS.COPIED && this.Demo.copyStatusIndex: numbercopyStatusIndex === index: anyindex) { return `Copied ${const char: anychar}`; } if (this.Demo.copyStatus: anycopyStatus === const COPY_STATUS: anyCOPY_STATUS.FAILED && this.Demo.copyStatusIndex: numbercopyStatusIndex === index: anyindex) { return 'Copy failed'; } // toString(16) turns the number into hexadecimal (base 16) digits, the way // Unicode code points are usually written. padStart(4, '0') adds leading zeros so // "U+00E9" is the same width as "U+2013". const const hex: anyhex = const codePoint: anycodePoint.toString(16).toUpperCase().padStart(4, '0'); return `U+${const hex: anyhex} (${const codePoint: anycodePoint}) ${const char: anychar}`; } } // Hand the Demo class to BLIT386 to start the demo loop. function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>
One-liner bootstrap function for BLIT386 demos. Handles canvas retrieval and engine initialization. Backend selection (WebGPU or software fallback) is managed internally by BTAPI. This function provides a streamlined way to start a demo with sensible defaults while allowing customization through options.
@since0.2.0@changed1.4.0 Calling `bootstrap()` again while already initialized now routes to a hot swap (via {@link registerHotReload}) when a Vite HMR context is registered, or logs a double-bootstrap guard and returns `false` otherwise - previously it silently started a second, unstoppable `GameLoop`.@changed1.7.0 Exposes `BT` on `window.BT` after bootstrap finishes, gated by {@link BootstrapOptions.exposeGlobal} (default: {@link BT.isDevMode}).@paramDemoClass - Demo class constructor implementing `IBTDemo` (optional `configure()` for hardware settings).@paramoptions - Optional configuration for IDs and callbacks.@returns`true` when the demo boots successfully; otherwise `false`.@example// Simplest usage - uses default IDs. bootstrap(MyDemo);@example// With custom options. bootstrap(MyDemo, { canvasID: 'custom-canvas', containerID: 'custom-container', onSuccess: () => console.log('Demo started!'), onError: (err) => analytics.trackError(err), });@example// Await the result. const success = await bootstrap(MyDemo); if (success) { console.log('Demo is running'); }
bootstrap
(class DemoDemo);