# `A2UICore.State.Surface`
[🔗](https://github.com/a2ui-elixir/a2ui/blob/v0.1.1/umbrella/apps/a2ui_core/lib/a2ui_core/state/surface.ex#L1)

The state of one UI surface: its identity, catalog selection, theme,
data model and components.

A surface is plain data. Mutating operations return the updated
surface (and, where something observable happened, effects); nothing
is delivered by callback. The processor (`A2UICore.Processor`) is what
applies protocol messages to surfaces.

  * `:id` — the surface id, unique within a group.
  * `:catalog_id` — the default catalog's id (`nil` until resolved).
  * `:catalogs` — `%{catalog_id => catalog}` available to the surface.
  * `:theme` — theme properties (v0.9; empty under v1.0).
  * `:send_data_model` — whether the renderer should attach this
    surface's data model to outbound action messages.
  * `:root_id` — the entry component, `"root"` by default.
  * `:data_model` — an `A2UICore.State.DataModel`.
  * `:components` — an `A2UICore.State.Components`.
  * `:version` — the protocol version the surface was created under.
  * `:metadata` — free-form host data, never sent on the wire.

# `t`
*since 0.1.0* 

```elixir
@type t() :: %A2UICore.State.Surface{
  catalog_id: String.t() | nil,
  catalogs: %{optional(String.t()) =&gt; term()},
  components: A2UICore.State.Components.t(),
  data_model: A2UICore.State.DataModel.t(),
  id: String.t(),
  metadata: map(),
  root_id: String.t(),
  send_data_model: boolean(),
  theme: map(),
  version: String.t() | nil
}
```

# `default_catalog`
*since 0.1.0* 

```elixir
@spec default_catalog(t()) :: A2UICore.Catalog.t() | nil
```

The surface's default catalog, or `nil` when it has none.

# `dispatch_action`
*since 0.1.0* 

```elixir
@spec dispatch_action(t(), term(), String.t(), DateTime.t()) :: map() | nil
```

Builds the action event a component interaction produces, or `nil`
when `payload` is not an action.

`payload` is either an action object (`%{"name" => …, "context" => …}`)
or `%{"event" => %{…}}`, optionally carrying a `"catalogId"`. The
event has `name`, `surfaceId`, `sourceComponentId`, `timestamp`
(RFC 3339, UTC), `context` (empty when absent or not an object) and,
when present, `catalogId` and `userMessage`.

`now` is the event time; it defaults to the current UTC time and is
a parameter so callers and tests can keep the core deterministic.

    iex> surface = A2UICore.State.Surface.new("main")
    iex> now = ~U[2026-01-02 03:04:05Z]
    iex> A2UICore.State.Surface.dispatch_action(surface, %{"name" => "save"}, "btn", now)
    %{
      "name" => "save",
      "surfaceId" => "main",
      "sourceComponentId" => "btn",
      "timestamp" => "2026-01-02T03:04:05Z",
      "context" => %{}
    }

# `dispose`
*since 0.1.0* 

```elixir
@spec dispose(t()) :: t()
```

Releases the surface's resources: detaches data-model subscriptions.

# `get_data`
*since 0.1.0* 

```elixir
@spec get_data(t(), term()) :: {:ok, term()} | {:error, A2UICore.Error.t()}
```

Reads from the surface's data model; see `A2UICore.State.DataModel.get/2`.

# `new`
*since 0.1.0* 

```elixir
@spec new(String.t(), keyword()) :: t()
```

Creates a surface. Options: `:catalog_id`, `:catalogs`, `:theme`,
`:send_data_model`, `:root_id`, `:data_model` (a model or initial
data), `:version`, `:metadata`.

    iex> surface = A2UICore.State.Surface.new("main", send_data_model: true)
    iex> {surface.id, surface.root_id, surface.send_data_model}
    {"main", "root", true}

# `protocol_version`
*since 0.1.0* 

```elixir
@spec protocol_version(t()) :: String.t() | nil
```

The surface's protocol version: its default catalog's, or, for a v1.0
surface without a default catalog, the version it was created under.

# `put_components`
*since 0.1.0* 

```elixir
@spec put_components(t(), A2UICore.State.Components.t()) :: t()
```

Replaces the surface's components.

# `resolve_catalog`
*since 0.1.0* 

```elixir
@spec resolve_catalog(t(), String.t() | nil, String.t()) ::
  {:ok, A2UICore.Catalog.t()} | {:error, A2UICore.Error.t()}
```

The catalog an item on the surface runs in: the one `catalog_id`
names (`nil` names none; an empty string names a catalog too), else
the default catalog. `subject` describes the item in the error, as in
`"Function call 'formatString'"`.

# `set_data`
*since 0.1.0* 

```elixir
@spec set_data(t(), term(), term()) ::
  {:ok, t(), [A2UICore.State.DataModel.notification()]}
  | {:error, A2UICore.Error.t()}
```

Writes into the surface's data model; see `A2UICore.State.DataModel.set/3`.

---

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