# Registry reference

A quick catalog of the components and functions Banto ships with.
This doc is hand-curated (categories + tightened descriptions) but
**drift-checked** against the canonical registries in
`packages/core/src/registries/` by `npm run docs:check` — every item in
those JSON files must appear here and vice versa. When in doubt, the JSON
files are the source of truth.

## Contents

- [Components: `cpnt.X`](#components-cpntx)
- [Server functions: `func.X`](#server-functions-funcx)
- [Client functions: `client.X`](#client-functions-clientx)
- [`Object` built-ins](#object-built-ins)

---

## Components: `cpnt.X`

Used inside `host`/`plyr`/`cpnt` blocks anywhere a `child` (or
nested `element`) is expected. Pass an object whose fields match the
component's `params`. Optional fields are marked `?` and may be
omitted.

### Layout & screens

| Component        | Params                                                                                                  | What it is |
|------------------|---------------------------------------------------------------------------------------------------------|------------|
| `HostLobby`      | `{ onStart: action, roomCode: string, playerButtons: { name: string, sessionId: string, onClick: action(string) }[] }` | The default host lobby. Purple background, room code, player list with kick buttons. |
| `PlayerScreen`   | `{ name: string, score: number, roomCode: string, child: element }`                                     | Wrapper for player views — shows the player's name, score, and room code along with the inner `child`. |
| `HostScreen`     | `{ roomCode: string, onSkip: action, sideBar: { label: string, child: element }[], child: element }`    | Wrapper for host views — main area + side bar. Default white background. |
| `container`      | `{ child?: element, class: style }`                                                                     | A styleable wrapper. The Banto equivalent of `<div>`. Omit `child` for a purely decorative element. |
| `Icon`           | `{ id: string, class?: style }`                                                                         | A [lucide](https://lucide.dev/icons) icon rendered by name. `id` accepts a kebab-case id (`alarm-clock`) or PascalCase name (`AlarmClock`); an unknown name renders nothing. Style the SVG via `class` (size with `w-8 h-8`, color with `text-*`). |
| `conditional`    | `{ when: boolean, child: element }[]`                                                                   | Renders the first entry whose `when` is true. Useful for picking a child based on data. |
| `portal`         | `{ child: element }`                                                                                    | Renders `child` into a React portal on `document.body`, so it escapes parent layout / overflow / stacking context. Use for overlays (toasts, modals, floating menus). |
| `modal`          | `{ isOpen: boolean, onClose: action, child: element }`                                                  | A full-screen dimmed overlay centering `child` while `isOpen` is true. Clicking the backdrop (outside `child`) fires `onClose`. The child owns its styling; the modal only supplies the centered, dimmed overlay. |
| `cursorLayer`    | `{ cursors: { id: string, x: number, y: number, color: string }[], onMove: action({ x: number, y: number }) }` | A full-bleed overlay rendering one colored cursor per `cursors` entry and reporting this client's pointer movement via `onMove`. Positions are percentages (0–100) of the layer's box, so cursors stay aligned across differently-sized viewports. Drive shared-cursor experiences by writing each `onMove` into `var.*` keyed by session id. |

### Buttons & inputs

| Component         | Params                                                                                                                                         | What it is |
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------|------------|
| `button`          | `{ child: element, onClick: action, class: style, keyboardNav?: boolean }`                                                                     | A clickable element. `keyboardNav` defaults to `true`; set to `false` to opt out of keyboard focus (renders `tabIndex={-1}` and suppresses focus-on-mousedown). |
| `textInput`       | `{ value: string, onChange?: action(string), onSubmit?: action(string), hint?: string, maxLength?: number, class?: style }`                    | Single-line text input. Bind `value` to a `cstate.<name>` so typing stays local. |
| `numberInput`     | `{ value: number, onChange?: action(number), onSubmit?: action(number), hint?: string, min?: number, max?: number, step?: number, class?: style }` | Single-line numeric input. Empty input delivers `0`. |
| `textArea`        | `{ value: string, onChange?: action(string), onSubmit?: action(string), hint?: string, maxLength?: number, rows?: number, class?: style }`     | Multi-line text input. `onSubmit` fires on Ctrl/Cmd+Enter; Enter alone inserts a newline. |
| `select`          | `{ value: string, options: { label: string, value: string }[], onChange?: action(string), placeholder?: string, class?: style }`               | Dropdown. |
| `checkbox`        | `{ checked: boolean, label?: string, onChange?: action(boolean), class?: style }`                                                              | Single boolean checkbox. |
| `checkboxGroup`   | `{ values: string[], options: { label: string, value: string }[], onChange?: action(string[]), class?: style }`                                | Multi-select checkboxes. `onChange` receives the full new selection list. |
| `radioGroup`      | `{ value: string, options: { label: string, value: string }[], onChange?: action(string), class?: style }`                                     | Single-select radios. |
| `slider`          | `{ value: number, min: number, max: number, step?: number, onChange?: action(number), onSubmit?: action(number), class?: style }`              | Range slider. `onChange` ticks during drag; `onSubmit` fires on release. |
| `keyboard`        | `{ value: string, onChange?: action(string), onSubmit?: action(string), maxLength?: number, showNumbers?: boolean, showSpace?: boolean }`      | Bottom-pinned QWERTY keyboard. A–Z plus ENTER (bottom-left) and backspace (bottom-right); optional 0–9 row when `showNumbers` is true; optional spacebar row when `showSpace` is true (also enables physical space key). Doesn't render the value itself — bind `value` to a `cstate.<name>`. `onChange` fires on every key with the new full string; `onSubmit` fires on ENTER. Caps at ~30% viewport width on desktop, full width on mobile. |

### Drawing

| Component | Params | What it is |
|-----------|--------|------------|
| `Canvas`  | `{ mode: "draw" | "view", value: { lines: { points: number[], color: string, width: number }[], bg: string, w: number, h: number }, onChange?: action({ lines: { points: number[], color: string, width: number }[], bg: string, w: number, h: number }), colors?: string[], class?: style }` | A react-konva drawing surface. In `mode: "draw"` it shows a toolbar (color palette, pencil/bucket, stroke-width slider, undo, clear) and fires `onChange` with the full new sketch after every stroke or toolbar action. In `mode: "view"` it renders the saved sketch read-only, scaled to fit. Each line stores Konva-format flat `points: [x0,y0,x1,y1,...]`, a stroke `color`, and a stroke `width`; `bg` is the background fill; `w`/`h` are the original surface dimensions used to scale on view. Bind `value` to a `var.*` slot and write the action's argument back to it in `onChange`. `colors` overrides the toolbar palette (defaults to a 10-color set). |

### Media (custom assets)

Render your own uploaded images and audio. `src` takes an `asst.<name>`
reference (build-validated to the right kind) or a raw asset id / URL string.
See [Custom image & audio assets](./language-guide.md#custom-image--audio-assets)
for the upload flow (`banto assets push`).

| Component | Params | What it is |
|-----------|--------|------------|
| `image`   | `{ src: image, alt?: string, fit?: "cover" | "contain", class?: style }` | Renders a custom image asset. `fit` maps to CSS `object-fit` (`cover` crops to fill, `contain` letterboxes; default `cover`). `alt` is the accessible label. Size it via `class`. A raw string `src` is resolved at runtime and renders nothing if it can't load. |
| `audio`   | `{ src: audio, autoplay?: boolean, loop?: boolean, controls?: boolean, class?: style }` | Plays a custom audio asset. Set `controls` to show a native player; omit it for background/ambient audio driven by `autoplay`/`loop`. For a one-shot sound cue fired from an action, use `client.playSound(asst.<name>)` instead. |

### Animation

| Component           | Params                                                                                                                                                                                                                                                         | What it is |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|
| `AnimatedComponent` | `{ child: element, class: style, onStart?: action, onComplete?: action, delay: number, transition: { type?: "spring" | "tween", stiffness?: number, damping?: number, duration?: number }, animate: { scale?: number, rotate?: number, opacity?: number, x?: number, y?: number }, initial: { scale?: number, rotate?: number, opacity?: number, x?: number, y?: number } }` | A framer-motion-style entrance animation wrapper. `initial` is the start state; `animate` is the resting state; `transition` controls timing. |

> **Heads up.** Components, params, and slot types evolve. The
> registry JSON in `packages/core/src/registries/components.json` is
> the authoritative list. If something here looks stale, that file
> wins.

---

## Server functions: `func.X`

Run on the server. Use as expressions everywhere except where noted
(`returns: action` functions can also be statements in an action
body; `returns: void` functions are statement-only).

### Time & RNG

| Call | Returns | Notes |
|---|---|---|
| `func.currentTime()` | `number` | Milliseconds since Unix epoch. |
| `func.randomInt({min, max})` | `number` | Random int in `[min, max]`. Bounds swap if reversed; non-integer bounds are floored. |
| `func.randomFloat({min, max})` | `number` | Random float in `[min, max)`. |
| `func.shuffle(arr)` | `any[]` | Non-mutating; returns the array in random order. Works on any element type. |
| `func.shuffleStrings(arr)` | `string[]` | Deprecated — prefer `func.shuffle`. |
| `func.pickRandom(arr)` | `any` | One random element. Returns `undefined` for an empty list. |
| `func.pickUnused({total, used})` | `number` | Random int in `[0, total)` not in `used`. Returns `-1` if all used. |
| `func.range({start, end, step?})` | `number[]` | Inclusive of `start`, exclusive of `end`. `step` defaults to 1; negative counts down. |

### Math

| Call | Returns | Notes |
|---|---|---|
| `func.floor(n)` | `number` | Equivalent to `Math.floor`. |
| `func.ceil(n)` | `number` | Equivalent to `Math.ceil`. |
| `func.round(n)` | `number` | Half-up. Equivalent to `Math.round`. |
| `func.abs(n)` | `number` | Absolute value. |
| `func.clamp({value, min, max})` | `number` | Constrain to `[min, max]`. Bounds swap if reversed. |
| `func.sum(arr)` | `number` | Empty → 0. |
| `func.avg(arr)` | `number` | Empty → 0 (not NaN, so it renders cleanly). |
| `func.minOf(arr)` | `number` | Empty → 0. |
| `func.maxOf(arr)` | `number` | Empty → 0. |
| `func.toInt(value)` | `number` | Coerces to int; `-1` on failure. |

### Strings

| Call | Returns | Notes |
|---|---|---|
| `func.isAlpha(s)` | `boolean` | True for non-empty ASCII letter strings (A–Z, a–z). Empty / mixed → false. |
| `func.isNumeric(s)` | `boolean` | True for non-empty ASCII digit strings (0–9). No signs, decimals, or whitespace. Pair with `func.toInt` for the value. |

### Collections

| Call | Returns | Notes |
|---|---|---|
| `func.size(obj)` | `number` | Sugar over `Object.keys(obj).length`. |
| `func.isEmpty(any)` | `boolean` | True for `null`, `undefined`, `""`, `[]`, `{}`. |
| `func.hasKey({obj, key})` | `boolean` | True if `obj` has its own property `key`. |
| `func.first(arr)` | `any` | First element or `undefined`. |
| `func.last(arr)` | `any` | Last element or `undefined`. |
| `func.unique(arr)` | `any[]` | First-occurrence dedup, `===` equality (no deep dedup). |
| `func.setHas({items, value})` | `boolean` | Set-backed membership check. Builds a `Set` from `items` then checks `value`; O(1) lookup, cheaper than `items.includes(value)` for large lists. `===` equality. |
| `func.equals({a, b})` | `boolean` | Deep structural equality. |
| `func.merge({a, b})` | `object` | Shallow `{...a, ...b}`. Non-mutating. |
| `func.removeKey({obj, key})` | `object` | Copy of `obj` without `key`. Non-mutating. The cleanest way to drop an entry from a record. |
| `func.mapValues({obj, key})` | `object` | Project a property out of every value: `{[k]: obj[k][key]}`. |
| `func.groupBy({items, key})` | `object` | Group `items` into a record keyed by `item[key]`. Missing keys grouped under `""`. |
| `func.fill({value, n})` | `any[]` | Length-`n` array where every entry is `value`. `n ≤ 0` or non-integer → `[]`. Pad a list to a fixed length: `pd.guesses.concat(func.fill({value: emptyRow, n: 6})).slice(0, 6)`. The same reference is repeated — mutating one entry mutates all. |

### Game control & UX

| Call | Returns | Notes |
|---|---|---|
| `func.kickPlayer(sessionId)` | `action` | Disconnects the player and removes them from `var.players`. Statement-callable from action bodies. |
| `func.notify({to, message, type?})` | `void` | Pop a toast. `to` ∈ `"host" | "all" | <sessionId>`; `type` ∈ `"error" | "success" | "standard"` (default `"standard"`). Fire-and-forget — unknown sessionIds drop silently. |
| `func.log({message, level?})` | `void` | Write to the server log. `level` defaults to `"info"`. Handy for debugging your game's server logic. |

> **About the canonical list.** `packages/core/src/registries/functions.json`
> is the single source of truth. New functions are added there.

---

## Client functions: `client.X`

Run on the player's browser. Restrictions:

- May only appear inside an **action arrow body** (an `onClick`,
   `onChange`, `onSubmit`, etc.).
- May not appear inside an iterator body (`.map`, `.forEach`, …).
- Always returns `void`; can't be used as an expression.

| Call | Argument |
|---|---|
| `client.playSound(sound)` | A built-in cue name — `"tick"`, `"ding"`, `"whoosh"`, `"chching"`, `"applause"`, `"countdown"`, `"anticipation"`, `"waiting"` — **or** an `audio` asset (`asst.<name>`) to play a custom clip. |

The compiler optimizes statically-known calls (e.g., a literal cue name
or a single `asst.<name>`) so they fire instantly on click without a
server round-trip. Calls with non-literal arguments still work — they
just dispatch via the server.

---

## `Object` built-ins

`Object.X(...)` works the way you expect from JavaScript:

| Call | Returns | Notes |
|---|---|---|
| `Object.keys(obj)` | `string[]` | The object's own keys. |
| `Object.values(obj)` | `T[]` | The object's own values. |
| `Object.entries(obj)` | `[string, T][]` | Tuples. The standard input to `.map(([key, value]) => ...)`. |
| `Object.fromEntries(arr)` | `object` | Inverse of `Object.entries`. |

These are the only `Object.*` calls Banto recognizes. Anything else
(`Object.assign`, `Object.freeze`, etc.) is a compile error.