SurfaceNode tree; each frame UiFrame measures it, places rects, emits otter-render commands, and records click targets.
Core objects
UiState(capacities)
Long-lived state on your app struct. Holds:
commands: the frame’sDefaultCommandListelements: laid-out element rects (for inspector/debug)hit_regions: interactive regions forhitTestanddispatchoverlays/tooltips: queued after the root tree rendersinput: last pointer, hover, active, and focus idsscroll_states,animation_states,text_states: keyed bySurfaceId
FrameError.Overflow.
UiFrame
Short-lived handle from ui_state.begin(options). Call render, queueOverlay, queueTooltip, then finish. Don’t keep it across frames.
FrameOptions
Pass
text_system and text_scratch when the UI shows user-typed text, RTL labels, or CJK. Plain ASCII labels can skip them.
Frame lifecycle
render(node, bounds)
- Measure the node subtree (content intrinsic size).
- Apply
LayoutSpecsizing rules insidebounds. - Place children and emit draw commands for leaf content.
- Register hit regions for nodes with
hitset.
finish()
Renders queued overlays and tooltips on top, updates debug metrics, finalizes overlay damage. Call finish() before rasterizing.
state.rasterize(surface, damage_rects, full_redraw)
Hands the command list to otter-render’s quad_renderer. Pass damage rects from otter-wayland DamageTracker for partial redraws.
Stable ids with SurfaceId
Every interactive or inspectable node needs an id:
named("settings.save")hashes a string at runtimenamedComptime("settings.save")hashes at compile time (preferred)child(parent, "suffix")/childInt(parent, n)builds hierarchical ids for list rows
Building node trees
Static children array
Comptime tuple via staticChildren
otter-note/src/surface.zig).
Theme and disabled state
Leaf emitters read colors fromFrameOptions.theme when spec color fields are null. Set disabled = true on control specs (for example ButtonSpec.disabled) to gray out content and mark hit regions non-enabled.
For app-specific chrome, pass styles: &my_style_set or set explicit colors on each spec.
Debug overlays
Toggle with Ctrl+Shift+I at runtime, or start with--inspect / --metrics. UiState records frame timing, command counts, and memory in debug_metrics.
Common mistakes
Next
- Layout model:
NodeKind,SizeRule, flex, alignment - Nodes and controls: per-control spec fields
- Input and overlays: pointer routing and popups

