A Phoenix LiveView renderer for A2UI surfaces, built on a2ui_core. It renders the basic catalog (protocols v0.9, v0.9.1 and v1.0) as function components, keeps two-way bindings in the surface's data model, and turns user actions into renderer→agent messages.

Installation

def deps do
  [
    {:a2ui_phoenix, "~> 0.1.0"}
  ]
end

Usage

defmodule MyAppWeb.AgentLive do
  use MyAppWeb, :live_view

  alias A2UIPhoenix.{Live, Session}

  def mount(_params, _session, socket) do
    # Check every component against its catalog; relaxed validation still
    # accepts a surface whose components arrive over several messages.
    validation = A2UICore.Validation.Config.relaxed()
    session = Session.new([A2UICore.Catalog.Basic.catalog("v1.0")], validation: validation)
    {:ok, Live.init(socket, session, on_send: &send_to_agent/2)}
  end

  # Messages from the agent, however they arrive (SSE, A2A, a process).
  def handle_info({:agent, payload}, socket) do
    {_result, socket} = Live.handle_message(socket, payload)
    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <.live_component
      :for={surface <- Session.surfaces(@a2ui)}
      module={A2UIPhoenix.Surface}
      id={"surface-" <> surface.id}
      session={@a2ui}
      surface_id={surface.id}
    />
    """
  end

  # Messages for the agent (`action`, `error`, `callAgentFunction`,
  # `rendererFunctionResponse`), here broadcast to the process that talks to it.
  defp send_to_agent(message, socket) do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "a2ui:renderer", {:renderer, self(), message})
    socket
  end
end

Add priv/static/a2ui_phoenix.js to the page's scripts, so that openUrl actions open their URL, and priv/static/a2ui_phoenix.css, the components' design system: light and dark, themed by --a2ui-* custom properties (or replaced by your own styles for the a2ui-* classes). The script also holds the hook of the MCP catalog's McpApp component (hooks: {...window.A2UIPhoenix.hooks}), whose sandbox proxy page, priv/static/a2ui_mcp_sandbox.html, the endpoint serves. Rendering surfaces shows how to load them, and covers theming, agent functions and the MCP catalog.

A catalog of your own brings its components as a map:

<.live_component module={A2UIPhoenix.Surface} components={%{"Chart" => &chart/1}} ... />

Each component receives @ctx, @node, @props (bindings resolved), @a11y and @attrs; see A2UIPhoenix.Components.

Streaming to other renderers

A2UIPhoenix.Plug.SSE streams A2UI messages to a renderer outside LiveView, such as a web renderer in the browser, as server-sent events, one message per event:

# In a controller:
def events(conn, params), do: A2UIPhoenix.Plug.SSE.stream(conn, MyAgent.messages(params))

# Or mounted in a router; for each request the plug calls MyAgent.messages(conn):
forward "/a2ui/events", A2UIPhoenix.Plug.SSE, source: {MyAgent, :messages, []}

License

Apache-2.0