///|
#cfg(target="js")
pub using @cmd {perform, attempt, effect, delay}

///|
pub using @cmd {none, batch}

///|
pub type Cmd = @cmd.Cmd

///|
pub type Html = @html.Html

///|
/// Creates an application from a root component builder.
///
/// The builder runs when the app is mounted and must return its root
/// incremental HTML value. Use `Val::map`, `Val::switch`, and `Val::assoc` to
/// express subsequent changes, then call `App::mount` to attach the app to a
/// DOM element.
#cfg(target="js")
pub fn new(builder : () -> Val[Html]) -> App {
  let sandbox = @runtime.Sandbox::new()
  ambient_graph.val = Some(Graph(sandbox))
  { sandbox, init_cmd: None, builder }
}

///|
/// Creates an application using an Elm-style model, update, and view.
///
/// This is a convenience wrapper around `create_state` and `new`. Each emitted
/// message updates the model, schedules the returned command, and refreshes the
/// optional subscriptions.
#cfg(target="js")
pub fn[Model : Eq, Msg] elmish(
  model~ : Model,
  view~ : (Emit[Msg], Model) -> Html,
  update~ : (Emit[Msg], Msg, Model) -> (Model, Cmd),
  subscriptions? : (Emit[Msg], Model) -> @sub.Sub,
) -> App {
  fn builder() {
    let (model, emit) = create_state(model, update~, subscriptions?)
    model.map(model => view(emit, model))
  }
  let sandbox = @runtime.Sandbox::new()
  ambient_graph.val = Some(Graph(sandbox))
  { sandbox, init_cmd: None, builder }
}

///|
/// Configure routing callbacks for this app.
///
/// - `url_changed` is triggered for browser history navigation and when
///   `push_url` / `replace_url` commands are used.
/// - `url_request` is triggered when captured links `@html.a()` are clicked.
///
/// If the app is already mounted, a `url_changed` command for the current URL
/// will be enqueued immediately.
#cfg(target="js")
#deprecated("use subscriptions and @sub.on_url_changed/@sub.on_url_request instead")
pub fn App::with_route(
  self : Self,
  url_changed? : Emit[@url.Url],
  url_request? : Emit[@url.UrlRequest],
) -> Unit {
  if url_changed is Some(msg) {
    for _, sub in @sub.on_url_changed(msg).to_map(@key.key, filter_global=false) {
      let (payload, loader) = sub
      let _ = loader(payload, self.sandbox)
    }
  }
  if url_request is Some(msg) {
    for _, sub in @sub.on_url_request(msg).to_map(@key.key, filter_global=false) {
      let (payload, loader) = sub
      let _ = loader(payload, self.sandbox)
    }
  }
}

///|
/// Registers a one-shot command to be queued when `mount` runs.
/// This API is still evolving and may change in future releases.
#cfg(target="js")
#internal(unstable, "Experimental API")
pub fn App::with_init(self : Self, cmd : Cmd) -> Unit {
  self.init_cmd = Some(cmd)
}

///|
/// Mount the app into the DOM element identified by `element_id`.
///
/// This initializes the runtime and schedules the first render flush.
/// If routing is configured, the current URL is also emitted on mount.
#cfg(target="js")
pub fn App::mount(self : Self, element_id : String) -> Unit {
  self.sandbox.mount = element_id
  @dom.document()
  .get_element_by_id(element_id)
  .unwrap()
  .set_inner_html("
") if self.init_cmd is Some(cmd) { self.sandbox.add(cmd) self.init_cmd = None } let vnode = (self.builder)() self.sandbox.initialize(vnode.0.map(html => html.0)) self.sandbox.flush() } ///| /// A running Rabbita application. #cfg(target="js") struct App { sandbox : @runtime.Sandbox builder : () -> Val[Html] mut init_cmd : Cmd? } ///| /// Render a component into an HTML string. /// /// This is the server-side rendering entrypoint for Rabbita. It evaluates /// the component once and serializes the resulting virtual DOM into static /// HTML. /// /// Hydration is currently not supported. If you later call `mount` on the /// client, Rabbita will do a fresh client-side render instead of attaching to /// the existing server-rendered DOM. /// /// ## Example /// /// ```mbt check /// test "render html to string" { /// inspect( /// @rabbita.render_to_string(() => { /// @rabbita.Val::constant(@html.ul(["todo1", "todo2"].map(x => @html.li(x)))) /// }), /// content=( /// #| /// ), /// ) /// } /// ``` #internal(unstable, "Experimental API") pub fn render_to_string(builder : () -> Val[Html]) -> String { @runtime.server_side_render(() => builder().0.read().0) }