Skip to content
elcoospPublic

About

The cross-platform framework with zero compromises. Write in a reactive .flux language, hot-reload via binary patches on-device, and compile to 100% native SwiftUI and Jetpack Compose for production. No JS bridges. No WebViews. πŸ“±πŸ¦€

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

1,407 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Flux Logo

Write once, render native.
A write-once UI language for native iOS & Android. Edit .flux source and watch it hot-reload on-device in milliseconds as binary patches over WebSocket β€” then compile the exact same code to idiomatic SwiftUI and Jetpack Compose for release. No webview. No JS bridge. No interpreter in production.

Rust License Platforms Codegen Hot Reload Crates Lines of code Stdlib ISA vectors ADRs Testing
Rust CI iOS CI Android CI


Flux

Note

Flux is under active development. The Rust workspace β€” 16 crates, ~63k lines β€” is end-to-end functional: parse β†’ typecheck β†’ lower β†’ diff β†’ patch β†’ VM, plus native codegen for both platforms. On Android, dev renders through the same declarative components the release codegen emits; the iOS dev tier is still imperative (UIKit) and convergence is gated on measurement (ADR-0048).


Table of Contents


Why Flux

Traditional cross-platform Flux
Render target WebView / canvas Real UIView / Compose View
Hot reload Full rebuild or JS eval Binary IR patch over WebSocket
Release build Interpreter shipped in prod SwiftUI / Compose codegen
State Framework-locked SolidJS-style signal graph over one shared IR

The dev loop and the release artifact come from the same Reactive Tree IR. You iterate against a fast bytecode VM host during development and ship platform-native code in release β€” no fork in the road, no "dev looks different from prod." A parity harness (flux-parity) continuously proves that dev-VM execution and release codegen stay equivalent, so the two paths are verified, not assumed.


Features

Language

  • compo components with typed props, record data types, local state signals, closures (|| { ... }, fn(v) { ... }), and ForEach with key functions.
  • SolidJS-style reactivity β€” assignments to state signals propagate through a fine-grained dependency graph; $binding two-way bindings for text inputs.
  • Navigation built in: Router / Screen / RouterNav with named routes.
  • 28-component standard library β€” Text, Button, Column, Row, Stack, Grid, ScrollView, TextField, TextInput, Toggle, Switch, Slider, Picker, DatePicker, Image, Modal, Sheet, Dialog, SafeArea, Spacer, and more β€” plus prelude, traits, and capability modules.
  • Platform capabilities for storage, network, and device services through a versioned capability contract, resolved identically on server and both hosts.

Pipeline

  • Hand-written lexer + recursive-descent parser with error recovery; every error carries a source Span.
  • Type checker (flux-types) over the parse tree.
  • Reactive Tree IR (flux-ir) β€” arena-allocated, stable u32 node IDs, a ClosureIR bytecode table, and an InstanceRegistry that lets hosts preserve component state across hot swaps.
  • Structural differ (flux-differ) β†’ minimal edit scripts, serialized as MessagePack binary patches (flux-ir-serde) with blake3 content-addressed interning.
  • Register-based bytecode VM β€” the flux-vm-ref crate is the dependency-light behavioral oracle for the ISA; the shipping Swift (FluxBytecodeVM) and Kotlin VMs are validated against 92 golden ISA vectors under tests/isa-vectors/.
  • Capability calls (CALL_CAP) return result-cell signals (Ready / Pending / Error); async capabilities settle via an injected AsyncResolver (ADR-0044/0045).
  • Native codegen β€” a data-driven shared emitter (flux-codegen-core with a Backend trait and primitive registry) drives the SwiftUI and Jetpack Compose backends, with a node-ID bridge so generated views keep the exact identity the VM used in dev.

Tooling

  • flux CLI β€” scaffold, dev server, native builds, formatter, diagnostics emitter, stdlib schema, environment doctor.
  • Hot-reload dev server (flux-devserver) β€” file watching, lower + diff on every save, WebSocket patch channel on :7331, HTTP asset server on :7332, optional LAN pairing tokens.
  • Language Server (flux-lsp, built on async-lsp) plus a VS Code extension with syntax highlighting, diagnostics, hot-reload status, and "Run on device".
  • DevTools desktop app (flux-devtools-ui) β€” a gpui-based GUI rendering live telemetry over the same wire protocol (ADR-0041).
  • Parity harness (flux-parity) β€” trace capture, reduction, and equivalence checking between dev-VM execution and release codegen output.
  • Perf harness (flux-perf-harness) β€” render-performance measurement against the project's latency budgets.

Architecture

 main.flux
     β”‚
     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ flux-parser │──▢│ flux-types  │──▢│       flux-ir        β”‚
β”‚ (lex/parse) β”‚   β”‚ (typecheck) β”‚   β”‚ (Reactive Tree IR)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β–Ό dev                                     β–Ό release
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚ flux-differ  │──▢│ flux-ir-serde β”‚      β”‚    flux-codegen-core    β”‚
        β”‚ (tree diff)  β”‚   β”‚ (MsgPack patchβ”‚      β”‚ (data-driven emitter)   β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚ WebSocket :7331           β”‚
                                   β–Ό                           β”œβ”€β–Ά flux-codegen-swift  β†’ SwiftUI
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   └─▢ flux-codegen-kotlin β†’ Compose
                        β”‚  Host app VM +   β”‚
                        β”‚  signal graph +  β”‚   ◀── validated against
                        β”‚  shadow tree     β”‚        92 golden ISA vectors
                        β”‚ (Swift / Kotlin) β”‚        (tests/isa-vectors/)
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Node identity

Node IDs are stable, content-derived FNV-1a-32 hashes β€” never sequential. Two tag families keep expression and declaration IDs disjoint, and the codegen backends thread the exact same family tags through their node-ID bridges. IDs are stable across edits where structure doesn't change, which is what lets a hot swap preserve component state.

Prop indexing

Prop indices are FNV-1a-32 of the prop name masked to u16, derived identically by the host kits (PropsIndex.propIndexForName on Android). Indices are never hardcoded β€” a hardcoded index desyncs from the server and renders silently blank UI.

The reactive core and the shadow tree

  • The shadow tree is not a tree of raw native views. Each ShadowNode holds its materialized props in a platform observable: on Android, props live in a Compose MutableState (injected via propsStateFactory, because a plain var is invisible to snapshot tracking); on iOS the tree is observed natively.
  • Keyed reconciliation matches children by nodeId β€” an existing node is reordered, never recreated β€” preserving scroll position, text field contents, and screen state across diffs and router push/pop.
  • Three contracts are normative and versioned: the wire protocol (Appendix D), the VM ISA (Appendix E), and the adapter contracts (Appendix F) in docs/spec/mlp-appendices.md.

Getting Started

Prerequisites

  • Rust β€” the toolchain is pinned to an exact nightly (nightly-2026-08-28) via rust-toolchain.toml; rustup will provision it automatically on your first build. A pinned nightly is required because the gpui-based DevTools crate uses unstable std features; the declared rust-version = "1.86" is the edition-2024 language floor.
  • cargo-nextest β€” the project's test runner.
  • Xcode (iOS 16+ deployment target) and/or the Android SDK β€” only for the host apps.

Tip

Everything on the Rust side β€” dev server, parser, differ, VM oracle, codegen, parity harness, benchmarks β€” builds and runs without Xcode or the Android SDK.

Build & test the workspace

git clone https://github.com/elcoosp/flux
cd flux

cargo build --release          # the CLI lands in target/release/flux

# Format + lint must be clean
cargo fmt -- --check
cargo clippy -- -D warnings

# Full test suite (nextest, never plain `cargo test`)
cargo nextest run
cargo test --doc               # doctests (nextest does not run these)

Run the todo example

cargo build -p flux-cli
flux dev --root ./examples/todo

Open runtimes/ios in Xcode or build the Android host with ./gradlew :runtimes:android:build, point it at ws://<host>:7331, and edit examples/todo/main.flux β€” patches land on-device in milliseconds.


Usage

CLI

Command Purpose
flux init <name> Scaffold a new Flux project at <name>/.
flux dev [--root] [--ws-host] [--ws-port] [--http-port] [--token] Start the hot-reload dev server (WS :7331, HTTP :7332 by default).
flux build ios|android [--root] Codegen the project to platforms/<platform>/Generated/, then invoke the native toolchain when present.
flux fmt [<files>...] [--check] Format .flux sources to canonical style; --check exits non-zero on drift (CI gating).
flux lsp <file> [--types] Emit parse + type-check diagnostics as JSON.
flux doc Emit a JSON schema of the stdlib API to stdout.
flux doctor [--strict] Environment health check: toolchain, stdlib parse, wire protocol version, dependency drift; --strict exits non-zero on findings.

Dev server flags

flux dev --root ./my-app                       # serve a project (defaults to cwd)
flux dev --ws-host 0.0.0.0 --token <secret>    # expose on the LAN, require pairing

Hosts authenticate with the token read from FLUX_DEV_TOKEN or [dev] token in flux.toml during the Hello handshake. Without --token, localhost keeps an open dev loop.

Project manifest (flux.toml)

[project]
name = "todo"
entry = "main.flux"

[dev]
ws_port = 7331
http_port = 7332

Language Tour

A real component from examples/todo/main.flux:

record Task { label: String, done: Bool, }

compo TaskRow(task: Task, tasks: List[Task])
    Row gap: 8.0
        Text text: task.label
        Button text: "Remove", onPress: || { tasks.remove(task) }

compo TodoApp
    state tasks: List[Task] = [
        Task(label: "Buy milk", done: false),
        Task(label: "Walk dog", done: false),
    ]
    state newTask: String = ""
    Router initialRouteName: "tasks"
        Screen route: "tasks"
            Column gap: 12.0
                Text text: "Flux To-Do"
                Row gap: 8.0
                    TextInput text: $newTask, onChangeText: fn(v) { newTask = v }
                    Button text: "Add", onPress: || {
                        tasks.append(Task(label: newTask, done: false))
                        newTask = ""
                    }
                Column gap: 8.0
                    ForEach(tasks, key: fn(t) { t.label }) { item => TaskRow(task: item, tasks: tasks) }
        Screen route: "about"
            Column gap: 12.0
                Button text: "Back", onPress: || { RouterNav.navigate("tasks") }

More examples live in examples/: counter (minimal state + patch flow) and router (multi-screen navigation).


Development

Workspace layout

flux/
β”œβ”€β”€ crates/                       # Rust workspace (16 crates)
β”‚   β”œβ”€β”€ flux-syntax/              # Spans, NodeId, FNV-1a node-id hashing, value/patch vocab
β”‚   β”œβ”€β”€ flux-parser/              # Hand-written lexer + recursive-descent parser
β”‚   β”œβ”€β”€ flux-types/               # Type checker
β”‚   β”œβ”€β”€ flux-ir/                  # Reactive Tree IR: arena, ClosureIR, InstanceRegistry
β”‚   β”œβ”€β”€ flux-ir-serde/            # Binary patch (de)serialization (MessagePack, blake3)
β”‚   β”œβ”€β”€ flux-differ/              # Structural tree differ
β”‚   β”œβ”€β”€ flux-vm-ref/              # Reference register VM (test oracle, not the shipping VM)
β”‚   β”œβ”€β”€ flux-devserver/           # Hot-reload pipeline + WebSocket + HTTP asset server
β”‚   β”œβ”€β”€ flux-codegen-core/        # Shared data-driven emitter (Backend trait, primitives)
β”‚   β”œβ”€β”€ flux-codegen-swift/       # SwiftUI codegen (node-ID bridge)
β”‚   β”œβ”€β”€ flux-codegen-kotlin/      # Jetpack Compose codegen (node-ID bridge)
β”‚   β”œβ”€β”€ flux-cli/                 # The `flux` binary
β”‚   β”œβ”€β”€ flux-parity/              # Dev VM == release codegen parity harness
β”‚   β”œβ”€β”€ flux-lsp/                 # Language server (async-lsp)
β”‚   β”œβ”€β”€ flux-devtools-ui/         # gpui DevTools desktop app
β”‚   └── flux-perf-harness/        # Render-perf benchmark harness
β”œβ”€β”€ runtimes/                     # Host apps: ios/ (Swift 6, XcodeGen) Β· android/ (Gradle)
β”œβ”€β”€ adapters/                     # Platform adapter implementations (contract version 1)
β”œβ”€β”€ stdlib/                       # 28 .flux stdlib components + prelude/traits/capabilities
β”œβ”€β”€ examples/                     # counter Β· router Β· todo
β”œβ”€β”€ editors/vscode/               # VS Code extension
β”œβ”€β”€ tests/isa-vectors/            # 92 golden ISA vectors shared by Rust + Swift + Kotlin VMs
β”œβ”€β”€ docs/spec/                    # mlp-spec.md + mlp-appendices.md (A–G)
β”œβ”€β”€ docs/adr/                     # 39 Architecture Decision Records (MADR)
└── website/                      # Astro documentation site

Conventions

  • #![forbid(unsafe_code)] in every library crate; no unwrap / expect / panic! in production code; every error carries a source Span.
  • cargo fmt and cargo clippy -- -D warnings must be clean before commit.
  • Dependencies are vetted through ADRs β€” flux doctor reports unapproved drift.
  • Release profile ships with lto = "thin" and codegen-units = 1.

Testing

Every public function has a test. The suite spans five layers, run by cargo nextest:

Layer Tool What it proves
Unit cargo nextest run Every function, edge cases, error paths
Property proptest Node-ID stability, diff minimality, round-trips
Snapshot insta Swift & Kotlin codegen output
Benchmark criterion Per-stage latency budgets
Parity tests/ Dev VM execution == release codegen

Performance budgets

Enforced by cargo bench β€” the headline metric is the Save β†’ pixels budget, the wall-clock a developer feels during hot reload, measured end-to-end by the LANE-H harness (a real DevServer plus a headless loopback WebSocket client, β‰₯ 50 edits per tree size, p50/p99):

Stage Budget
Parse a 500-line file < 5 ms
Diff a 50-node tree < 1 ms
Serialize a 50-node patch < 1 ms
VM eval per frame < 2 ms

Continuous Integration

19 GitHub Actions workflows guard main:

Workflow Purpose
rust-check.yml cargo fmt / clippy / nextest
ios-check.yml Swift build + test of the iOS host
android-check.yml Kotlin build + test of the Android host
parity-check.yml Dev VM == release codegen equivalence
codegen-compile.yml Generated Swift/Kotlin compiles
stdlib-contract.yml Stdlib component contract checks
wire-fuzz.yml Fuzzes the wire-protocol (de)serialization
compat-matrix.yml Cross-version compatibility matrix
mutation-testing.yml Mutation testing
benchmarks.yml Criterion benchmark suite
perf-harness.yml Render-perf suite
size-gate.yml Artifact size budgets
release-gate.yml Pre-release gate
artifact-publish.yml Publishes release artifacts
devtools-nightly-canary.yml Probes new nightlies against the pinned gpui
vscode-check.yml Builds/checks the VS Code extension
website-check.yml Builds/checks the docs site
adr-numbering.yml Enforces ADR numbering discipline
manifest-steward.yml Keeps the workspace manifest consistent

Documentation


Acknowledgements

Flux builds on excellent open-source foundations:


Flux β€” write once, render native.

About

The cross-platform framework with zero compromises. Write in a reactive .flux language, hot-reload via binary patches on-device, and compile to 100% native SwiftUI and Jetpack Compose for production. No JS bridges. No WebViews. πŸ“±πŸ¦€

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages