Sign in

    respo

    A tiny virtual DOM library

    virtual-dom
    Download zip
    Author
    Version
    0.3.2
    License
    Apache-2.0
    Last updated
    5 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, G] RespoApp::render_loop(self : RespoApp[Model], renderer : () ->
    RespoNode
    [ActionOp, G] 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

    collect_global_handlers

    Collect all global event handlers from the virtual DOM tree Returns an array of handler functions with their coordinates

    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

    dispatch_global_event

    fn[T, G] dispatch_global_event(event : G, tree :
    RespoNode
    [T, G], dispatch :
    DispatchFn
    [T]) -> Unit raise
    RespoCommonError

    Dispatch a global event to all registered handlers in the tree

    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, G] render_node(mount_target :
    Node
    , store :
    Ref
    [U], renderer : () ->
    RespoNode
    [T, G] 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