Documentation

fission-core

The runtime, widget system, and action/reducer architecture for the Fission UI framework.

fission-core is the central crate of the Fission toolkit. It provides the declarative widget tree, the unidirectional data-flow architecture (actions, reducers, effects), and the runtime that ties everything together. Applications describe their UI as a Node tree built from composable widgets, dispatch Action values to modify AppState, and let the runtime lower the tree into the intermediate representation consumed by platform renderers.

Architecture overview

  Widget::build()          Runtime::dispatch()
       |                        |
  View<S> + BuildCtx<S>     ActionEnvelope
       |                        |
    Node tree                Reducer(s)
       |                        |
  LoweringContext            mutate AppState
       |                    + emit Effects
    CoreIR (fission-ir)          |
       |                   EffectEnvelope
  LayoutEngine                  |
       |                 Platform executor
    Renderer
  1. Widget layer -- Widget::build() receives read-only state through View<S> and a mutable BuildCtx<S> for binding actions. It returns a Node tree.
  2. Lowering -- Each Node variant implements the Lower trait, converting itself into fission-ir operations (paint ops, layout ops, semantics).
  3. Layout -- fission-layout resolves flex, grid, scroll, and absolute positioning.
  4. Rendering -- Platform backends (Metal, Skia, HTML Canvas) consume the laid-out IR.
  5. Actions -- User interactions produce ActionEnvelope values that the Runtime dispatches to registered reducers.
  6. Reducers -- Pure functions that mutate AppState and optionally emit side-effects through Effects.
  7. Effects -- Asynchronous operations (HTTP, file I/O, alerts) that the platform executor runs outside the deterministic core.

Key concepts

AppState

Any Send + Sync + Debug + 'static type that implements AppState. The runtime stores one instance per concrete type.

Action / ActionEnvelope

Action is a trait for strongly-typed, serialisable event payloads. Actions are transported as ActionEnvelope (type-erased ID + JSON bytes) so the reducer pipeline stays generic.

BuildCtx

The mutable context passed to Widget::build(). Use it to:

  • Bind actions to handlers: ctx.bind(MyAction { .. }, my_handler as fn(&mut S, MyAction))
  • Register portals (overlays, modals, toasts)
  • Request animations

View

Read-only access to AppState, the current theme, i18n registry, layout snapshot, and animation values.

Node

A serialisable enum with one variant per built-in widget (Button, Text, Row, Column, etc.) plus a Custom escape hatch.

Effects

Reducers that need async work (network, disk, timers) push EffectEnvelope values through Effects. The platform executor fulfils them and dispatches the on_ok / on_err callbacks back into the action pipeline.

Quick example

use fission_core::*;
use fission_core::ui::*;
use serde::{Deserialize, Serialize};

// 1. Define state
#[derive(Debug, Default)]
struct Counter { count: i32 }
impl AppState for Counter {}

// 2. Define an action
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Increment;
impl Action for Increment {
    fn static_id() -> ActionId {
        ActionId::from_name("app::Increment")
    }
}

// 3. Build the widget tree
struct CounterWidget;
impl Widget<Counter> for CounterWidget {
    fn build(&self, ctx: &mut BuildCtx<Counter>, view: &View<Counter>) -> Node {
        let on_press = ctx.bind(Increment, handle_increment as fn(&mut Counter, Increment));
        Column {
            children: vec![
                Text::new(format!("Count: {}", view.state.count)).into_node().into(),
                Button {
                    child: Some(Box::new(Text::new("Add one").into_node())),
                    on_press: Some(on_press),
                    ..Default::default()
                }.into_node().into(),
            ],
            ..Default::default()
        }.into_node()
    }
}

// 4. Reducer
fn handle_increment(state: &mut Counter, _action: Increment) {
    state.count += 1;
}

Crate feature flags

None at this time -- all functionality is included by default.

License

MIT