Mounting

ascent-js is the browser mount/binding engine. Apps call AscentApp.mount(ui, parent) or AscentApp.mountBody(ui); that supplies the JS DomOps and DomStyleSink to the cross-platform Mount walker so you never name those pieces at the call site.

Entry points

import ascent.*
import ascent.dsl.*

AscentApp.mount(ui, parent)       // into an element
AscentApp.mountBody(E.body(...))  // replace document body

Mount walks the AST once and wires reactive boundaries. In-memory slots own the DOM nodes per boundary (no comment anchors). Event dispatch runs handlers via runtime.unsafe.runOrFork: the synchronous prefix runs inline (needed for preventDefault); the first suspension forks to the next macrotask.

E.div(A.id("app"), "mount target")
mount target

Lifecycle

Attr.OnMount fires after insertion (so getBoundingClientRect / canvas work). Attr.OnUnmount runs LIFO at unmount / swap / key-drop. Prefer scoped { … } for resource lifetimes tied to a subtree.

for ready <- sq(false)
yield E.div(
  Attr.OnMount[Any](_ => ready.set(true)),
  when(ready)(E.span("mounted")),
)
mounted

Sync handlers

Use Ev.sync.onDragStart { … } (and friends) when side effects must run on the dispatch stack before the browser continues the event.

E.div(A.draggable(true), "drag me")
drag me