respo

A tiny virtual DOM library

virtual-dom
Download zip
Author
Version
0.3.4
License
Apache-2.0
Last updated
3 months ago
Downloads
2K

#Respo in MoonBit

A tiny virtual DOM library ported from Respo.rs.

Core ideas:

  • Immutable data by design - Store and component states must be immutable. Updates create new values using record update syntax ({ ..old, field: new_value }). Use Ref[T] for mutable references at the application level
  • Hot reload friendly for better dev experience
  • Embraces types and autocompletion
  • States are stored in a single atom, which is a tree structure with a concept like cursor
  • Uni-directional data flow, dispatching is only allowed when user events occur
  • Effects are for DOM updates, rather than dispatching actions
  • Physical equality optimization - Since data is immutable, unchanged references are skipped during re-rendering

This project is in beta stage. APIs and structure are relatively stable.

#Immutable Data Requirements

Respo requires all state to be immutable:

// Store must be immutable - no `mut` fields
struct Store {
counted : Int
tasks : @immut/array.T[Task] // Use immutable collections
states : RespoStatesTree
}

// Update returns a new Store, never mutates in place
fn Store::update(self : Store, op : ActionOp) -> Store {
match op {
Increment => { ..self, counted: self.counted + 1 }
// ...
}
}

// At top level, wrap in Ref for mutability
let app_store : Ref[Store] = Ref::new(Store::default())
app_store.val = app_store.val.update(action) // Replace with new value

This design enables:

  • Efficient change detection via reference equality (physical_equal)
  • Predictable state management
  • Safe concurrent access patterns

#Usage

moon add tiye/respo

To use Respo, start with the boilerplate at https://github.com/Respo/respo-moonbit-workflow

#License

Apache-2.0

ObscureState

type ObscureState

MoonBit does not allow trait object with Selfs, use ffi js value to bypass.

RespoApp

pub(all) struct RespoApp[Model] {
store : Ref[Model]
storage_key : String
mount_target :
Node

}

get basic App structure

RespoApp::backup_model_beforeunload

fn[Model : ToJson] RespoApp::backup_model_beforeunload(self : RespoApp[Model]) -> Unit

backup store to local storage before unload

RespoApp::render_loop

fn[Model : Eq, ActionOp] RespoApp::render_loop(self : RespoApp[Model], renderer : () ->
RespoNode
[ActionOp] raise
RespoCommonError
, dispatch_action : (ActionOp) -> Unit raise
RespoCommonError
) -> Unit

RespoStatesTree

pub(all) struct RespoStatesTree {
backup : Json?
cursor :
T
[String]
branches :
HashMap
[String, RespoStatesTree]
// private fields
}

Respo maintains states in a tree structure, where the keys are strings, each child component "picks" a key to attach its own state to the tree, and it dispatches events to global store to update the state.

This is an immutable data structure - all updates return new trees.

RespoStatesTree::cast_branch

Cast the data in the branch to the specified type. If data is not present but backup exists, restore from backup.

RespoStatesTree::local_pair

local state in component could be None according to the tree structure Returns (state, cursor)
let (state, cursor) = states.local_pair();

RespoStatesTree::path

RespoStatesTree::pick

fn RespoStatesTree::pick(self : RespoStatesTree, name : String) -> RespoStatesTree

Pick a branch from the tree by name, returns a new tree representing that branch.

RespoStatesTree::set_in

Immutably update the tree at the specified cursor path. Returns a new tree with the update applied.

RespoUpdateState

pub(all) struct RespoUpdateState {
cursor : Array[String]
data : ObscureState?
backup : Json?
}

framework defined action for updating states branch

StateRef

pub struct StateRef[T] {
cell : Ref[T]
}

StateRef wraps a mutable Ref while serializing to an empty JSON object. Use it for ephemeral fields that should not affect persisted snapshots.
impl Default for StateRef[T]
impl Eq for StateRef[T]
impl Hash for StateRef[T]
impl Show for StateRef[T]
impl ToJson for StateRef[T]

StateRef::borrow

fn[T] StateRef::borrow(self : StateRef[T]) -> Ref[T]

Returns the underlying Ref so callers can mutate the payload directly.

StateRef::get

fn[T] StateRef::get(self : StateRef[T]) -> T

Get the current value (read-only)

StateRef::new

fn[T] StateRef::new(value : T) -> StateRef[T]

StateRef::set

fn[T] StateRef::set(self : StateRef[T], value : T) -> Unit

Set a new value

StateRef::update

fn[T] StateRef::update(self : StateRef[T], f : (T) -> T) -> Unit

Update the value using a function

code_fonts

let code_fonts : String

debug_event

fn debug_event(event_type : String, details : String) -> Unit

Log an event (only when debug mode is enabled)

debug_log

fn debug_log(message : String) -> Unit

Log a debug message (only when debug mode is enabled)

debug_render

fn debug_render(message : String) -> Unit

Log a render cycle (only when debug mode is enabled)

debug_state_change

fn[T : Show] debug_state_change(label : String, old_state : T, new_state : T) -> Unit

Log a state change (only when debug mode is enabled)

debug_store_update

fn[T : Show] debug_store_update(action : T) -> Unit

Log store update (only when debug mode is enabled)

default_fonts

let default_fonts : String

fancy_fonts

let fancy_fonts : String

is_debug_mode

fn is_debug_mode() -> Bool

Check if debug mode is enabled

mark_need_rerender

fn mark_need_rerender() -> Unit

memo_once1

fn[K : Eq, V] memo_once1(f : (K) -> V) -> ((K) -> V)

Memoize a function with a single cache slot, taking one argument.

memo_once2

fn[K1 : Eq, K2 : Eq, V] memo_once2(f : (K1, K2) -> V) -> ((K1, K2) -> V)

Memoize a function with a single cache slot, taking two arguments.

memo_once3

fn[K1 : Eq, K2 : Eq, K3 : Eq, V] memo_once3(f : (K1, K2, K3) -> V) -> ((K1, K2, K3) -> V)

Memoize a function with a single cache slot, taking three arguments.

memo_once4

fn[K1 : Eq, K2 : Eq, K3 : Eq, K4 : Eq, V] memo_once4(f : (K1, K2, K3, K4) -> V) -> ((K1, K2, K3, K4) -> V)

Memoize a function with a single cache slot, taking four arguments.

memo_once5

fn[K1 : Eq, K2 : Eq, K3 : Eq, K4 : Eq, K5 : Eq, V] memo_once5(f : (K1, K2, K3, K4, K5) -> V) -> ((K1, K2, K3, K4, K5) -> V)

Memoize a function with a single cache slot, taking five arguments.

memoize1

fn[K : Hash + Eq, V] memoize1(f : (K) -> V) -> ((K) -> V)

memoize2

fn[K1 : Hash + Eq, K2 : Hash + Eq, V] memoize2(f : (K1, K2) -> V) -> ((K1, K2) -> V)

memoize3

fn[K1 : Hash + Eq, K2 : Hash + Eq, K3 : Hash + Eq, V] memoize3(f : (K1, K2, K3) -> V) -> ((K1, K2, K3) -> V)

memoize4

fn[K1 : Hash + Eq, K2 : Hash + Eq, K3 : Hash + Eq, K4 : Hash + Eq, V] memoize4(f : (K1, K2, K3, K4) -> V) -> ((K1, K2, K3, K4) -> V)

memoize5

fn[K1 : Hash + Eq, K2 : Hash + Eq, K3 : Hash + Eq, K4 : Hash + Eq, K5 : Hash + Eq, V] memoize5(f : (K1, K2, K3, K4, K5) -> V) -> ((K1, K2, K3, K4, K5) -> V)

normal_fonts

let normal_fonts : String

preset

let preset : String

common CSS resets for Respo pages

render_node

fn[T, U : Eq] render_node(mount_target :
Node
, store : Ref[U], renderer : () ->
RespoNode
[T] raise
RespoCommonError
, dispatch_action : (T) -> Unit raise
RespoCommonError
, _interval : Float?) -> Unit raise
RespoCommonError

render elements

set_debug_mode

fn set_debug_mode(enabled : Bool) -> Unit

Enable or disable debug mode for Respo framework When enabled, logs state changes, events, and render cycles

show_obscure_state

fn show_obscure_state(msg : String, v : ObscureState) -> Unit

try_load_storage

fn[Model :
FromJson
+ Default] try_load_storage(key : String) -> Model

ui_button

let ui_button : String

ui_button_danger

let ui_button_danger : String

ui_button_primary

let ui_button_primary : String

ui_center

let ui_center : String

layout items in column and center them with flexbox demos https://ui.respo-mvc.org/#/layouts.html

ui_column

let ui_column : String

layout items in column and center them with flexbox demos https://ui.respo-mvc.org/#/layouts.html

ui_column_dispersive

let ui_column_dispersive : String

layout items in column with flexbox, space around demos https://ui.respo-mvc.org/#/layouts.html

ui_column_evenly

let ui_column_evenly : String

layout items in column with flexbox, space evenly demos https://ui.respo-mvc.org/#/layouts.html

ui_column_parted

let ui_column_parted : String

layout items in column with flexbox, space between demos https://ui.respo-mvc.org/#/layouts.html

ui_expand

let ui_expand : String

expand item with flex:1

ui_font_code

let ui_font_code : String

monospace font for code, Source Code Pro, Menlo, Ubuntu Mono, Consolas

ui_font_fancy

let ui_font_fancy : String

fancy font for title, Josefin Sans, Helvetica neue, Arial refers to https://fonts.google.com/specimen/Josefin+Sans or https://cdn.tiye.me/favored-fonts/main-fonts.css

ui_font_normal

let ui_font_normal : String

normal font for text, Hind, Helvatica, Arial refers to https://fonts.google.com/specimen/Hind or https://cdn.tiye.me/favored-fonts/main-fonts.css

ui_fullscreen

let ui_fullscreen : String

full page with absolute position

ui_global

let ui_global : String

ui_input

let ui_input : String

let ui_link : String

ui_row

let ui_row : String

layout items in row with flexbox, items are stretched demos https://ui.respo-mvc.org/#/layouts.html

ui_row_center

let ui_row_center : String

layout items in row with flexbox, center them demos https://ui.respo-mvc.org/#/layouts.html

ui_row_dispersive

let ui_row_dispersive : String

layout items in row with flexbox, space around demos https://ui.respo-mvc.org/#/layouts.html

ui_row_evenly

let ui_row_evenly : String

layout items in row with flexbox, space evenly demos https://ui.respo-mvc.org/#/layouts.html

ui_row_middle

let ui_row_middle : String

layout items in row with flexbox, space between demos https://ui.respo-mvc.org/#/layouts.html

ui_row_parted

let ui_row_parted : String

layout items in row with flexbox, space between demos https://ui.respo-mvc.org/#/layouts.html

ui_textarea

let ui_textarea : String

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io