Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
164 commits
Select commit Hold shift + click to select a range
d62bb7e
fix(native): avoid full-tree lists during input sync
tiensonqin Oct 15, 2026
0247464
fix: bind typed properties without intermediate signals
tiensonqin Oct 15, 2026
4b8cc1d
fix: release retired paint fragments
tiensonqin Oct 15, 2026
d44dc83
perf: avoid per-glyph damage allocations
tiensonqin Oct 15, 2026
1ec1132
perf: avoid allocating retained fragment lookup wrappers
tiensonqin Oct 15, 2026
640d7db
perf: deduplicate accessibility ancestor traversal
tiensonqin Oct 15, 2026
d70977c
perf: share raster frame snapshots
tiensonqin Oct 15, 2026
0e3d951
fix: invalidate measurements when resolved edges change
tiensonqin Oct 16, 2026
d4736ca
fix: preserve measurements across unrelated owner changes
tiensonqin Oct 16, 2026
6aa0fed
fix: reuse unchanged raster prefix bounds
tiensonqin Oct 16, 2026
c5ce3f1
fix: avoid unused opaque glyph color allocation
tiensonqin Oct 16, 2026
973ebd8
fix: traverse restyled child positions once
tiensonqin Oct 16, 2026
5f11c3a
feat: handle native split divider dragging
tiensonqin Oct 16, 2026
ff82ad6
feat: expose native split resize state
tiensonqin Oct 16, 2026
a05db50
fix: flip native popups at viewport edges
tiensonqin Oct 16, 2026
6969adb
fix: preserve inset content in native scroll ranges
tiensonqin Oct 16, 2026
47675b7
fix: anchor nested popups after ancestor placement
tiensonqin Oct 16, 2026
ceb2fad
fix: place parent popups before nested surfaces
tiensonqin Oct 16, 2026
e30fed0
fix: read live native scroll state in retained callbacks
tiensonqin Oct 16, 2026
82e6b74
fix: preserve native surface motion and split geometry
tiensonqin Oct 16, 2026
c1f56ae
fix: keep unfocused windows open on Escape
tiensonqin Oct 16, 2026
8af586f
fix: honor native border geometry and dialog chrome
tiensonqin Oct 16, 2026
f2938ee
fix: paint native select indicators
tiensonqin Oct 16, 2026
1ef9c6b
fix: honor explicit line heights in text layouts
tiensonqin Oct 16, 2026
d2f3f17
fix: preserve image aspect ratio in native fill mode
tiensonqin Oct 16, 2026
ba07de0
fix: bound and scroll native menu surfaces
tiensonqin Oct 16, 2026
00d2d87
fix: own native menu keyboard navigation
tiensonqin Oct 16, 2026
7e3f3fb
fix: preserve native menu text ownership
tiensonqin Oct 16, 2026
ffb37fa
fix: paint native control icons once
tiensonqin Oct 16, 2026
589a3b2
fix: align native content geometry
tiensonqin Oct 16, 2026
c470632
fix: match list item content layout
tiensonqin Oct 16, 2026
33b3c88
fix: round percentage colors consistently
tiensonqin Oct 16, 2026
c0f23b2
fix: deliver web appearance lifecycle events
tiensonqin Oct 16, 2026
57fb35b
fix: size buttons from custom content
tiensonqin Oct 16, 2026
89180d3
perf: reuse repeated translucent pixel blends
tiensonqin Oct 16, 2026
2ca427d
fix: render menu selection indicators across hosts
tiensonqin Oct 16, 2026
0cc7e8a
fix: honor typed menu slot layout in renderers
tiensonqin Oct 16, 2026
a9cd18e
fix: exclude explicit padding from native text measurement
tiensonqin Oct 16, 2026
38c19ef
fix: initialize native menu selection on open
tiensonqin Oct 16, 2026
4047e27
fix: align native select menus with their selected row
tiensonqin Oct 16, 2026
6ae9ce0
fix: honor native surface transition timing
tiensonqin Oct 16, 2026
5c0123f
fix: wait for complete web surface exits
tiensonqin Oct 16, 2026
e8fac1c
fix: align popup motion defaults across renderers
tiensonqin Oct 16, 2026
0d0556e
fix: preserve interrupted web popup exits
tiensonqin Oct 17, 2026
a4fe509
fix: separate SVG path subpaths at moveto
tiensonqin Oct 17, 2026
38675ca
fix: reserve native picker indicator width
tiensonqin Oct 17, 2026
d10b6f7
fix: cancel eased split geometry during direct resize
tiensonqin Oct 17, 2026
75e89c1
feat: suspend interaction in retained containers
tiensonqin Oct 17, 2026
ca6d245
fix: preserve moved paint fragments during disposal
tiensonqin Oct 17, 2026
1eb675a
fix: bound scroll ranges at nested clips
tiensonqin Oct 17, 2026
0954b24
fix: exclude hidden subtrees from scroll extents
tiensonqin Oct 17, 2026
8e01e57
fix: size choice controls and bound dialog widths
tiensonqin Oct 17, 2026
ca7cfd1
fix: preserve explicit dialog widths within the viewport
tiensonqin Oct 17, 2026
d34ee17
fix: restrict pointer activation to foreground ancestry
tiensonqin Oct 17, 2026
9aec537
fix: constrain anchored popovers to available space
tiensonqin Oct 17, 2026
e6616c8
fix: chain wheel input past scroll boundaries
tiensonqin Oct 17, 2026
30898b1
fix: clamp scroll offsets before rebuilding hit regions
tiensonqin Oct 17, 2026
ba1a48c
fix: preserve popup surfaces and explicit appearance
tiensonqin Oct 17, 2026
4845b54
feat: navigate results from text inputs
tiensonqin Oct 17, 2026
022d17a
fix: scroll constrained Web lists
tiensonqin Oct 17, 2026
9f4f225
fix: preserve picker keyboard ownership and spacing
tiensonqin Oct 17, 2026
8bb09fe
fix: unify spacing and anchored button behavior
tiensonqin Oct 17, 2026
ce5df04
fix: retain split widths and transitions
tiensonqin Oct 17, 2026
fb827c9
fix: share native foreground resolution
tiensonqin Oct 17, 2026
0b17c29
fix: compose typed appearance with control states
tiensonqin Oct 17, 2026
795a291
fix: support icon opacity
tiensonqin Oct 17, 2026
a335c33
fix: bind reactive button selection
tiensonqin Oct 17, 2026
69b63db
fix(web): focus ordinary actions in popup menus
tiensonqin Oct 17, 2026
9aa493e
fix(native): focus ordinary actions in popup menus
tiensonqin Oct 17, 2026
5e3019e
fix: let shared views own dialog geometry
tiensonqin Oct 17, 2026
b60b717
feat: support responsive grid cells and row flow
tiensonqin Oct 17, 2026
35b176d
fix: honor grid alignment properties
tiensonqin Oct 17, 2026
b61a546
fix: build inserted layout subtrees once
tiensonqin Oct 17, 2026
282a241
fix: align native choice control geometry and colors
tiensonqin Oct 17, 2026
10c0e12
fix: admit typed choice label gaps
tiensonqin Oct 17, 2026
77411ee
fix: honor text opacity and pointer focus
tiensonqin Oct 17, 2026
5c66314
perf: avoid translucent text color allocations
tiensonqin Oct 17, 2026
adfc740
feat: add independent choice caption opacity
tiensonqin Oct 18, 2026
9b0617a
perf: avoid temporary geometry allocations
tiensonqin Oct 18, 2026
3e0565c
fix: preserve logical foregrounds across web portals
tiensonqin Oct 18, 2026
121a110
fix: share logical portal font inheritance
tiensonqin Oct 18, 2026
c4a891c
fix: retain split pane tracks outside normal flow
tiensonqin Oct 18, 2026
3670332
fix: preserve SVG curve traversal and relative origins
tiensonqin Oct 18, 2026
c078282
fix: update popup placement from final batch geometry
tiensonqin Oct 18, 2026
407a787
fix: retain button content slot ownership
tiensonqin Oct 18, 2026
009a408
fix: render Core Text truncation tokens
tiensonqin Oct 18, 2026
0fda600
fix: keep select captions on one line
tiensonqin Oct 18, 2026
e206cb9
fix: align native form control paint with web
tiensonqin Oct 18, 2026
b5a54b5
fix: match native text field insets to web
tiensonqin Oct 18, 2026
dd8bd70
fix: restore focus after dropdown dismissal
tiensonqin Oct 18, 2026
1f212a2
fix: respect native focus visibility during paint
tiensonqin Oct 18, 2026
d1dfb3f
fix: distinguish pointer and keyboard menu opening
tiensonqin Oct 18, 2026
09e2adb
fix: align native menu focus with opening input
tiensonqin Oct 18, 2026
95d6b33
fix: include borders in native text placement
tiensonqin Oct 18, 2026
7c97701
fix: paint focused empty text controls
tiensonqin Oct 18, 2026
9c23d94
feat: anchor popovers to retained controls
tiensonqin Oct 18, 2026
c1be41a
fix: dismiss popovers with unavailable anchors
tiensonqin Oct 18, 2026
bba716a
fix: preserve nested wheel scroll distance
tiensonqin Oct 18, 2026
889417d
fix: contain wheel scrolling within overlays
tiensonqin Oct 18, 2026
b85a94a
fix: align row popover menu focus
tiensonqin Oct 18, 2026
5aab691
fix: validate named custom menu content
tiensonqin Oct 18, 2026
658f933
feat: add typed button navigation events
tiensonqin Oct 18, 2026
35fba03
fix: validate button navigation callback registration
tiensonqin Oct 18, 2026
f007aad
feat: support time inputs and blur submission
tiensonqin Oct 18, 2026
4b96431
fix: expose RTLD_DEFAULT on older glibc in lui_gl stubs (#241)
tiensonqin Oct 18, 2026
c907ec9
fix: honor intrinsic button sizing on web
tiensonqin Oct 18, 2026
37bba71
fix: restore focus through nested native popovers
tiensonqin Oct 18, 2026
faa935c
fix: support dialog roles on popovers
tiensonqin Oct 18, 2026
eb4317e
fix: restore form popover trigger focus
tiensonqin Oct 18, 2026
b4fa97a
fix: parse numeric HSL channels in native themes
tiensonqin Oct 18, 2026
1c5ce60
fix: preserve action list item semantics
tiensonqin Oct 18, 2026
017f917
fix: keep form popovers open across focus changes
tiensonqin Oct 18, 2026
9773da7
feat: add popover alignment offsets
tiensonqin Oct 18, 2026
5edc3a4
fix: refine nested grids after their ancestors
tiensonqin Oct 18, 2026
f176c87
fix: preserve shared-cell paint and hit order
tiensonqin Oct 18, 2026
45f7bba
fix: release modal input ownership at close
tiensonqin Oct 18, 2026
93b199b
fix: retain rectangle anchors for replacement popovers
tiensonqin Oct 19, 2026
1aa4f98
fix(web): anchor positioned modal children to the card
tiensonqin Oct 19, 2026
7ef200f
feat: align headings and paragraphs across renderers
tiensonqin Oct 19, 2026
5961355
fix(split): separate resize handles from pane spacing
tiensonqin Oct 19, 2026
e4e517f
fix: share native table column sizing
tiensonqin Oct 19, 2026
b0e2cbb
fix: admit preserved wrapping text
tiensonqin Oct 19, 2026
ac69aea
fix: preserve inline link flow
tiensonqin Oct 19, 2026
be9b67e
fix: preserve read-only table hover colors
tiensonqin Oct 19, 2026
afcde5f
feat: support application actions on links
tiensonqin Oct 19, 2026
253738d
fix: retain rich children in text containers
tiensonqin Oct 19, 2026
be49e1b
fix: constrain inset popovers and their contents
tiensonqin Oct 19, 2026
c875e08
fix: preserve intrinsic image sizing
tiensonqin Oct 19, 2026
dbb3270
feat: support explicit natural height
tiensonqin Oct 19, 2026
4ddf08d
fix: reject completions from retired resource entries
tiensonqin Oct 19, 2026
86d0a7d
fix: measure native images from decoded resources
tiensonqin Oct 19, 2026
0b79bda
fix: allocate image identities atomically
tiensonqin Oct 19, 2026
5427b7e
fix: release resource observers with their scopes
tiensonqin Oct 19, 2026
464e071
fix: respect keys on dynamic branch roots
tiensonqin Oct 19, 2026
21900ab
fix: preserve positioned overlay geometry
tiensonqin Oct 19, 2026
8fda2f7
feat: support semantic lists in layout elements
tiensonqin Oct 19, 2026
a867244
fix: preserve native keyboard focus ownership
tiensonqin Oct 19, 2026
9e7e00c
fix: retain focus through anchored popover handoffs
tiensonqin Oct 19, 2026
163a241
feat: share focus-within appearance
tiensonqin Oct 19, 2026
4826353
fix: paint native hover border colors
tiensonqin Oct 19, 2026
30858e5
fix: retain focus across popover handoff
tiensonqin Oct 19, 2026
6779b5b
fix: style checkbox indicator borders independently
tiensonqin Oct 20, 2026
d6060da
fix: honor fitting on image elements
tiensonqin Oct 20, 2026
4062c42
fix: honor inherited whitespace in native inline layout
tiensonqin Oct 20, 2026
be32c89
fix: preserve native inline fragment widths
tiensonqin Oct 20, 2026
fc070f3
fix: preserve native ancestor hover boundaries
tiensonqin Oct 20, 2026
272cc70
fix: support text visibility properties
tiensonqin Oct 20, 2026
a05c570
feat: explore EDN views with OCaml model and events
tiensonqin Oct 20, 2026
3908187
fix: restore Apple and Web CI
tiensonqin Oct 20, 2026
029c88a
feat: validate multi-file EDN against typed views
tiensonqin Oct 20, 2026
55c822b
fix: preserve text-only avatar admission
tiensonqin Oct 20, 2026
96b2f4a
feat: compose native text paragraphs
tiensonqin Oct 20, 2026
1de6bd0
feat: shape measured inline text objects
tiensonqin Oct 20, 2026
b25c90e
feat: configure popup collision padding
tiensonqin Oct 20, 2026
8e83870
fix: preserve popup scroll during placement
tiensonqin Oct 20, 2026
3be5537
fix: honor typed switch chrome across renderers
tiensonqin Oct 20, 2026
ae43dd6
fix: purge discarded native layout children
tiensonqin Oct 20, 2026
fbb097e
fix: retain nonwrapping row label widths
tiensonqin Oct 20, 2026
11bfc3f
feat: add clamped inline text flow
tiensonqin Oct 20, 2026
f9b859f
fix: support splits without resize handles
tiensonqin Oct 21, 2026
1f61a35
fix: preserve automatic sizing and pane overflow
tiensonqin Oct 21, 2026
36aeff6
fix: keep split panes within their available height
tiensonqin Oct 21, 2026
59de33a
Merge remote-tracking branch 'origin/main' into feat/ml-backend
tiensonqin Oct 21, 2026
8ac6361
fix: stabilize native tests and use 20ms performance budgets
tiensonqin Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
- name: Schema tests
run: node --test tooling/test/*.mjs
- name: OCaml tests
run: opam exec -- dune build --action-stderr-on-success=must-be-empty @runtest
run: make test-ocaml
- name: Runtime performance qualification
run: bash tooling/performance/qualify_runtime.sh
- name: Trim dune cache
Expand Down Expand Up @@ -389,14 +389,15 @@ jobs:
- name: Test platform/native
# Tests skip what the runner cannot provide: no display -> GL/parity
# skip, no dbus -> ax/shell skip, no lavapipe -> vulkan skip.
# Run frame-budget tests without competing test processes or compilers.
# Windows: bound the whole suite while a hung test is diagnosed —
# verbose lists each test command as it starts, so the last one
# printed identifies the hang.
run: |
if [ "$RUNNER_OS" = "Windows" ]; then
timeout 600 opam exec -- dune runtest platform/native/ -f --verbose
timeout 600 opam exec -- dune runtest platform/native/ -j 1 -f --verbose
else
opam exec -- dune runtest platform/native/ -f
opam exec -- dune runtest platform/native/ -j 1 -f
fi
shell: bash
- name: Trim dune cache
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ generate-json-view:
python3 tools/gen_json_view.py

test-ocaml:
opam exec -- dune build --action-stderr-on-success=must-be-empty @runtest
# Run frame-budget tests without competing Dune jobs.
opam exec -- dune build --action-stderr-on-success=must-be-empty @runtest -j 1

test-apple:
swift test
Expand Down
182 changes: 182 additions & 0 deletions examples/data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# EDN views with OCaml-owned state and events

This is an experiment, not a public LUI API. EDN declares view structure, bindings,
components, conditional/platform branches and action names. OCaml supplies the typed
model, derived fields, action signatures, event decoder, reducer and asynchronous
work. EDN does not evaluate functions or run business logic.

## Run

Use the project's OCaml 5.5.0 switch and its `melange-edn-core` pin. The script
builds the existing `lui.cmxa` target and compiles this example into
`_build/data-view`; it does not edit dune files or mount a graphical window.

```sh
OPAMSWITCH=5.5.0 sh examples/data/run.sh
OPAMSWITCH=5.5.0 sh examples/data/run.sh --check
OPAMSWITCH=5.5.0 sh examples/data/run.sh examples/data/scenarios.edn --mount-only
OPAMSWITCH=5.5.0 sh examples/data/run.sh examples/data/extensions.edn --profile web
OPAMSWITCH=5.5.0 sh examples/data/run.sh examples/data/extensions.edn --profile ios --mount-only
OPAMSWITCH=5.5.0 sh examples/data/run.sh examples/data/extensions.edn --profile android --mount-only
OPAMSWITCH=5.5.0 sh examples/data/run.sh examples/data/extensions.edn --profile web --benchmark 250 --mount-only
```

The default todo example scripts input, addition, toggling and reordering over a
headless patch backend. `--mount-only` starts with a sample item and skips scripted
interactions. `--profile` selects a declared host profile; it does **not** run that
host's UI. `--benchmark N` reports in-memory root parse, filesystem module load
and parse, static validation/compilation, cached graph recompilation and OCaml
model-update/retained-render timings separately. These are development-machine
measurements, not browser, iOS, Android or large-list benchmarks.

## Module format

`todos.edn` imports `frame.edn` and `todo-row.edn`; the frame imports `labels.edn`.
Imports are explicit and aliases are local to each source document:

```edn
{:version 1
:imports {:layout "frame.edn"}
:components
{:workspace
{:props {:caption :string}
:slots [:body]
:view [:column
[:text {:text {:bind [:props :caption]}}]
[:slot :body]]}}
:view
[:use :workspace {:caption "Tasks"}
{:body [:if {:bind [:model :empty?]}
[:text {:text "Empty"}]
[:text {:text "Tasks available"}]]}]}
```

The real example uses `[:use :layout/panel ...]`. A component's `:props` are a
shape declaration; its `:slots` enumerate required slots. Slots evaluate in the
caller's scope, so a slot can bind to the caller's model or keyed row. Shape
expressions include `:string`, `:keyword`, `:bool`, `:int`, `:float`, vectors,
records, tuples, nullable values and finite enumerations. Module-local names can
coexist; ambiguous references must be namespace-qualified. `:view` is optional
in imported component-only documents, mandatory at the entry point. A standalone
view vector remains valid for small single-file experiments.

The compiler takes a host-supplied `Sources.loader` callback returning canonical
source identity and content. The native example resolves imports relative to the
importer and normalizes their paths. Web clients can supply an already-fetched
source map, without making core compilation depend on filesystems or async I/O.
Only transitively imported documents are loaded. A `Sources.cache` reuses parsed
units with the same canonical identity **and content**; changing content invalidates
the unit. The compiled plan is reusable across model changes; no EDN is parsed
or recompiled by a model signal update.

## View vocabulary

- Standard `[:kind {:property value ...} children ...]` nodes use generated
OCaml constructor contracts plus canonical protocol admission. A wire property
is not automatically a valid typed argument. `:root` and internal section
wrapper nodes are runtime-only.
- `{:bind [:model :field]}` and `{:bind [:props :field]}` read typed contract
paths. `[:for {:items {:bind [:model :items]} :as :item :key [:id]} child]`
introduces a keyed row alias; nested lists can use different aliases.
- `[:when condition child]`, `[:if condition then else]` and
`[:platform {:web view :ios view :macos view :android view :default view}]`
select retained views. Other supported profile keys include `:linux`,
`:windows` and `:generic`. All listed branches are compiled.
- `:on {:press :add}` or `:on {:input [:rename {:bind [:item :id]}]}` refers
only to declared OCaml actions; arguments read the current scope at event
time. The original typed LUI event (input text, checkbox state, pointer
coordinates, extension fields) is decoded by OCaml, not by EDN.
- `[:ext/split-view ...]`, `[:ext/split-tab ...]` and
`[:ext/gallery-accent ...]` in `extensions.edn` use the real gallery schema.
Its split event updates an OCaml-owned ratio; only `:split-tab` admits
standard children. `:platform` supplies a standard fallback for generic,
Linux and Windows profiles.

## Contracts and validation boundary

### Preserve OCaml view types

`tooling/typed_view_schema.mjs` derives constructor contracts from
`src/lui_elements.mli` and canonical value encoders in `lui_elements.ml`, rather
than treating the broader wire schema as the typed API. Generation fails on an
unknown signature or value domain. The compiler preserves constructor property
surfaces, required labeled arguments, closed variants, abstract child categories,
fixed child arity and named element slots. The typed `align` modifier is also
available on standard nodes returning `t`. Components, slots, conditions, keyed
collections and platform branches do not erase their children's result types.
Branches must agree on that result type even in unused component definitions.

Values do not silently coerce between OCaml types:

```edn
[:row {:grow 1.0}] ; float, not integer 1
[:spinner {:size :sm}] ; closed variant, not string "sm"
[:icon {:name [:app "company-logo"]}] ; explicit open icon constructor
[:file-picker {:request [:int 1]}] ; explicitly tagged picker token
[:column {:data-attrs [["data-id" "item"]]}] ; string-pair list, not wire text
[:popover {:anchor :below :anchor-rect [0.0 0.0 12.0 12.0]}]
[:input-group
{:slots {:actions [:input-group-actions [:button {:text "Send"}]]}}
[:textarea]]
```

Bindings must have the same domains: a general `:string` or `:keyword` binding
cannot satisfy a finite variant; declare its exact keyword enumeration in the
OCaml model contract. Optional arguments may be nullable, required ones may not.
`split` and `drawer` need exactly two unconditional children, `input-group` one.
Use `:slots` for `input-group`'s `:actions`, `list-section`'s `:header`/`:footer`
and `list-item`'s `:swipe-actions`; structural wrappers cannot be supplied as raw
wire nodes to bypass the typed API.

The stronger `Lui_split.mli` contracts are generated separately: for example,
`split-branch.orientation` accepts only `:horizontal` or `:vertical`, although
its host wire schema carries strings. Other generated extension APIs export
`view_contracts` from the same declarations as their typed constructors. Pass
these to `Data_contract.create ~extension_contracts`; handwritten extensions need
an authoritative typed contract too. A registry entry without a typed contract
is rejected, and caller-supplied contracts cannot weaken built-in Split types.
An unsupported OCaml API is rejected, not inferred from permissive wire scalars.

### Static and runtime checks

`Data_contract.create` freezes a model shape, action signatures, the existing
`Lui_extension.extension_registry`, typed extension contracts and per-profile
host fingerprint manifests.
The native example reads literal split fingerprints from actual Web, Apple and
Android host sources. The Web gallery accent adapter uses the shared OCaml
registry rather than a separate host literal; its registered tweak fingerprint
is supplied from that registry. Hosts must provide truthful registration data:
static checks cannot prove that a host actually implements its adapter.

`Data_view.compile_files ~contract ~load ~entry ()` resolves imports, checks
cycles and duplicate declarations, and validates every component plus every
reachable document view, including unused definitions and inactive branches.
It checks binding shape/path, expression type, component props/slots, action
arity and typed event support, standard kinds/properties/children/scalar ranges,
structurally impossible property sets, extension profile/host/fingerprint,
required extension properties, child admission and extension event names.
Errors include source document and component/view context, not token-level
line/column locations. Host manifests are checked before mounting the selected
branch; inactive platform branches are schema-checked without requiring their
host to be installed on the current platform. The runtime still validates
rendered values against the canonical LUI protocol.

Static validation cannot decide the actual values of nullable bindings,
duplicate keys in live model lists, whether an external image/file exists,
permission grants or real native UI behavior. OCaml must validate event payloads
and business authorization. The source-size/expansion limits prevent accidents,
not malicious EDN; do not treat remote documents as trusted executable content.

| Scenario family | Declarative representation | Verification boundary |
| --- | --- | --- |
| Layout, forms, text fields, buttons, checkbox/toggle/radio, value controls, scroll | Standard kinds and bound properties/actions | Headless todo and scenario mounts; no real input/IME or focus test |
| Keyed lists, virtual lists, tables, trees, tabs, split | `:for` and standard kinds | Keyed-node identity/reorder/action regression check; scenario mounts only for the rest |
| Dialogs, menus, nested menus, sheets/overlays, loading/error | Standard kinds, `:if`, `:when`, projected OCaml state | Admission checked; native presentation and async workflows not exercised |
| Files, media, resource IDs, accessibility, navigation/pointer events | Standard schema, OCaml resources/actions | Schema admission only; resources and permissions remain host-owned |
| Custom cross-platform controls and tweaks | Extension registry and `:platform` fallback | Split example mounts in a headless profile; host UI parity not proven |

The `Signal.map`/`Lui_dynamic` implementation is a view-plan runtime bridge;
applications supply `project` and `dispatch`. There is no EDN `eval`, arbitrary
OCaml function lookup, hot replacement, transactional rollback or claim of
complete browser/mobile/native GUI parity. Measure large models and run real
host interaction suites before promoting this experiment to a production API.
Loading
Loading