ui

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 13 Imported by: 0

README

github.com/vuka-lang/ui

The UI library for Vuka: what JSX in a .vuka file renders with. A file that imports github.com/vuka-lang/ui is a JSX file whose elements become ui.El, ui.Text, ui.Child, … (the package declares const VukaJSX = 1, Vuka's JSX target contract).

  • ui — Node (= templ.Component), elements and text, Safe, Style, ErrorBoundary, HTML rendering (String, Write, Handler), Walk and Renderer; stateful components (Live, On, Event).
  • ui/live — sessions that keep stateful components alive for a connected browser, and the wire protocol transports speak.
  • ui/term — renders a node tree as terminal text.
  • ui/templx — templ interop: children for templ components, the components of .templ files a vuka.File names.
import "github.com/vuka-lang/ui"

func Hello(name string) ui.Node {
	return <p>Hello, {name}!</p>
}

Documentation: https://vuka-lang.github.io/vuka/ui/

The code moved here from github.com/vuka-lang/vuka at commit 1af97d1 (v0.8.0); see that repository's history for its earlier changes.

MIT licensed.

Documentation

Index

Constants

View Source
const VukaJSX = 1

VukaJSX marks the package as a JSX target implementing version 1 of Vuka's contract: a .vuka file importing ui lowers its JSX to El, Text, Child, Fragment, Nodes and Try; on… attributes to On; tags naming a Stateful type to Component; and children of components that read them from their context (templ components) go through WithChildren.

Variables

This section is empty.

Functions

func AttachLive

func AttachLive(c Stateful, inst LiveInstance)

AttachLive binds a component to its session instance. Package live calls it.

func CheckHandler

func CheckHandler(fn any) error

CheckHandler reports a value an on… attribute can't take: a handler is a func(), or a func taking the event's value (a string, number or bool), a form (a struct or pointer to one, or url.Values) or an Event, optionally after a context.Context, returning nothing or an error.

func CheckStateful

func CheckStateful(c Stateful) error

CheckStateful reports a stateful component that embeds *Live rather than Live, which has no instance to attach.

func EventName

func EventName(attr string) string

EventName is the DOM event an on… attribute names: onClick is click, onKeyDown keydown.

func Handler

func Handler(f func(r *http.Request) (Node, error)) http.Handler

Handler serves the Node f builds for each request through templ.Handler: rendered in full with the request's context, then sent as text/html. An error from f or from rendering answers 500 with only the status text.

func KeyText

func KeyText(k any) string

KeyText is a key as text, its type and value: int=7.

func MountLive

func MountLive(ctx context.Context, c Stateful) error

MountLive runs c's Mount method, if it has one: Mount(), Mount() error, Mount(context.Context) or Mount(context.Context) error.

func SettleState

func SettleState(c Stateful)

SettleState marks c's Assigns unchanged, after a render.

func String

func String(ctx context.Context, n Node) (string, error)

String renders n as HTML into a string.

func TrackedState

func TrackedState(c Stateful) (tracked, changed bool)

TrackedState reports whether c's state is all Assigns (tracked), and whether one of them changed since SettleState. Package live calls it.

func Void

func Void(tag string) bool

Void reports whether tag is a void element: no children, no closing tag.

func Walk

func Walk(ctx context.Context, n Node, r Renderer) error

Walk sends n's tree to r. Frames, Groups, Builders and Child conversions are flattened (r never sees them); a TryNode with an error stops the walk with it; a Boundary walks its children into a recording first, and replays it into r — or, when they fail, walks Fallback(err) instead, so r never sees the failed part. Inside a Boundary an opaque Node is rendered while recording and replayed as Opaque of its HTML (a RawHTML). Children on a void element are an error. The first error from r stops the walk.

func WithLiveHost

func WithLiveHost(ctx context.Context, h LiveHost) context.Context

WithLiveHost is ctx carrying h.

func Write

func Write(w http.ResponseWriter, r *http.Request, n Node) error

Write renders n with r's context and sends it as text/html (unless a Content-Type is set already). The page is rendered in full first, so a render error is returned with nothing written.

Types

type Assign

type Assign[T any] struct {
	// contains filtered or unexported fields
}

Assign is a stateful component's state value whose changes a live session tracks: Set and Update mark it changed. A component whose state fields (its unexported ones, except those tagged `vuka:"-"`) are all Assigns renders again only when one of them changed or its props did; otherwise the session keeps its last render without calling Render.

type Row struct {
	ui.Live
	Name string            // a prop
	qty  ui.Assign[int]  // state
}

func (r *Row) Inc() { r.qty.Update(func(n int) int { return n + 1 }) }

Render must then read nothing but props and Assigns (and fields tagged `vuka:"-"`, which must not change what it renders); in tests (or with VUKA_LIVE_VERIFY=1) the session renders skipped components anyway and fails when the result differs.

func (*Assign[T]) Get

func (a *Assign[T]) Get() T

Get is the value.

func (*Assign[T]) Set

func (a *Assign[T]) Set(v T)

Set sets the value and marks it changed.

func (*Assign[T]) Update

func (a *Assign[T]) Update(f func(T) T)

Update sets the value to f of it and marks it changed.

type Attr

type Attr struct {
	Name  string
	Value any
}

Attr is one attribute of an element. In HTML it renders the way templ renders the same attribute written as an expression:

  • nil and false omit it, true writes it bare;
  • class takes a string or anything templ.Classes does ([]string, map[string]bool, templ.KV, …);
  • style takes a Style or anything templ's style attributes do, sanitized by templ (an unsafe property or value becomes templ's placeholder);
  • href on a and link, action on form and data on object are URLs: templ.URL keeps relative URLs and http, https, mailto, tel, ftp and ftps, and turns anything else into about:invalid#TemplFailedSanitizationURL; a templ.SafeURL passes as is. Like templ, other attributes (img src, …) aren't URL-checked;
  • a templ.ComponentScript writes its Call; a Node is rendered and escaped; anything else goes through fmt.Sprint and is escaped (templ.ResolveAttributeValue).

className and htmlFor name class and for. Names must be letters, digits, - _ : and . (templ only ever sees literal names; here a dynamic one could smuggle in another attribute), else Render fails.

type Boundary

type Boundary struct {
	Fallback func(err error) Node
	Children []Node
}

Boundary renders Children or, when one of them fails, Fallback(err) in place of everything they produced.

func (Boundary) Render

func (n Boundary) Render(ctx context.Context, w io.Writer) error

type Builder

type Builder func(add func(Node))

Builder produces children as it runs: a loop, if or match block. It runs once per Render or Walk, each added child rendered as it is added.

func (Builder) Render

func (n Builder) Render(ctx context.Context, w io.Writer) error

type ComponentNode

type ComponentNode struct {
	Site  string
	Key   any
	Props Stateful
}

ComponentNode is a stateful component's tag, <Counter Start={5} />: Props is a fresh value carrying the attributes, never mounted itself. Site (the tag's place in the source), Key and the instance rendering it identify the instance a session keeps.

func (*ComponentNode) Render

func (n *ComponentNode) Render(ctx context.Context, w io.Writer) error

type Element

type Element struct {
	Tag      string
	Attrs    []Attr
	Children []Node
}

Element is an element: <Tag Attrs>Children</Tag>. A void element (br, img, input, …; see Void) has no closing tag, and children on one are an error; in HTML, html is preceded by the doctype.

Text inside script follows templ's Go expressions in a script: it is JSON-encoded (templ's runtime.ScriptContentOutsideStringLiteral), so it arrives as a quoted JS string with < > & escaped. templ has no expressions inside style; here text in a style element is written as is with "</" written "<\/", so it can't end the element.

func (*Element) Render

func (n *Element) Render(ctx context.Context, w io.Writer) error

type Event

type Event struct {
	Target string     `json:"target"`
	Type   string     `json:"event,omitempty"`
	Value  string     `json:"value,omitempty"`
	Key    string     `json:"key,omitempty"`
	Form   url.Values `json:"form,omitempty"`
}

Event is what the browser reports of an event, and what a handler taking a ui.Event receives. Target is the handler's id; Type the DOM event; Value the target element's value (for a checkbox, "true" or "false"); Key the key of a keyboard event; Form the fields of the form a submit event comes from.

type EventHandler

type EventHandler struct{ Fn any }

EventHandler is an on… attribute's expression on an element: onClick={c.Inc}. Under a live session it renders as data-vk-on-<event>="<handler id>" and the session calls Fn when the browser reports the event; without one it renders nothing. A templ.ComponentScript renders as templ renders it.

func On

func On(fn any) EventHandler

On is an on… attribute's expression.

type Frame

type Frame struct {
	FP    uint64
	Shape string
	Kids  []Node
	// contains filtered or unexported fields
}

Frame is a piece of JSX as the compiler saw it: a tree's root, a block's body (one per loop iteration or branch taken), or a component tag's children. It renders exactly as its Kids would; under a live session it is also a frame of the render tree, whose markup splits into statics (what the source fixes, identical every render) and dynamics (what Go expressions produce), so a session sends the statics once and then only the dynamics that changed.

FP fingerprints what its statics are made of: its tags, attribute names and literal values, text, and where the expressions go — pieces of JSX that render the same statics share it, however they are formatted. Shape describes Kids, one item each, in the order they appear:

t        text from the source
h b c    a dynamic: an {expr} child, a {for}/{if}/{match} block, a component tag
E…(…)    an element: a flag per attribute, s (a literal) or d (an expression),
         then its children's items in parentheses
G(…)     a fragment, <>…</>

Hand-written trees need no Frame: a Node that isn't one is a single dynamic of the frame around it, rendered to HTML.

func (*Frame) Render

func (n *Frame) Render(ctx context.Context, w io.Writer) error

type Group

type Group []Node

Group is children with no element around them.

func (Group) Render

func (n Group) Render(ctx context.Context, w io.Writer) error

type HTMLRenderer

type HTMLRenderer struct {
	// contains filtered or unexported fields
}

HTMLRenderer is the Renderer writing HTML: what Render does. Escaping and sanitizing are templ's (see Attr and Element).

func NewHTMLRenderer

func NewHTMLRenderer(ctx context.Context, w io.Writer) *HTMLRenderer

NewHTMLRenderer is an HTMLRenderer writing to w, unbuffered; ctx renders Node attribute values.

func (*HTMLRenderer) Close

func (h *HTMLRenderer) Close(e *Element) error

func (*HTMLRenderer) Opaque

func (h *HTMLRenderer) Opaque(ctx context.Context, n Node) error

func (*HTMLRenderer) Open

func (h *HTMLRenderer) Open(e *Element) error

func (*HTMLRenderer) Raw

func (h *HTMLRenderer) Raw(html string) error

func (*HTMLRenderer) Text

func (h *HTMLRenderer) Text(s string) error

type Live

type Live struct {
	// contains filtered or unexported fields
}

Live, embedded by value in a struct whose pointer has a Render() Node method, makes the struct a stateful component:

type Counter struct {
	ui.Live
	Start int // exported fields are props, set from the tag's attributes
	n     int // unexported fields are state
}

Rendered without a live session (ui.String, Handler, …) a stateful component mounts and renders once, as plain HTML. Under a session (package live) it is an instance that keeps its state between renders and answers the events of the elements it renders.

func (*Live) ID

func (l *Live) ID() string

ID is the instance's id in its session, the value of its root element's data-vk-id; "" when rendered without one.

func (*Live) Subscribe

func (l *Live) Subscribe(topics ...string)

Subscribe asks the session for the messages published on topics, delivered to the component's Update methods. Without a session it does nothing.

type LiveHost

type LiveHost interface {
	Component(ctx context.Context, n *ComponentNode, w io.Writer) error
	Handler(ctx context.Context, fn any) string
	Keyed(ctx context.Context, key any) context.Context
}

LiveHost renders stateful components and registers event handlers for a live session. Package live implements it; ctx carries it.

type LiveInstance

type LiveInstance interface {
	ID() string
	Subscribe(topics ...string)
}

LiveInstance is a session's side of a component instance. Package live implements it.

type Node

type Node = templ.Component

Node is anything that renders HTML: what a JSX expression evaluates to. It is templ.Component itself, so Vuka components are templ components and templ components are Vuka children, with nothing in between.

The Nodes built here (*Element, TextNode, RawHTML, Group, Builder, TryNode, Boundary, *Frame) are also a tree any Renderer can Walk; any other Node — a templ component, a NodeFunc — is opaque: it can only render HTML. Nodes are immutable values; rendering one concurrently is safe.

func Child

func Child(v any) Node

Child is a {expr} child: a Node as is, nil as nothing, an error as its message, a []Node or []any as each element in turn, anything else as Text.

func Component

func Component(site string, key any, props Stateful) Node

Component is a stateful component's tag.

func El

func El(tag string, attrs []Attr, children ...Node) Node

El is an element.

func ErrorBoundary

func ErrorBoundary(fallback func(err error) Node, children ...Node) Node

ErrorBoundary renders children, or — when one of them fails — fallback(err) in place of everything they wrote.

func F

func F(fp uint64, shape string, kids ...Node) Node

F is a Frame. Generated code calls it.

func Fragment

func Fragment(children ...Node) Node

Fragment renders children in order, with no element around them.

func Nodes

func Nodes(build func(add func(Node))) Node

Nodes is the children a loop, if or match block produces.

func RootAttrs

func RootAttrs(n Node, attrs ...Attr) (Node, bool)

RootAttrs is n with attrs added after its root element's own, when n is an element or a frame of one element; ok is false otherwise.

func Safe

func Safe(html string) Node

Safe is trusted HTML, written as is. Never pass it user input.

func Text

func Text(v any) Node

Text is text: v formatted like fmt.Sprint, nil as nothing.

func Try

func Try(n Node, err error) Node

Try is a (Node, error) component's result as a child.

func WithChildren

func WithChildren(c Node, children Node) Node

WithChildren renders c with children as its templ children (templ.WithChildren), so a templ component's { children... } renders them: <Card>{…}</Card> on a templ Card.

type NodeFunc

type NodeFunc = templ.ComponentFunc

NodeFunc is a function rendering HTML: templ.ComponentFunc.

type RawHTML

type RawHTML string

RawHTML is trusted HTML, written as is.

func (RawHTML) Render

func (n RawHTML) Render(ctx context.Context, w io.Writer) error

type Renderer

type Renderer interface {
	Open(e *Element) error  // before children
	Close(e *Element) error // after children (not called for void elements)
	Text(s string) error
	Raw(html string) error                    // Safe content
	Opaque(ctx context.Context, n Node) error // a component that only renders HTML (templ, NodeFunc)
}

Renderer receives a node tree in document order; Walk drives it. The HTML renderer behind every Node's Render is one; package term is another.

type Stateful

type Stateful interface {
	Render() Node
	// contains filtered or unexported methods
}

Stateful is a stateful component: a pointer to a struct embedding Live, with a Render method.

func CopyLive

func CopyLive(c Stateful) Stateful

CopyLive is a shallow copy of c.

type Style

type Style map[string]string

Style is an inline style: style={ui.Style{"color": "red"}}, rendered by templ as "color:red;" sorted by property, each property and value sanitized.

type TextNode

type TextNode string

TextNode is text, escaped when rendered.

func (TextNode) Render

func (n TextNode) Render(ctx context.Context, w io.Writer) error

type Tree

type Tree struct {
	Key     uint64
	Statics []string
	Dyn     []any
}

Tree is a frame of a live render tree: the markup of a piece of JSX as statics, the strings its source fixes, around dynamics, what its expressions rendered. Its HTML is Statics[0] + Dyn[0] + Statics[1] + … + Statics[len(Dyn)]. A dynamic is a string of HTML, a nested *Tree, a *TreeList (a block's items) or a TreeRef (a stateful component instance, rendered by the session as a tree of its own).

Key identifies the statics: the frame's fingerprint in its render context. Two trees with the same Key have the same Statics, which are shared and must not be modified.

func BuildTree

func BuildTree(ctx context.Context, n Node, verify bool) (*Tree, error)

BuildTree renders n as a render tree. A Frame becomes a Tree split as its shape says; anything else is a frame of one dynamic. ctx should carry a live host; with verify, statics already known for a Key are rendered again and compared, and a difference is an error.

func OpaqueTree

func OpaqueTree(dyns ...any) *Tree

OpaqueTree is a frame with no statics of its own around dyns.

func (*Tree) HTML

func (t *Tree) HTML(b *strings.Builder, ref func(b *strings.Builder, id string))

HTML writes t's HTML into b, each TreeRef as ref writes it.

type TreeHost

type TreeHost interface {
	LiveHost
	ComponentRef(ctx context.Context, n *ComponentNode) (id string, err error)
}

TreeHost is a LiveHost that can render a stateful component's tag as a reference to its instance. Package live implements it.

type TreeList

type TreeList struct {
	Items []*Tree
	Keys  []string
}

TreeList is a {for}, {if} or {match} block's items, a frame per iteration or branch taken. Keys are the items' keys, from the key attribute of each item's first element or component tag; nil unless every item has one.

type TreeRef

type TreeRef string

TreeRef is a stateful component instance in a render tree: its id.

type TryNode

type TryNode struct {
	Node Node
	Err  error
}

TryNode is a (Node, error) component's result as a child: Err fails the render, else Node renders.

func (TryNode) Render

func (n TryNode) Render(ctx context.Context, w io.Writer) error

Directories

Path Synopsis
Package live runs stateful components for a connected browser: a Session keeps the component instances of one page, calls their event handlers and Update methods, and answers each with the HTML of the components whose output changed.
Package live runs stateful components for a connected browser: a Session keeps the component instances of one page, calls their event handlers and Update methods, and answers each with the HTML of the components whose output changed.
internal/demo
Package demo is stateful components compiled by the Vuka compiler, for the live package's property tests and benchmarks.
Package demo is stateful components compiled by the Vuka compiler, for the live package's property tests and benchmarks.
livetest
Package livetest is a reference client of the live protocol, in Go: it keeps what a browser runtime keeps — the page's HTML for v1, each instance's render tree for v2 — and applies the server's messages the way a runtime must, so tests can check that what a client ends up with is what the server rendered.
Package livetest is a reference client of the live protocol, in Go: it keeps what a browser runtime keeps — the page's HTML for v1, each instance's render tree for v2 — and applies the server's messages the way a runtime must, so tests can check that what a client ends up with is what the server rendered.
Package templx bridges templ: children for templ components, and the components of .templ files a vuka.File names.
Package templx bridges templ: children for templ components, and the components of .templ files a vuka.File names.
Package term renders a ui Node tree as terminal text: headings, paragraphs word-wrapped at a width, lists, tables, quotes, code, links, and inline bold/italic/underline as ANSI styles when Color is on.
Package term renders a ui Node tree as terminal text: headings, paragraphs word-wrapped at a width, lists, tables, quotes, code, links, and inline bold/italic/underline as ANSI styles when Color is on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL