ui
js/ui.ts
fino:ui — host-neutral component construction and rendering.
fino:ui is the portable core for JSX-style Fino interfaces. It owns VNode
construction, function components, and keyed reconciliation into a host
adapter. It re-exports the reactive primitives from fino:signals for
compatibility, but the signal kernel itself is shared runtime
infrastructure. This module does not know about the DOM, HTML, terminal
cells, input devices, or styling rules. Renderers such as fino:tty/tui
provide those host details.
Design
Components are plain functions that receive normalized props and a
children array. State is held in explicit Signal objects rather than
hook order. Hosts provide a small imperative adapter for creating nodes,
moving children, updating props, and batching mutations.
/** @jsxImportSource fino:ui *\/
import { createSignal } from 'fino:ui';
const count = createSignal(0);
function Counter() {
return <label>Count: {count.get()}</label>;
}
count.subscribe(() => {
// Ask the selected host renderer to render Counter again.
});
Types
type Child = VNode | string | number | boolean | null | undefined | Child[]
Primitive child value accepted by h().
Numbers are stringified, nested arrays are flattened, and null,
undefined, and boolean placeholders are ignored during normalization.
type Component<P = Record<string, unknown>> = (props: P & {
children?: NormalizedChild[];
}) => VNode
Function component accepted by h().
Components receive normalized props plus an optional normalized children
array. They return a concrete VNode; components do not keep hidden hook state.
type VNodeType = string | typeof Fragment | Component<any>
Host-neutral element or component type accepted by h().
Strings are host element names, Fragment groups children without a host
node, and functions are invoked as components.
type NormalizedChild = VNode | string
Child value after normalization.
A normalized child is either a VNode or text. Empty placeholders and nested arrays have already been removed.
type Props = Record<string, unknown>
Props object stored on a VNode after key and children are removed.
Interfaces
interface VNode {
Host-neutral virtual node produced by h() and the JSX runtime.
type is either a host element name or a renderer-specific component output
type. props never includes key or children; children is already
flattened and does not contain null, undefined, or boolean placeholders.
Properties
type: string
Host element name, or 'fragment' for fragment VNodes.
props: Props
Host props with key and children removed.
children: NormalizedChild[]
Flattened child nodes and text.
key: string | number | null
Optional reconciliation key copied from the original props.
interface HostAdapter< Node, Root > {
Host adapter consumed by createRenderer().
Hosts own the concrete node representation. The renderer calls beginUpdate
and endUpdate once around each render() call when those hooks are
provided.
Contract
createNode() and createText() allocate host nodes. insertChild(),
moveChild(), and removeChild() mutate child order under a parent or root.
updateNode() replaces host props for an existing element, and setText()
updates an existing text node.
Methods
createNode(type: string, props: Props): Node
Create a host element node for type and props.
createText(text: string): Node
Create a host text node.
updateNode(node: Node, props: Props): void
Replace or patch props on an existing host element node.
setText(node: Node, text: string): void
Update an existing host text node.
insertChild(parent: Node | Root, child: Node, index: number): void
Insert child under parent at index.
moveChild(parent: Node | Root, child: Node, index: number): void
Move an existing child under parent to index.
removeChild(parent: Node | Root, child: Node): void
Remove child from parent.
beginUpdate?(): void
Optional hook called before each render pass.
endUpdate?(): void
Optional hook called after each render pass, including failed passes.
Constants
const Fragment
Fragment marker used by JSX to group children without adding a host node.
h(Fragment, null, ...) returns the normalized children to its parent rather
than creating a renderer-visible node.
Functions
function h(type: VNodeType, props: Props | null, ...children: Child[]): VNode
Construct a host-neutral VNode or invoke a function component.
key is copied out of props and stored on the VNode for reconciliation.
children from props and variadic children are merged, flattened, and
stripped of empty placeholders. Function components receive the normalized
children as props.children.
import { h } from 'fino:ui';
const button = h('button', { key: 'save', disabled: true }, 'Save');
function createRenderer<
Node,
Root
>(host: HostAdapter<Node, Root>)
Create a renderer for a concrete host adapter.
The renderer keeps one mounted tree per root object. Re-rendering the same root reconciles by element type and child keys, producing host updates inside one update batch.
Children without explicit keys are matched by position. Text nodes are keyed
by their index, while VNodes prefer their key and fall back to index.
import { createRenderer, h } from 'fino:ui';
const renderer = createRenderer(host);
renderer.render(h('label', null, 'Ready'), root);