///|
#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

///|
/// Create an application from a root `Cell`.
///
/// Use `simple_cell()`, `static_cell()`, `cell()`, `cell_with_dispatch()` to create a cell.
/// Call `mount` to attach it to a DOM element.
/// Optionally call `with_route` to install routing callbacks.
/// 
/// **For beginners, `simple_cell` is recommended. See its documentation for more details.**
/// 
/// ## Example
/// 
/// ```moonbit check
/// test "minimal static page" {
///   let app = @rabbita.static_cell(div("hello world"))
///   ignore(app)
///   // use `@rabbita.new(app).mount("id")` in client
/// }
/// ```
#cfg(target="js")
pub fn new(root : Cell) -> App {
  let sandbox = @runtime.Sandbox::new(root.0)
  root.0.flags().is_root = true
  App::{ sandbox, init_cmd: None }
}

///|
/// 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? : Dispatch[@url.Url],
  url_request? : Dispatch[@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 dispatched 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 } self.sandbox.initialize() self.sandbox.flush() } ///| /// A running Rabbita application. #cfg(target="js") struct App { sandbox : @runtime.Sandbox mut init_cmd : Cmd? } ///| /// Render a `Cell` tree into an HTML string. /// /// This is the server-side rendering entrypoint for Rabbita. It evaluates /// view 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 /// #warnings("-alert_unstable") /// test "render html to string" { /// enum Msg {} /// struct Model { /// todos : Array[String] /// } /// let page = @rabbita.simple_cell( /// model={ todos: ["todo1", "todo2"] }, /// update=(_ : Msg, model) => model, /// view=(_, model) => @html.ul(model.todos.map(x => @html.li(x))), /// ) /// inspect( /// @rabbita.render_to_string(page), /// content=( /// #| /// ), /// ) /// } /// ``` #internal(unstable, "Experimental API") pub fn render_to_string(root : Cell) -> String { @runtime.server_side_render(root.0) }