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