# `Emerge`
[🔗](https://github.com/emerge-elixir/emerge/blob/v0.4.0-beta.1/lib/emerge.ex#L1)

Defines Emerge viewport modules.

A viewport owns a rendered UI and receives its events. Use `Emerge` in a
module, implement `mount/1`, and implement either `render/0` or `render/1`.

`use Emerge` imports the common `Emerge.UI` helpers and aliases `Emerge` as
`Viewport` inside your module.

## Example

    defmodule MyApp.CounterViewport do
      use Emerge

      @impl Viewport
      def mount(opts) do
        {:ok, %{count: 0}, Keyword.merge([title: "Counter"], opts)}
      end

      @impl Viewport
      def render(%{count: count}) do
        row([spacing(12), padding(12)], [
          Input.button([Event.on_press(:decrement)], text("-")),
          text("Count: #{count}"),
          Input.button([Event.on_press(:increment)], text("+"))
        ])
      end

      @impl Viewport
      def handle_info(:increment, state) do
        {:noreply, Viewport.rerender(%{state | count: state.count + 1})}
      end

      def handle_info(:decrement, state) do
        {:noreply, Viewport.rerender(%{state | count: state.count - 1})}
      end
    end

Start the viewport directly:

    {:ok, viewport} = MyApp.CounterViewport.start_link()

Or supervise it:

    children = [MyApp.CounterViewport]
    Supervisor.start_link(children, strategy: :one_for_one)

`use Emerge` adds `start_link/1` and `child_spec/1` to the viewport module.

## Callbacks

`mount/1` receives the options passed to `start_link/1`. Return one of:

- `{:ok, viewport_options}` and implement `render/0`;
- `{:ok, state, viewport_options}` and implement `render/1`;
- `{:stop, reason}` to stop startup.

State must be a map. The `:__emerge__` key is reserved.

`render/0` or `render/1` returns an `Emerge.tree()` built with `Emerge.UI`.

Implement `handle_info/2` for messages and element events. Implement
`handle_input/2` only when you need raw renderer input. Implement
`handle_close/2` to customize window-close behavior; the default stops the
viewport normally.

Callback handlers return `{:noreply, state}` or
`{:stop, reason, state}`.

## Rerendering

Call `rerender/1` after changing viewport state:

    {:noreply, Viewport.rerender(%{state | count: state.count + 1})}

Return the state produced by `rerender/1`. Calling it schedules a new render;
it does not call `render/1` immediately.

## Element events

Event helpers send ordinary Elixir messages. Passing only a message sends it
to the viewport process:

    Input.button([Event.on_press(:save)], text("Save"))

    @impl Viewport
    def handle_info(:save, state) do
      {:noreply, state}
    end

To send elsewhere, pass `{pid, message}`. Payload events append the payload to
a tuple message or send `{message, payload}` for a non-tuple message. Override
`wrap_payload/3` when an integration needs another shape.

## Configuration and output modes

Viewport options choose the window/display backend, rendering API, size,
assets, diagnostics, and headless output. `Emerge.Runtime.Viewport` documents
these user-facing options and includes desktop, DRM, and headless examples.

Options can be returned at the top level or under `emerge_skia: [...]`.
Runtime-only viewport options use `viewport: [...]`.

Video elements use viewport-local atom targets. Submit owned binary or
borrowed DMA-BUF frames with `submit_video_frame/3`. Vulkan rendering and
Vulkan video import are supported where compiled and available.

# `state`

```elixir
@type state() :: map()
```

Viewport state map passed to render/1 and callback functions.

# `tree`

```elixir
@type tree() :: Emerge.Engine.Element.t()
```

Public tree type built with `Emerge.UI` and rendered by Emerge backends.

# `handle_close`
*optional* 

```elixir
@callback handle_close(term(), state()) :: {:noreply, state()} | {:stop, term(), state()}
```

# `handle_info`
*optional* 

```elixir
@callback handle_info(term(), state()) :: {:noreply, state()} | {:stop, term(), state()}
```

# `handle_input`
*optional* 

```elixir
@callback handle_input(term(), state()) :: {:noreply, state()} | {:stop, term(), state()}
```

# `mount`

```elixir
@callback mount(keyword()) ::
  {:ok, state(), keyword()} | {:ok, keyword()} | {:stop, term()}
```

# `render`
*optional* 

```elixir
@callback render() :: tree()
```

# `render`
*optional* 

```elixir
@callback render(state()) :: tree()
```

# `wrap_payload`
*optional* 

```elixir
@callback wrap_payload(term(), term(), term()) :: term()
```

# `default_wrap_payload`

```elixir
@spec default_wrap_payload(term(), term(), term()) :: term()
```

Applies Emerge's default payload-event message shape.

Tuple messages receive the payload as a final tuple element. Other messages
become `{message, payload}`. Viewports normally use this through the default
`wrap_payload/3` callback.

# `notify_source_reloaded`

```elixir
@spec notify_source_reloaded(term()) :: :ok
```

Requests a rerender from viewports after application source is reloaded.

`Emerge.Runtime.CodeReloader` calls this automatically. Custom development
reloaders may pass metadata for their own logging or coordination.

# `renderer`

```elixir
@spec renderer(pid()) :: term()
```

Returns the renderer handle owned by a running viewport.

Use this handle with renderer-specific APIs such as
`EmergeSkia.renderer_info/1`, `EmergeSkia.stats/2`, and capture.

    renderer = Emerge.renderer(viewport)
    {:ok, info} = EmergeSkia.renderer_info(renderer)

# `rerender`

```elixir
@spec rerender(state()) :: state()
```

Marks viewport state for rerendering and returns the updated state.

Call this after changing local state and return its result from the callback:

    {:noreply, Viewport.rerender(%{state | count: state.count + 1})}

# `submit_video_frame`

```elixir
@spec submit_video_frame(pid(), atom(), VideoInterop.Frame.t()) ::
  :ok | {:error, term()}
```

Submits one frame to a viewport-local video target.

The target is the same atom passed to `Emerge.UI.video/2`. Hidden targets
consume and drop frames. Visible targets retain only the latest frame.

Every normal return consumes the supplied frame: immutable binary frames need
no further action, while borrowed storage is released by Emerge when retired.

    :ok = Emerge.submit_video_frame(viewport, :camera, frame)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
