diff --git a/README.md b/README.md
index b214c6b3..fc23432a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Ash UI
-A resource-driven UI framework for Elixir built on the Ash Framework, enabling dynamic UI generation from database resources through Phoenix LiveView and static web rendering.
+A resource-driven UI framework for Elixir built on the Ash Framework, enabling dynamic UI generation from database resources through the unified UI rendering ecosystem.
## Overview
@@ -8,7 +8,7 @@ Ash UI provides a declarative approach to building user interfaces by defining U
- **Database-Driven UI** - Define screens and elements as resources
- **Reactive Data Binding** - Connect UI directly to Ash resources
-- **Dual Rendering** - Output to LiveView (interactive) or static HTML
+- **Multi-Platform Rendering** - Output to LiveView, static HTML, or desktop via unified renderer packages
- **Type Safety** - Leverage Ash's type system for UI components
- **Authorization-First** - Built-in policy-based access control
@@ -24,22 +24,38 @@ flowchart LR
subgraph AshUI["Ash UI Framework"]
Compiler["Compiler"]
- IUR["IUR"]
- Renderer["Renderer"]
+ IUR["Ash IUR"]
+ Adapter["IUR Adapter"]
+ end
+
+ subgraph Unified["Unified Ecosystem"]
+ Canonical["Canonical IUR"]
+ end
+
+ subgraph Renderers["Renderer Packages"]
+ Live["live_ui"]
+ Web["web_ui"]
+ Desktop["desktop_ui"]
end
subgraph Output["User Interface"]
- Live["LiveView"]
+ LV["LiveView"]
HTML["Static HTML"]
+ Native["Desktop UI"]
end
Element --> Compiler
Screen --> Compiler
Binding --> Compiler
Compiler --> IUR
- IUR --> Renderer
- Renderer --> Live
- Renderer --> HTML
+ IUR --> Adapter
+ Adapter --> Canonical
+ Canonical --> Live
+ Canonical --> Web
+ Canonical --> Desktop
+ Live --> LV
+ Web --> HTML
+ Desktop --> Native
```
## Quick Start
@@ -52,6 +68,8 @@ Add to your `mix.exs`:
def deps do
[
{:ash_ui, "~> 0.1"},
+ {:unified_iur, "~> 0.1"}, # Canonical IUR format
+ {:live_ui, "~> 0.1"}, # LiveView renderer (or web_ui, desktop_ui)
{:ash, "~> 3.0"},
{:phoenix_live_view, "~> 1.0"}
]
@@ -82,7 +100,6 @@ end
```elixir
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
- use AshUI.LiveView
def mount(params, _session, socket) do
{:ok, mount_ui_screen(socket, :dashboard, params)}
@@ -90,6 +107,8 @@ defmodule MyAppWeb.DashboardLive do
end
```
+The `mount_ui_screen/3` helper handles compilation, IUR conversion, and rendering.
+
## Documentation
### User Guides
@@ -127,9 +146,9 @@ This project is in early development. The governance system and core architectur
|---|---|
| Governance System | ✅ Implemented |
| Resource Definitions | 🚧 In Progress |
-| Compilation Pipeline | 🚧 Planned |
-| LiveView Rendering | 🚧 Planned |
-| Static Rendering | 🚧 Planned |
+| Compilation Pipeline | 🚧 In Progress |
+| IUR Adapter | 🚧 Planned |
+| Renderer Integration | 🚧 Planned (via unified packages) |
## Governance
@@ -148,11 +167,16 @@ See [RFC-0001](rfcs/RFC-0001-ash-ui-governance-system.md) for details on the gov
| Control Plane | Scope | Module |
|---|---|---|
| Framework | Resource definitions, type system | `AshUI.Framework` |
-| Compilation | Resource → IUR pipeline | `AshUI.Compilation` |
-| Rendering | Output generation | `AshUI.Rendering` |
+| Compilation | Resource → canonical IUR pipeline | `AshUI.Compilation` |
+| Rendering | IUR adaptation, renderer delegation | `AshUI.Rendering` |
| Runtime | Session lifecycle | `AshUI.Runtime` |
| Extension | Widgets, plugins | `AshUI.Extension` |
+**External Renderer Packages** (unified ecosystem):
+- `live_ui` - Phoenix LiveView rendering
+- `web_ui` - Static HTML + Elm rendering
+- `desktop_ui` - Native desktop rendering
+
See [Control Plane Ownership](specs/contracts/control_plane_ownership_matrix.md) for details.
## Contributing
@@ -171,6 +195,11 @@ We welcome contributions! Please:
## Related Projects
- [Ash Framework](https://ash-hq.org/) - The declarative foundation
+- [Unified UI Ecosystem](https://github.com/your-org/unified) - Renderer packages:
+ - [unified_iur](https://github.com/your-org/unified/tree/main/packages/unified_iur) - Canonical IUR format
+ - [live_ui](https://github.com/your-org/unified/tree/main/packages/live_ui) - LiveView renderer
+ - [web_ui](https://github.com/your-org/unified/tree/main/packages/web_ui) - Static HTML renderer
+ - [desktop_ui](https://github.com/your-org/unified/tree/main/packages/desktop_ui) - Desktop renderer
- [Phoenix](https://www.phoenixframework.org/) - The web framework
- [Phoenix LiveView](https://hexdocs.pm/phoenix_live_view) - Real-time UI
diff --git a/guides/developer/DG-0001-architecture-overview.md b/guides/developer/DG-0001-architecture-overview.md
index 4f823d79..3bd5181c 100644
--- a/guides/developer/DG-0001-architecture-overview.md
+++ b/guides/developer/DG-0001-architecture-overview.md
@@ -62,8 +62,15 @@ flowchart TB
end
subgraph Rendering["Rendering Control Plane"]
- LiveRenderer["LiveView Renderer"]
- WebRenderer["Static Renderer"]
+ Adapter["IUR Adapter"]
+ Registry["Renderer Registry"]
+ end
+
+ subgraph Unified["Unified Ecosystem"]
+ Canonical["Canonical IUR"]
+ LiveRenderer["LiveUI.Renderer"]
+ WebRenderer["WebUI.Renderer"]
+ DesktopRenderer["DesktopUI.Renderer"]
end
LiveView --> Session
@@ -76,9 +83,15 @@ flowchart TB
Compiler --> IUR
IUR --> Validator
Validator --> Normalizer
+ Normalizer --> Adapter
+ Adapter --> Canonical
+
+ Canonical --> LiveRenderer
+ Canonical --> WebRenderer
+ Canonical --> DesktopRenderer
- Normalizer --> LiveRenderer
- Normalizer --> WebRenderer
+ LiveRenderer --> LiveView
+ WebRenderer --> Static
Validator --> Element
Validator --> Screen
@@ -95,12 +108,14 @@ flowchart TB
classDef rendering fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef runtime fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
classDef data fill:#eceff1,stroke:#37474f,stroke-width:2px
+ classDef unified fill:#e0f2f1,stroke:#00695c,stroke-width:2px
class Element,Screen,Binding framework
class Compiler,IUR,Validator,Normalizer compilation
- class LiveRenderer,WebRenderer rendering
+ class Adapter,Registry rendering
class Session,Event,Lifecycle runtime
class Resources,DB data
+ class Canonical,LiveRenderer,WebRenderer,DesktopRenderer unified
```
## Control Planes
@@ -141,13 +156,16 @@ Ash UI is organized into five control planes, each with distinct authority and r
**Module**: `AshUI.Rendering`
-**Authority**: Output generation for target platforms
+**Authority**: IUR adaptation and renderer delegation
**Components**:
-- `AshUI.Renderer.LiveView` - LiveView rendering
-- `AshUI.Renderer.Static` - Static HTML rendering
-- `AshUI.Renderer.Registry` - Renderer registry
-- `AshUI.Renderer.Presenter` - Presentation logic
+- `AshUI.Rendering.IURAdapter` - Converts Ash IUR to canonical unified_iur format
+- `AshUI.Rendering.Registry` - Manages available renderer packages
+
+**External Renderer Packages** (unified ecosystem):
+- `live_ui` - LiveView rendering (https://github.com/your-org/unified/tree/main/packages/live_ui)
+- `web_ui` - Static HTML rendering (https://github.com/your-org/unified/tree/main/packages/web_ui)
+- `desktop_ui` - Desktop rendering (https://github.com/your-org/unified/tree/main/packages/desktop_ui)
**Key Contract**: [rendering_contract.md](../../specs/contracts/rendering_contract.md)
@@ -202,7 +220,7 @@ flowchart LR
## Intermediate UI Representation (IUR)
-### IUR Schema
+### Ash UI IUR Schema (Internal)
```elixir
%AshUI.Compilation.IUR{
@@ -217,12 +235,25 @@ flowchart LR
}
```
+### Canonical IUR Schema (unified_iur package)
+
+```elixir
+%UnifiedIUR.Screen{
+ id: UUID.t(),
+ elements: [%UnifiedIUR.Element{}],
+ layout: UnifiedIUR.Layout.t(),
+ signals: [%UnifiedIUR.Signal{}],
+ metadata: map()
+}
+```
+
### IUR Properties
- **Serializable** - Can be encoded to JSON/binary
- **Immutable** - IUR instances are never modified
- **Self-contained** - All information needed for rendering
- **Versioned** - Schema version for compatibility
+- **Convertible** - Ash UI IUR converts to canonical unified_iur format
## Module Namespace
@@ -243,10 +274,8 @@ AshUI # Application root
│ ├── Normalizer
│ └── Cache
├── Rendering # Rendering Control Plane
-│ ├── LiveView
-│ ├── Static
-│ ├── Registry
-│ └── Presenter
+│ ├── IURAdapter # Ash IUR → Canonical IUR
+│ └── Registry # Renderer package management
├── Runtime # Runtime Control Plane
│ ├── Session
│ ├── Event
@@ -257,6 +286,12 @@ AshUI # Application root
└── Loader
```
+**External Packages** (unified ecosystem):
+- `unified_iur` - Canonical IUR schema and types
+- `live_ui` - LiveView renderer
+- `web_ui` - Static HTML renderer
+- `desktop_ui` - Desktop renderer
+
## Extension Points
### Custom Element Types
@@ -278,21 +313,36 @@ AshUI.Extension.Widget.register(:my_custom, MyApp.CustomElement)
### Custom Renderers
-Implement the renderer behaviour:
+Custom renderers are implemented via the unified ecosystem packages. To create a custom renderer:
+
+1. **For Ash UI integration**: Ensure your renderer accepts canonical unified_iur format
+2. **Implement renderer contract**: Follow the unified ecosystem renderer spec
+3. **Register with Ash UI**: Add to renderer registry
```elixir
-defmodule MyApp.CustomRenderer do
- @behaviour AshUI.Renderer
+# Example: Using live_ui renderer
+defmodule MyAppWeb.MyLive do
+ use Phoenix.LiveView
- def render(iur, opts) do
- # Custom rendering logic
- end
+ def mount(_params, _session, socket) do
+ # Compile Ash UI screen
+ {:ok, iur} = AshUI.Compilation.compile(:my_screen, %{})
- def can_render?(iur), do: true
- def format, do: :custom
+ # Convert to canonical IUR
+ {:ok, canonical_iur} = AshUI.Rendering.IURAdapter.to_canonical(iur)
+
+ # Render via live_ui
+ {:ok, heex} = LiveUI.Renderer.render(canonical_iur, [])
+
+ {:ok, assign(socket, :content, heex)}
+ end
end
```
+For creating custom renderer packages, see:
+- [Unified Ecosystem Architecture](https://github.com/your-org/unified/blob/main/.spec/specs/architecture.spec.md)
+- [Platform Runtimes Spec](https://github.com/your-org/unified/blob/main/.spec/specs/platform_runtimes.spec.md)
+
## Data Flow
### LiveView Request Flow
diff --git a/guides/user/UG-0001-getting-started.md b/guides/user/UG-0001-getting-started.md
index 881b87ce..52039238 100644
--- a/guides/user/UG-0001-getting-started.md
+++ b/guides/user/UG-0001-getting-started.md
@@ -16,7 +16,7 @@ diagram_required: true
## Overview
-This guide introduces Ash UI, a resource-driven UI framework for Elixir built on the Ash Framework. Ash UI enables dynamic UI generation from database resources through Phoenix LiveView and static web rendering.
+This guide introduces Ash UI, a resource-driven UI framework for Elixir built on the Ash Framework. Ash UI enables dynamic UI generation from database resources through the unified UI rendering ecosystem.
## What is Ash UI?
@@ -24,7 +24,8 @@ Ash UI is a framework that:
- **Defines UI as Resources** - UI components are Ash resources stored in your database
- **Compiles to IUR** - Resources are compiled to an Intermediate UI Representation
-- **Renders Anywhere** - Output to LiveView, static HTML, or future formats
+- **Converts to Canonical IUR** - IUR is converted to canonical unified_iur format
+- **Renders via Unified Packages** - Output to LiveView, static HTML, or desktop via external renderer packages
- **Binds Data Reactively** - Connect UI elements directly to Ash resources
## Architecture Overview
@@ -37,28 +38,48 @@ flowchart LR
subgraph AshUI["Ash UI Framework"]
Compiler["Resource Compiler"]
- IUR["Intermediate UI\nRepresentation"]
- Renderer["Renderer"]
+ IUR["Ash IUR"]
+ Adapter["IUR Adapter"]
+ end
+
+ subgraph Unified["Unified Ecosystem"]
+ Canonical["Canonical IUR"]
+ end
+
+ subgraph Renderers["Renderer Packages"]
+ Live["LiveUI.Renderer"]
+ Web["WebUI.Renderer"]
+ Desktop["DesktopUI.Renderer"]
end
subgraph Output["User Interface"]
- Live["Phoenix LiveView"]
- Static["Static HTML"]
+ LV["LiveView"]
+ HTML["Static HTML"]
+ Native["Desktop UI"]
end
Resource --> Compiler
Compiler --> IUR
- IUR --> Renderer
- Renderer --> Live
- Renderer --> Static
+ IUR --> Adapter
+ Adapter --> Canonical
+ Canonical --> Live
+ Canonical --> Web
+ Canonical --> Desktop
+ Live --> LV
+ Web --> HTML
+ Desktop --> Native
classDef input fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef aui fill:#fff3e0,stroke:#e65100,stroke-width:2px
+ classDef unified fill:#e0f2f1,stroke:#00695c,stroke-width:2px
+ classDef renderer fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef output fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
class Resource input
- class Compiler,IUR,Renderer aui
- class Live,Static output
+ class Compiler,IUR,Adapter aui
+ class Canonical unified
+ class Live,Web,Desktop renderer
+ class LV,HTML,Native output
```
## Prerequisites
@@ -72,13 +93,17 @@ Before using Ash UI, you should have:
## Installation
-Add Ash UI to your dependencies:
+Add Ash UI and dependencies to your `mix.exs`:
```elixir
# mix.exs
defp deps do
[
{:ash_ui, "~> 0.1"},
+ {:unified_iur, "~> 0.1"}, # Canonical IUR format
+ {:live_ui, "~> 0.1"}, # LiveView renderer (choose one)
+ # {:web_ui, "~> 0.1"} # Static HTML renderer (alternative)
+ # {:desktop_ui, "~> 0.1"} # Desktop renderer (alternative)
{:ash, "~> 3.0"},
{:phoenix_live_view, "~> 1.0"}
]
@@ -163,14 +188,15 @@ end
```elixir
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
- use AshUI.LiveView
- def mount(params, session, socket) do
+ def mount(params, _session, socket) do
{:ok, mount_ui_screen(socket, :dashboard, params)}
end
end
```
+The `mount_ui_screen/3` helper handles compilation, IUR conversion, and rendering via your configured renderer package (e.g., `live_ui`).
+
## Core Concepts
### UI.Element
diff --git a/rfcs/RFC-0002-ash-ui-unified-integration.md b/rfcs/RFC-0002-ash-ui-unified-integration.md
new file mode 100644
index 00000000..b789f9f4
--- /dev/null
+++ b/rfcs/RFC-0002-ash-ui-unified-integration.md
@@ -0,0 +1,390 @@
+# RFC-0002: Ash UI as an Ash Framework Integration Layer for unified-ui
+
+**Status**: Draft
+**Phase**: 1
+**Authors**: Ash UI Team
+**Created**: 2026-03-19
+**Modified**: 2026-03-19
+**Replaces**: N/A
+**Replaced By**: N/A
+
+---
+
+## Summary
+
+Reposition Ash UI as an **Ash Framework integration layer** for the unified-ui ecosystem, rather than defining parallel UI concepts. Ash UI's unique value is storing unified-ui definitions as Ash Resources with Ash authorization, actions, and data binding — while delegating widgets, layouts, styling, compilation, and rendering to the unified-ui packages.
+
+## Motivation
+
+### Problem Statement
+
+The current Ash UI architecture defines its own UI concepts (`UI.Screen`, `UI.Element`, `UI.Binding`) that parallel the unified-ui ecosystem. This creates:
+
+1. **Conceptual duplication** - Two ways to define the same UI constructs
+2. **Maintenance burden** - Need to sync Ash UI concepts with unified-ui changes
+3. **Confusion for users** - Which widget/layout concepts should they use?
+4. **Fragmentation** - Ash UI cannot benefit from unified-ui ecosystem growth
+
+### Current Limitations
+
+- `UI.Screen` duplicates unified-ui's root element + layout composition
+- `UI.Element` invents widget types instead of using unified-ui widgets
+- `UI.Binding` is Ash-specific but doesn't integrate cleanly with unified-ui signals
+- Compilation pipeline duplicates unified-ui compiler work
+- No clear boundary between "Ash concerns" and "UI concerns"
+
+### Proposed Solution
+
+**Ash UI = unified-ui + Ash Framework integration**
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Ash UI │
+│ ┌─────────────────────────────────────────────────────┐ │
+│ │ Ash-Specific Layer │ │
+│ │ • UI definitions stored as Ash Resources │ │
+│ │ • Ash policies for authorization │ │
+│ │ • Ash actions for CRUD operations │ │
+│ │ • Data binding to Ash resources │ │
+│ └─────────────────────────────────────────────────────┘ │
+│ ↓ │
+│ ┌─────────────────────────────────────────────────────┐ │
+│ │ Adapter Layer │ │
+│ │ • Ash Resource → unified-ui DSL │ │
+│ │ • Ash data → unified-ui signals │ │
+│ │ • Ash policies → unified-ui visibility │ │
+│ └─────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────┘
+ ↓
+┌─────────────────────────────────────────────────────────────┐
+│ Unified UI Ecosystem │
+│ • unified-ui: DSL, widgets, layouts, styling, theming │
+│ • unified_iur: Canonical intermediate representation │
+│ • live_ui, web_ui, desktop_ui: Renderer packages │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Proposed Design
+
+### Overview
+
+Ash UI becomes a **thin integration layer** that:
+
+1. **Stores** unified-ui DSL definitions as Ash Resources
+2. **Authorizes** UI access via Ash policies
+3. **Binds** UI data to Ash resources
+4. **Adapts** Ash concerns to unified-ui concepts
+5. **Delegates** everything else (widgets, layouts, compilation, rendering) to unified-ui
+
+### Technical Details
+
+#### Resource Structure
+
+```elixir
+# Ash UI stores unified-ui definitions as Ash Resources
+defmodule MyApp.UI.DashboardScreen do
+ use Ash.Resource,
+ domain: MyApp.UI,
+ data_layer: AshPostgres.DataLayer
+
+ # The unified-ui DSL is stored in an attribute
+ attributes do
+ uuid_primary_key :id
+ attribute :name, :string
+ attribute :unified_dsl, :map do
+ # Stores unified-ui DSL structure:
+ # %{
+ # root: %{type: :row, children: [...]},
+ # styles: %{...},
+ # signals: %{...}
+ # }
+ end
+ attribute :version, :integer
+ end
+
+ # Ash authorization for UI access
+ policies do
+ policy always() do
+ authorize_if expr(user.role == :admin)
+ end
+ end
+
+ # Ash actions for UI CRUD
+ actions do
+ defaults [:read, :create, :update, :destroy]
+ end
+end
+```
+
+#### Data Binding
+
+```elixir
+# Ash data binding to unified-ui signals
+defmodule AshUI.Data.Binding do
+ @moduledoc """
+ Binds Ash resources to unified-ui signals.
+ """
+
+ def bind_signal(%Ash.Resource{} = resource, signal_path) do
+ # Converts Ash resource references to unified-ui signal format
+ %UnifiedIUR.Signal{
+ source: "ash:#{inspect(resource)}:#{signal_path}",
+ type: :value,
+ transform: &Ash.read!/1
+ }
+ end
+end
+```
+
+#### API Usage
+
+```elixir
+# Before: Ash UI DSL
+defmodule MyApp.UI.Dashboard do
+ use Ash.Resource
+
+ ui_screen do
+ layout :dashboard
+ elements [...]
+ end
+end
+
+# After: unified-ui DSL stored as Ash Resource
+defmodule MyApp.UI.Dashboard do
+ use Ash.Resource
+
+ attributes do
+ attribute :unified_dsl, :map
+ end
+
+ # Use unified-ui directly
+ def unified_dsl do
+ %UnifiedUI.Root{
+ type: :row,
+ children: [
+ UnifiedUI.text("Welcome"),
+ UnifiedUI.button("Click", on_click: [...] )
+ ]
+ }
+ end
+end
+```
+
+#### Compilation Flow
+
+```mermaid
+sequenceDiagram
+ participant App as Application
+ participant Ash as Ash UI
+ participant Unified as Unified UI
+ participant Renderer as live_ui
+
+ App->>Ash: Load UI Resource
+ Ash->>Ash: Check Policies
+ Ash->>Unified: Pass DSL
+ Unified->>Unified: Compile to IUR
+ Unified->>Renderer: Render IUR
+ Renderer->>App: Output
+```
+
+### API Changes
+
+```elixir
+# New Ash API
+defmodule AshUI.Resource do
+ @moduledoc """
+ Helpers for using unified-ui with Ash Resources.
+ """
+
+ defmacro __using__(_opts) do
+ quote do
+ # Store unified-ui DSL
+ attribute :unified_dsl, :map
+
+ # Ash policies for UI access
+ policies do
+ policy action(:render) do
+ authorize_if expr(user.active)
+ end
+ end
+ end
+ end
+end
+
+# Usage
+defmodule MyApp.UI.Dashboard do
+ use Ash.Resource
+ use AshUI.Resource
+
+ unified_ui do
+ # Direct unified-ui DSL here
+ root do
+ row do
+ text "Welcome"
+ button "Click", on_click: Signal.new(...)
+ end
+ end
+ end
+end
+```
+
+### Migration Path
+
+1. **Phase 1**: Add adapter layer to existing Ash UI
+2. **Phase 2**: Deprecate `ui_screen`/`ui_element` DSL in favor of `unified_ui`
+3. **Phase 3**: Remove old DSL after deprecation period
+4. **Phase 4**: Ash UI becomes pure integration layer
+
+### Performance Impact
+
+- **Positive**: Less code to maintain in Ash UI
+- **Positive**: Benefiting from unified-ui optimizations
+- **Neutral**: Adapter layer adds minimal overhead
+- **Negative**: Initial migration cost
+
+## Governance Mapping
+
+### Requirements
+
+| REQ ID | Description | Contract |
+|---|---|---|
+| REQ-ASHUI-001 | Ash UI must store unified-ui DSL as Ash Resources | resource_contract.md (revised) |
+| REQ-ASHUI-002 | Ash UI must authorize UI access via Ash policies | authorization_contract.md |
+| REQ-ASHUI-003 | Ash UI must bind Ash data to unified-ui signals | binding_contract.md (revised) |
+| REQ-ASHUI-004 | Ash UI must not define widget/layout types | Removed (delegated to unified-ui) |
+| REQ-ASHUI-005 | Ash UI must adapt Ash resources to unified-ui DSL | adapter_contract.md (new) |
+
+### Scenarios
+
+| SCN ID | Description | Status |
+|---|---|---|
+| SCN-ASHUI-001 | Load unified-ui definition from Ash Resource | Draft |
+| SCN-ASHUI-002 | Authorize UI access via Ash policies | Draft |
+| SCN-ASHUI-003 | Bind Ash resource data to UI signals | Draft |
+| SCN-ASHUI-004 | Compile unified-ui DSL to IUR | Draft |
+| SCN-ASHUI-005 | Render via live_ui/web_ui packages | Draft |
+
+### Contracts
+
+- **Modified**: resource_contract.md (stores unified-ui DSL)
+- **Modified**: authorization_contract.md (Ash policies for UI)
+- **Modified**: binding_contract.md (Ash data → unified signals)
+- **Modified**: compilation_contract.md (uses unified-ui compiler)
+- **Removed**: screen_contract.md (replaced by unified-ui root)
+- **Removed**: element_contract.md (replaced by unified-ui widgets)
+- **New**: adapter_contract.md (Ash ↔ unified-ui adaptation)
+
+### ADRs
+
+- **New**: ADR-0002 - Ash UI Architecture (redefines Ash UI as integration layer)
+- **Modified**: ADR-0001 - Update to reflect new architecture
+
+## Spec Creation Plan
+
+| Spec | Type | Status | Priority |
+|---|---|---|---|
+| adapter_contract.md | Contract | Draft | P0 |
+| resource_contract.md (revised) | Contract | Draft | P0 |
+| binding_contract.md (revised) | Contract | Draft | P0 |
+| topology.md (revised) | Component | Draft | P0 |
+| UG-0001 (revised) | Guide | Draft | P1 |
+| DG-0001 (revised) | Guide | Draft | P1 |
+
+## Alternatives
+
+### Alternative 1: Keep Parallel Concepts (Current)
+
+Continue defining Ash-specific UI concepts alongside unified-ui.
+
+**Pros**:
+- No migration work
+- Full control over Ash-specific UI needs
+
+**Cons**:
+- Duplicates unified-ui work
+- Confusing for users
+- Maintenance burden
+- Cannot benefit from unified-ui ecosystem growth
+
+### Alternative 2: Replace with unified-ui Entirely
+
+Drop Ash UI entirely; tell users to use unified-ui directly with their own Ash integration.
+
+**Pros**:
+- Cleanest separation
+
+**Cons**:
+- Each app builds their own integration
+- No shared Ash authorization patterns
+- No community around Ash + unified-ui
+
+### Alternative 3: Ash UI Becomes unified-ui Extension
+
+Build Ash UI as an official unified-ui extension for Ash Framework.
+
+**Pros**:
+- Official unified-ui ecosystem presence
+- Shared governance
+
+**Cons**:
+- Adds overhead of cross-repo coordination
+- Unified-ui may not want Ash-specific code
+
+### Rejected Alternatives
+
+**Alternative 1** was rejected because the long-term maintenance cost outweighs short-term convenience.
+
+**Alternative 2** was rejected because it pushes integration work onto each application.
+
+**Alternative 3** may be considered in the future after initial integration proves successful.
+
+## Unresolved Questions
+
+1. How do we handle existing `ui_screen`/`ui_element` definitions during migration?
+2. Should Ash UI provide migration tools from old DSL to unified-ui DSL?
+3. How do we handle Ash-specific features (like authorization) that unified-ui doesn't have?
+4. What's the timeline for deprecating old DSL?
+
+## Implementation Plan
+
+### Milestones
+
+| Milestone | Description | Status |
+|---|---|---|
+| M1 | RFC accepted | Pending |
+| M2 | Create adapter_contract.md | Pending |
+| M3 | Revise resource_contract.md | Pending |
+| M4 | Update topology.md | Pending |
+| M5 | Create migration guide | Pending |
+| M6 | Implement adapter layer | Pending |
+| M7 | Deprecate old DSL | Pending |
+
+### Tasks
+
+- [ ] RFC review and approval
+- [ ] Create adapter_contract.md spec
+- [ ] Revise resource_contract.md for unified-ui storage
+- [ ] Revise binding_contract.md for unified-ui signals
+- [ ] Update topology.md architecture diagram
+- [ ] Update ADR-0001
+- [ ] Create ADR-0002
+- [ ] Write migration guide
+- [ ] Implement Ash ↔ unified-ui adapter
+- [ ] Add deprecation warnings to old DSL
+- [ ] Update user and developer guides
+- [ ] Update scenario catalog
+
+## References
+
+- [unified-ui Architecture](https://github.com/your-org/unified/blob/main/.spec/specs/architecture.spec.md)
+- [unified-ui Widgets](https://github.com/your-org/unified/blob/main/.spec/specs/unified-ui/widgets.spec.md)
+- [unified-iur Constructs](https://github.com/your-org/unified/blob/main/.spec/specs/unified-iur/constructs.spec.md)
+- [RFC-0001: Ash UI Governance System](RFC-0001-ash-ui-governance-system.md)
+- [ADR-0001: Control Plane Authority](../specs/adr/ADR-0001-control-plane-authority.md)
+
+---
+
+## Changelog
+
+### 2026-03-19
+- Initial draft
diff --git a/rfcs/index.md b/rfcs/index.md
index fc189207..ce6b348f 100644
--- a/rfcs/index.md
+++ b/rfcs/index.md
@@ -7,6 +7,7 @@ This index lists all RFCs for the Ash UI framework.
| ID | Title | Status | Authors | Created | Target Phase |
|---|---|---|---|---|---|
| [RFC-0001](RFC-0001-ash-ui-governance-system.md) | Ash UI Governance System | Active | Governance Team | 2026-03-18 | 1 |
+| [RFC-0002](RFC-0002-ash-ui-unified-integration.md) | Ash UI as unified-ui Integration Layer | Draft | Ash UI Team | 2026-03-19 | 1 |
## RFC Status Legend
@@ -26,6 +27,7 @@ This index lists all RFCs for the Ash UI framework.
### Phase 1: Foundation
- RFC-0001: Ash UI Governance System
+- RFC-0002: Ash UI as unified-ui Integration Layer
### Phase 2: Core Features
- (No RFCs yet)
@@ -38,6 +40,9 @@ This index lists all RFCs for the Ash UI framework.
### Governance
- RFC-0001: Ash UI Governance System
+### Architecture
+- RFC-0002: Ash UI as unified-ui Integration Layer
+
### Resources
- (No RFCs yet)
@@ -52,7 +57,8 @@ This index lists all RFCs for the Ash UI framework.
## Recently Updated
-1. **RFC-0001** (2026-03-18) - Initial governance system
+1. **RFC-0002** (2026-03-19) - Ash UI as unified-ui Integration Layer
+2. **RFC-0001** (2026-03-18) - Initial governance system
## Submitting an RFC
diff --git a/specs/contracts/compilation_contract.md b/specs/contracts/compilation_contract.md
index 90817dac..9f7ca6da 100644
--- a/specs/contracts/compilation_contract.md
+++ b/specs/contracts/compilation_contract.md
@@ -1,10 +1,10 @@
# Compilation Contract (REQ-COMP-*)
-This contract defines the normative requirements for Resource → IUR compilation in the Ash UI framework.
+This contract defines the normative requirements for Resource → IUR → canonical IUR compilation in the Ash UI framework.
## Purpose
-Defines the requirements for compiling Ash UI resources (UI.Element, UI.Screen, UI.Binding) into the Intermediate UI Representation (IUR), which serves as the canonical format for rendering.
+Defines the requirements for compiling Ash UI resources (UI.Element, UI.Screen, UI.Binding) into the Intermediate UI Representation (IUR), and then converting to canonical `unified_iur` format for consumption by external renderer packages.
## Control Plane
@@ -14,6 +14,7 @@ Defines the requirements for compiling Ash UI resources (UI.Element, UI.Screen,
- REQ-RES-*: Source resource definitions
- REQ-FRAMEWORK-*: Framework contracts
+- **unified_iur** - Canonical intermediate representation format (external package)
## Requirements
@@ -54,9 +55,9 @@ Resource schemas MUST be validated before IUR generation.
### REQ-COMP-003: IUR Schema
-The Intermediate UI Representation MUST have a defined schema.
+The Intermediate UI Representation MUST have a defined schema compatible with canonical `unified_iur`.
-**IUR Structure**:
+**IUR Structure** (Ash UI internal):
```elixir
%AshUI.Compilation.IUR{
id: UUID.t(),
@@ -69,10 +70,21 @@ The Intermediate UI Representation MUST have a defined schema.
}
```
+**Canonical IUR Structure** (unified_iur package):
+```elixir
+%UnifiedIUR.Screen{
+ id: UUID.t(),
+ elements: [%UnifiedIUR.Element{}],
+ layout: UnifiedIUR.Layout.t(),
+ signals: [%UnifiedIUR.Signal{}],
+ metadata: map()
+}
+```
+
**Acceptance Criteria**:
-- AC-001: IUR has a version identifier
+- AC-001: Ash UI IUR is convertible to canonical unified_iur
- AC-002: IUR is serializable (to JSON/binary)
-- AC-003: IUR contains all required information for rendering
+- AC-003: IUR contains all required information for canonical conversion
- AC-004: IUR is independent of source resource format
### REQ-COMP-004: Resource Resolution
diff --git a/specs/contracts/control_plane_ownership_matrix.md b/specs/contracts/control_plane_ownership_matrix.md
index 2f0ea814..9322d7ae 100644
--- a/specs/contracts/control_plane_ownership_matrix.md
+++ b/specs/contracts/control_plane_ownership_matrix.md
@@ -38,10 +38,16 @@ This document defines the ownership boundaries and authority delegation across a
| Component | Module | Owner | Dependencies |
|---|---|---|---|
-| LiveView Renderer | `AshUI.Renderer.LiveView` | Rendering | Phoenix.LiveView |
-| Static Renderer | `AshUI.Renderer.Static` | Rendering | Phoenix.Template |
-| Presenter Layer | `AshUI.Renderer.Presenter` | Rendering | IUR |
-| Renderer Registry | `AshUI.Renderer.Registry` | Rendering | - |
+| IUR Adapter | `AshUI.Rendering.IURAdapter` | Rendering | unified_iur |
+| Renderer Registry | `AshUI.Rendering.Registry` | Rendering | live_ui, web_ui, desktop_ui |
+
+**External Renderer Packages** (consumed as dependencies):
+
+| Package | Module | Owner | Dependencies |
+|---|---|---|---|
+| live_ui | `LiveUI.Renderer` | External | Phoenix.LiveView, unified_iur |
+| web_ui | `WebUI.Renderer` | External | Phoenix, Elm, unified_iur |
+| desktop_ui | `DesktopUI.Renderer` | External | SDL2, unified_iur |
### Runtime Control Plane
@@ -90,18 +96,20 @@ The Compilation Control Plane has exclusive authority over:
**REQ-COMP-002**: Compiler must report all validation errors before IUR generation.
-### Rendering Authority (EXCLUSIVE)
+### Rendering Authority (DELEGATED)
+
+The Rendering Control Plane delegates actual rendering to external unified renderer packages:
-The Rendering Control Plane has exclusive authority over:
+- **IUR Production**: Compiles Ash Resources to canonical `unified_iur` format
+- **Renderer Selection**: Application selects target renderer package (live_ui, web_ui, desktop_ui)
+- **Adapter Contract**: Defines interface for IUR conversion and renderer invocation
+- **Package Integration**: Manages dependencies on external renderer packages
-- **Renderer Contracts**: Interface for all renderers
-- **Output Format**: Structure of rendered output
-- **Presentation Logic**: How data is formatted for display
-- **Renderer Selection**: How the appropriate renderer is chosen
+**REQ-RENDER-001**: Ash UI must produce valid canonical `unified_iur` for external renderers.
-**REQ-RENDER-001**: All renderers must implement the Renderer contract.
+**REQ-RENDER-002**: External renderer packages must accept canonical IUR without Ash-specific dependencies.
-**REQ-RENDER-002**: Renderers must produce valid output for all valid IUR.
+**Note**: LiveView, Static HTML, and Desktop renderers are owned by the unified ecosystem packages, not Ash UI. Ash UI consumes these packages as dependencies.
### Runtime Authority (EXCLUSIVE)
diff --git a/specs/contracts/rendering_contract.md b/specs/contracts/rendering_contract.md
index 57166a5c..5dde6ffa 100644
--- a/specs/contracts/rendering_contract.md
+++ b/specs/contracts/rendering_contract.md
@@ -1,126 +1,188 @@
# Rendering Contract (REQ-RENDER-*)
-This contract defines the normative requirements for IUR → Output rendering in the Ash UI framework.
+This contract defines the normative requirements for IUR → unified_iur → Output rendering in the Ash UI framework.
## Purpose
-Defines the requirements for rendering Intermediate UI Representation (IUR) to target output formats (LiveView HEEx, static HTML, etc.).
+Defines the requirements for compiling Ash UI resources to canonical `unified_iur` format and delegating rendering to external unified renderer packages (live_ui, web_ui, desktop_ui).
## Control Plane
**Owner**: `AshUI.Rendering` (Rendering Control Plane)
-## Dependencies
+## External Dependencies
+
+- **unified_iur**: Canonical intermediate representation format (https://github.com/your-org/unified/tree/main/packages/unified_iur)
+- **live_ui**: LiveView renderer package (https://github.com/your-org/unified/tree/main/packages/live_ui)
+- **web_ui**: Static HTML renderer package (https://github.com/your-org/unified/tree/main/packages/web_ui)
+- **desktop_ui**: Desktop renderer package (https://github.com/your-org/unified/tree/main/packages/desktop_ui)
+
+## Internal Dependencies
- REQ-COMP-*: IUR definitions
- REQ-FRAMEWORK-*: Framework contracts
## Requirements
-### REQ-RENDER-001: Renderer Contract
+### REQ-RENDER-001: Canonical IUR Production
+
+Ash UI MUST produce canonical `unified_iur` format for consumption by external renderer packages.
-All renderers MUST implement the standard renderer contract.
+**IUR Adapter Contract**:
+```elixir
+defmodule AshUI.Rendering.IURAdapter do
+ @doc """
+ Converts Ash UI IUR to canonical unified_iur format.
+ """
+ @callback to_canonical(AshUI.Compilation.IUR.t()) :: {:ok, UnifiedIUR.Screen.t()} | {:error, term()}
+
+ @doc """
+ Validates that the IUR is compatible with the target renderer.
+ """
+ @callback compatible?(AshUI.Compilation.IUR.t(), atom()) :: boolean()
+end
+```
-**Renderer Behaviour**:
+**External Renderer Invocation**:
```elixir
-defbehaviour AshUI.Renderer do
- @callback render(IUR.t(), keyword()) :: {:ok, RendererOutput.t()} | {:error, term()}
- @callback can_render?(IUR.t()) :: boolean()
- @callback format() :atom()
+# Application selects renderer
+case renderer_type do
+ :liveview -> LiveUI.Renderer.render(canonical_iur, opts)
+ :html -> WebUI.Renderer.render(canonical_iur, opts)
+ :desktop -> DesktopUI.Renderer.render(canonical_iur, opts)
end
```
**Acceptance Criteria**:
-- AC-001: All renderers implement the behaviour
-- AC-002: Renderers declare their supported format
-- AC-003: Renderers validate input before rendering
-- AC-004: Renderers return structured output
+- AC-001: Ash UI produces canonical unified_iur format
+- AC-002: External renderer packages consume IUR without Ash dependencies
+- AC-003: IUR adapter validates compatibility before rendering
+- AC-004: Renderer packages are selected by application, not Ash UI
+
+### REQ-RENDER-002: LiveView Rendering (via live_ui)
+
+LiveView rendering is provided by the external `live_ui` package.
+
+**Package**: `live_ui` (https://github.com/your-org/unified/tree/main/packages/live_ui)
+
+**Requirements**:
+- Ash UI produces canonical IUR compatible with live_ui
+- live_ui accepts canonical IUR via `LiveUI.Renderer.render/2`
+- Output format is LiveView HEEx templates
+
+**Acceptance Criteria**:
+- AC-001: Ash UI IUR maps to valid canonical IUR for live_ui
+- AC-002: live_ui produces valid HEEx output
+- AC-003: Events are bound according to unified signal transport contract
+- AC-004: HTML is properly escaped by live_ui
+
+**Note**: See [live_ui IUR Renderer spec](https://github.com/your-org/unified/blob/main/.spec/specs/live_ui/iur_renderer.spec.md) for canonical IUR requirements.
-### REQ-RENDER-002: LiveView Rendering
+### REQ-RENDER-003: Static HTML Rendering (via web_ui)
-The LiveView renderer MUST produce valid HEEx templates.
+Static HTML rendering is provided by the external `web_ui` package.
-**Output Format**: HEEx (Phoenix LiveView Engine)
+**Package**: `web_ui` (https://github.com/your-org/unified/tree/main/packages/web_ui)
**Requirements**:
-- Valid HEEx syntax
-- LiveView event bindings
-- Reactive update support
-- Safe HTML escaping
+- Ash UI produces canonical IUR compatible with web_ui
+- web_ui accepts canonical IUR via `WebUI.Renderer.render/2`
+- Output format is static HTML5 with optional Elm client
**Acceptance Criteria**:
-- AC-001: Output is valid HEEx
-- AC-002: Events are bound to LiveView handlers
-- AC-003: Dynamic values use assigns
-- AC-004: HTML is properly escaped
+- AC-001: Ash UI IUR maps to valid canonical IUR for web_ui
+- AC-002: web_ui produces valid HTML5 output
+- AC-003: Document structure follows web_ui conventions
+- AC-004: Assets are properly referenced
-### REQ-RENDER-003: Static HTML Rendering
+**Note**: See [web_ui IUR Renderer spec](https://github.com/your-org/unified/blob/main/.spec/specs/web_ui/iur_renderer.spec.md) for canonical IUR requirements.
-The static renderer MUST produce standalone HTML documents.
+### REQ-RENDER-003B: Desktop Rendering (via desktop_ui)
-**Output Format**: HTML5
+Desktop rendering is provided by the external `desktop_ui` package.
+
+**Package**: `desktop_ui` (https://github.com/your-org/unified/tree/main/packages/desktop_ui)
**Requirements**:
-- Complete HTML document structure
-- Inline CSS/JS (or external links)
-- No dynamic dependencies
-- SEO-friendly markup
+- Ash UI produces canonical IUR compatible with desktop_ui
+- desktop_ui accepts canonical IUR via `DesktopUI.Renderer.render/2`
+- Output format is native desktop UI (SDL2-based)
**Acceptance Criteria**:
-- AC-001: Output is valid HTML5
-- AC-002: Document includes DOCTYPE
-- AC-003: No unescaped interpolation
-- AC-004: Meta tags are present
+- AC-001: Ash UI IUR maps to valid canonical IUR for desktop_ui
+- AC-002: desktop_ui produces native desktop widgets
+- AC-003: Event handling follows unified signal transport contract
+- AC-004: Platform-specific rendering is handled by desktop_ui
+
+**Note**: See [desktop_ui IUR Renderer spec](https://github.com/your-org/unified/blob/main/.spec/specs/desktop_ui/iur_renderer.spec.md) for canonical IUR requirements.
### REQ-RENDER-004: Component Rendering
-Renderers MUST support rendering individual UI components.
+External renderer packages MUST support rendering individual UI components from canonical IUR.
+
+**Requirements**:
+- Canonical IUR allows partial/component rendering
+- Renderer packages support fragment rendering
+- Component boundaries are preserved in IUR
**Acceptance Criteria**:
-- AC-001: Elements can be rendered independently
-- AC-002: Partial rendering updates are supported
-- AC-003: Component state is preserved
+- AC-001: Canonical IUR can represent individual components
+- AC-002: Renderer packages support fragment rendering
+- AC-003: Component state is preserved across renders
- AC-004: Component boundaries are respected
### REQ-RENDER-005: Data Binding Rendering
-Renderers MUST preserve data binding semantics.
+Data bindings must be translated to canonical IUR format and preserved by external renderer packages.
+
+**Requirements**:
+- Ash UI bindings map to canonical IUR signal references
+- Renderer packages preserve binding semantics via unified signal transport
**Acceptance Criteria**:
-- AC-001: Bindings are translated to output format equivalents
-- AC-002: Reactive bindings use LiveView bindings
+- AC-001: Ash UI bindings map to canonical IUR signal references
+- AC-002: Reactive bindings use unified signal transport (Jido.Signal)
- AC-003: Static bindings render current values
-- AC-004: Action bindings create event handlers
+- AC-004: Action bindings create event handlers per renderer package conventions
### REQ-RENDER-006: Error Handling
-Renderers MUST handle rendering errors gracefully.
+Ash UI and external renderer packages MUST handle rendering errors gracefully.
+
+**Requirements**:
+- Ash UI validates IUR before passing to external renderers
+- External renderers handle invalid canonical IUR gracefully
+- Error reporting spans both Ash UI and renderer package
**Acceptance Criteria**:
-- AC-001: Invalid IUR produces error output
-- AC-002: Error output includes error details
-- AC-003: Errors don't crash the renderer
-- AC-004: Error rendering can be configured
+- AC-001: Ash UI validates IUR before external renderer call
+- AC-002: External renderer errors are wrapped and reported
+- AC-003: Errors don't crash the application
+- AC-004: Error rendering can be configured per renderer package
### REQ-RENDER-007: Layout Support
-Renderers MUST support screen layouts.
+Ash UI layouts MUST map to canonical IUR and be supported by external renderer packages.
-**Layout Types**:
+**Layout Types** (canonical IUR):
- `:default` - Standard full-page layout
- `:bare` - No layout (component only)
- `:modal` - Modal dialog layout
- `:panel` - Side panel layout
+**Requirements**:
+- Ash UI layouts map to canonical IUR layout constructs
+- Renderer packages support canonical layout types
+
**Acceptance Criteria**:
-- AC-001: Layouts wrap screen content
-- AC-002: Layouts can be customized
-- AC-003: Nested layouts are supported
-- AC-004: Layout errors are surfaced
+- AC-001: Ash UI layouts map to canonical IUR layouts
+- AC-002: Renderer packages support canonical layout types
+- AC-003: Nested layouts are supported in canonical IUR
+- AC-004: Layout errors are surfaced from renderer packages
### REQ-RENDER-008: Asset Management
-Renderers MUST handle asset references correctly.
+Assets are handled by external renderer packages according to their platform conventions.
**Asset Types**:
- CSS stylesheets
@@ -128,170 +190,212 @@ Renderers MUST handle asset references correctly.
- Images
- Fonts
+**Requirements**:
+- Ash UI passes asset references in canonical IUR
+- Renderer packages resolve assets per platform conventions
+
**Acceptance Criteria**:
-- AC-001: Asset paths are resolved
-- AC-002: Asset digests are used (in production)
-- AC-003: Missing assets produce warnings
-- AC-004: Asset loading is configurable
+- AC-001: Asset references are included in canonical IUR
+- AC-002: Renderer packages resolve assets per platform
+- AC-003: Missing assets produce warnings from renderer
+- AC-004: Asset loading is configurable per renderer package
### REQ-RENDER-009: Accessibility
-Renderers MUST produce accessible markup.
+External renderer packages MUST produce accessible markup per platform conventions.
**Accessibility Features**:
- ARIA attributes
-- Semantic HTML elements
+- Semantic HTML elements (web_ui, live_ui)
- Keyboard navigation
- Screen reader support
+**Requirements**:
+- Canonical IUR includes accessibility metadata
+- Renderer packages apply accessibility attributes per platform
+
**Acceptance Criteria**:
-- AC-001: Interactive elements have focus indicators
-- AC-002: Forms have proper labels
-- AC-003: ARIA attributes are present where needed
-- AC-004: Semantic HTML is preferred
+- AC-001: Canonical IUR includes accessibility hints
+- AC-002: Renderer packages apply platform-specific accessibility
+- AC-003: Keyboard navigation works per platform conventions
+- AC-004: Screen reader support is provided by renderer packages
### REQ-RENDER-010: Performance
-Renderers MUST meet performance requirements.
+Ash UI and external renderer packages MUST meet performance requirements.
**Performance Targets**:
-- Initial render: < 100ms for simple screens
-- Update render: < 50ms for small changes
+- IUR generation: < 50ms for typical screens
+- Initial render: < 100ms for simple screens (renderer dependent)
+- Update render: < 50ms for small changes (renderer dependent)
- Memory: Linear growth with IUR size
+**Requirements**:
+- Ash UI optimizes IUR generation
+- Renderer packages optimize their rendering pipelines
+
**Acceptance Criteria**:
-- AC-001: Rendering completes within time limits
-- AC-002: Memory usage is bounded
-- AC-003: Large screens use progressive rendering
-- AC-004: Performance can be measured
+- AC-001: Ash UI generates IUR within time limits
+- AC-002: Renderer packages render within their published targets
+- AC-003: Memory usage is bounded
+- AC-004: Performance can be measured at both layers
### REQ-RENDER-011: Extensibility
-Renderers MUST support custom component rendering.
+Custom components are supported through canonical IUR and renderer package extension points.
**Extension Points**:
-- Custom element types
-- Custom layouts
-- Render hooks
-- Middleware
+- Custom element types in Ash UI resources
+- Canonical IUR custom widget support
+- Renderer package extension APIs
+
+**Requirements**:
+- Ash UI supports custom element definitions
+- Custom elements map to canonical IUR extensions
+- Renderer packages provide extension APIs
**Acceptance Criteria**:
-- AC-001: Custom components can be registered
-- AC-002: Custom components override defaults
-- AC-003: Render hooks can modify output
-- AC-004: Extensions are isolated
+- AC-001: Ash UI supports custom element types
+- AC-002: Custom elements map to canonical IUR
+- AC-003: Renderer packages support custom widgets via their APIs
+- AC-004: Extensions are isolated per package
### REQ-RENDER-012: Observability
-Renderers MUST emit telemetry events.
+Ash UI and external renderer packages MUST emit telemetry events.
**Event Types**:
-- Render started
-- Render completed
-- Render error
-- Cache hit/miss
+- IUR generation started/completed (Ash UI)
+- IUR → canonical IUR conversion (Ash UI)
+- Render started/completed (renderer packages)
+- Render error (both layers)
+
+**Requirements**:
+- Ash UI emits events for IUR generation and conversion
+- Renderer packages emit events for their rendering operations
+- Events follow unified telemetry schema
**Acceptance Criteria**:
-- AC-001: Events include IUR ID
-- AC-002: Events include renderer type
-- AC-003: Events include duration
+- AC-001: Ash UI events include screen/element ID
+- AC-002: Renderer package events include canonical IUR ID
+- AC-003: Events include duration at each layer
- AC-004: Events follow standard telemetry schema
## Rendering Pipeline
```mermaid
flowchart LR
- subgraph Input["Input"]
- IUR["IUR"]
- Context["Render Context"]
+ subgraph AshUI["Ash UI"]
+ Resource["Ash Resource"]
+ Compiler["Compile to IUR"]
+ Adapter["IUR → Canonical IUR"]
+ end
+
+ subgraph Unified["Unified Ecosystem"]
+ Canonical["Canonical IUR
(unified_iur)"]
end
- subgraph Renderer["Renderer"]
- Select["Select Renderer"]
- Validate["Validate IUR"]
- Process["Process Elements"]
- Bind["Apply Bindings"]
- Layout["Apply Layout"]
- Assets["Resolve Assets"]
+ subgraph Renderers["External Renderer Packages"]
+ Live["LiveUI.Renderer"]
+ Web["WebUI.Renderer"]
+ Desktop["DesktopUI.Renderer"]
end
- subgraph Output["Output"]
- Result["Rendered Output"]
+ subgraph Output["Platform Output"]
+ LV["LiveView HEEx"]
+ HTML["Static HTML + Elm"]
+ Native["Desktop UI"]
end
- IUR --> Select
- Context --> Select
- Select --> Validate
- Validate -->|Valid| Process
- Validate -->|Invalid| Error["Error Output"]
- Process --> Bind
- Bind --> Layout
- Layout --> Assets
- Assets --> Result
-
- classDef input fill:#e1f5fe,stroke:#01579b,stroke-width:2px
- classDef renderer fill:#fff3e0,stroke:#e65100,stroke-width:2px
+ Resource --> Compiler
+ Compiler --> Adapter
+ Adapter --> Canonical
+
+ Canonical -->|"Application selected"| Live
+ Canonical -->|"Application selected"| Web
+ Canonical -->|"Application selected"| Desktop
+
+ Live --> LV
+ Web --> HTML
+ Desktop --> Native
+
+ classDef ashui fill:#e1f5fe,stroke:#01579b,stroke-width:2px
+ classDef unified fill:#fff3e0,stroke:#e65100,stroke-width:2px
+ classDef renderer fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef output fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
- classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px
- class IUR,Context input
- class Select,Validate,Process,Bind,Layout,Assets renderer
- class Result output
- class Error error
+ class Resource,Compiler,Adapter ashui
+ class Canonical unified
+ class Live,Web,Desktop renderer
+ class LV,HTML,Native output
```
-## Renderer Registry
+## Renderer Integration
```mermaid
classDiagram
- class RendererRegistry {
- +Map renderers
- +register(name, renderer) :ok
- +unregister(name) :ok
- +get(name) Renderer
- +for_format(format) Renderer
+ class AshUIRegistry {
+ <>
+ +Map available_renderers
+ +list_available() list(atom)
+ +compatible?(iur, format) boolean
}
- class Renderer {
- <>
- +render(IUR, opts) Result
- +can_render?(IUR) boolean
- +format() atom
+ class IURAdapter {
+ <>
+ +to_canonical(ash_iur) unified_iur
+ +validate(canonical_iur) :ok | :error
}
- class LiveViewRenderer {
- +render(IUR, opts) HEEx
- +can_render?(IUR) boolean
+ class UnifiedIUR {
+ <>
+ +Screen struct
+ +Element struct
+ +Signal struct
+ }
+
+ class LiveUIRenderer {
+ <>
+ +render(canonical_iur, opts) HEEx
+format() :liveview
}
- class StaticRenderer {
- +render(IUR, opts) HTML
- +can_render?(IUR) boolean
+ class WebUIRenderer {
+ <>
+ +render(canonical_iur, opts) HTML
+format() :html
}
- RendererRegistry --> Renderer : manages
- Renderer <|-- LiveViewRenderer
- Renderer <|-- StaticRenderer
+ class DesktopUIRenderer {
+ <>
+ +render(canonical_iur, opts) NativeUI
+ +format() :desktop
+ }
+
+ AshUIRegistry --> IURAdapter : uses
+ IURAdapter --> UnifiedIUR : produces
+ LiveUIRenderer --> UnifiedIUR : consumes
+ WebUIRenderer --> UnifiedIUR : consumes
+ DesktopUIRenderer --> UnifiedIUR : consumes
```
## Traceability
-| Requirement | ADR | Component Spec | Scenarios |
-|---|---|---|---|
-| REQ-RENDER-001 | ADR-0011 | rendering/registry.md | SCN-401, SCN-402 |
-| REQ-RENDER-002 | ADR-0012 | rendering/liveview.md | SCN-403, SCN-404 |
-| REQ-RENDER-003 | ADR-0013 | rendering/static.md | SCN-405, SCN-406 |
-| REQ-RENDER-004 | - | rendering/component.md | SCN-407 |
-| REQ-RENDER-005 | - | rendering/binding.md | SCN-408, SCN-409 |
-| REQ-RENDER-006 | - | rendering/errors.md | SCN-410 |
-| REQ-RENDER-007 | - | rendering/layout.md | SCN-411, SCN-412 |
-| REQ-RENDER-008 | - | rendering/assets.md | SCN-413 |
-| REQ-RENDER-009 | ADR-0014 | rendering/a11y.md | SCN-414, SCN-415 |
-| REQ-RENDER-010 | ADR-0015 | rendering/performance.md | SCN-416 |
-| REQ-RENDER-011 | ADR-0016 | rendering/extensibility.md | SCN-417, SCN-418 |
-| REQ-RENDER-012 | - | observability_contract.md | SCN-419 |
+| Requirement | ADR | Ash UI Spec | External Package Spec | Scenarios |
+|---|---|---|---|---|
+| REQ-RENDER-001 | ADR-0011 | rendering/iur_adapter.md | unified_iur package spec | SCN-401, SCN-402 |
+| REQ-RENDER-002 | ADR-0012 | - | live_ui/iur_renderer.spec.md | SCN-403, SCN-404 |
+| REQ-RENDER-003 | ADR-0013 | - | web_ui/iur_renderer.spec.md | SCN-405, SCN-406 |
+| REQ-RENDER-003B | ADR-0013B | - | desktop_ui/iur_renderer.spec.md | SCN-405B, SCN-406B |
+| REQ-RENDER-004 | - | - | unified_iur/component spec | SCN-407 |
+| REQ-RENDER-005 | - | binding_contract.md | signal_transport.spec.md | SCN-408, SCN-409 |
+| REQ-RENDER-006 | - | rendering/errors.md | (per renderer) | SCN-410 |
+| REQ-RENDER-007 | - | - | unified_iur/layout spec | SCN-411, SCN-412 |
+| REQ-RENDER-008 | - | - | (per renderer) | SCN-413 |
+| REQ-RENDER-009 | ADR-0014 | - | (per renderer) | SCN-414, SCN-415 |
+| REQ-RENDER-010 | ADR-0015 | - | (per renderer) | SCN-416 |
+| REQ-RENDER-011 | ADR-0016 | extension_contract.md | (per renderer) | SCN-417, SCN-418 |
+| REQ-RENDER-012 | - | observability_contract.md | (per renderer) | SCN-419 |
## Conformance
@@ -299,6 +403,15 @@ See [conformance/spec_conformance_matrix.md](../conformance/spec_conformance_mat
## Related Specifications
+### Ash UI Specifications
- [topology.md](../topology.md)
- [compilation_contract.md](compilation_contract.md)
- [screen_contract.md](screen_contract.md)
+
+### Unified Ecosystem Specifications
+- [unified_iur package](https://github.com/your-org/unified/tree/main/packages/unified_iur)
+- [live_ui IUR Renderer](https://github.com/your-org/unified/blob/main/.spec/specs/live_ui/iur_renderer.spec.md)
+- [web_ui IUR Renderer](https://github.com/your-org/unified/blob/main/.spec/specs/web_ui/iur_renderer.spec.md)
+- [desktop_ui IUR Renderer](https://github.com/your-org/unified/blob/main/.spec/specs/desktop_ui/iur_renderer.spec.md)
+- [Signal Transport](https://github.com/your-org/unified/blob/main/.spec/specs/signal_transport.spec.md)
+- [Platform Runtimes](https://github.com/your-org/unified/blob/main/.spec/specs/platform_runtimes.spec.md)
diff --git a/specs/rendering/README.md b/specs/rendering/README.md
index e886bceb..84516987 100644
--- a/specs/rendering/README.md
+++ b/specs/rendering/README.md
@@ -1,76 +1,156 @@
# Ash UI Rendering
-This directory contains specifications for IUR → Output rendering.
+This directory contains specifications for Ash UI's integration with the unified renderer packages.
## Rendering Overview
-The rendering layer transforms Intermediate UI Representation (IUR) into target output formats for different platforms.
+Ash UI produces canonical `unified_iur` format which is consumed by external renderer packages from the unified ecosystem.
-## Renderers
+## Architecture
-### LiveView Renderer (rendering/liveview.md)
+```mermaid
+flowchart LR
+ Ash["Ash Resources
(UI.Element, UI.Screen)"]
+ IUR["Ash IUR"]
+ Adapter["IUR Adapter"]
+ Canonical["Canonical IUR
(unified_iur)"]
+
+ Live["live_ui"]
+ Web["web_ui"]
+ Desktop["desktop_ui"]
+
+ Ash --> IUR
+ IUR --> Adapter
+ Adapter --> Canonical
+
+ Canonical --> Live
+ Canonical --> Web
+ Canonical --> Desktop
+```
+
+## Unified Renderer Packages
+
+### live_ui
-Renders IUR to Phoenix LiveView HEEx templates.
+Renders canonical IUR to Phoenix LiveView HEEx templates.
+**Package**: https://github.com/your-org/unified/tree/main/packages/live_ui
+**Spec**: https://github.com/your-org/unified/blob/main/.spec/specs/live_ui/iur_renderer.spec.md
**Features**:
- Reactive bindings via LiveView assigns
-- Event handling via phx-* bindings
+- Event handling via unified signal transport
- Optimized patches for updates
-### Static Renderer (rendering/static.md)
+### web_ui
-Renders IUR to standalone HTML documents.
+Renders canonical IUR to static HTML with optional Elm client.
+**Package**: https://github.com/your-org/unified/tree/main/packages/web_ui
+**Spec**: https://github.com/your-org/unified/blob/main/.spec/specs/web_ui/iur_renderer.spec.md
**Features**:
- Complete HTML5 document structure
-- Inline CSS/JS or external links
+- Phoenix server-side rendering
+- Elm client-side runtime
- SEO-friendly markup
+### desktop_ui
+
+Renders canonical IUR to native desktop UI (SDL2-based).
+
+**Package**: https://github.com/your-org/unified/tree/main/packages/desktop_ui
+**Spec**: https://github.com/your-org/unified/blob/main/.spec/specs/desktop_ui/iur_renderer.spec.md
+**Features**:
+- Native desktop widgets
+- Cross-platform (Windows, macOS, Linux)
+- Unified signal transport for events
+
+## Ash UI Components
+
+### IUR Adapter (rendering/iur_adapter.md)
+
+Converts Ash UI IUR to canonical unified_iur format.
+
+**Features**:
+- Resource mapping to canonical IUR schema
+- Validation of IUR compatibility
+- Error handling and reporting
+
### Renderer Registry (rendering/registry.md)
-Manages renderer registration and selection.
+Manages available renderer packages and their capabilities.
**Features**:
-- Dynamic renderer registration
-- Format-based renderer selection
-- Renderer capability queries
+- Lists available renderer packages
+- Validates IUR compatibility with target renderer
+- Provides renderer selection guidance
## Rendering Pipeline
```mermaid
flowchart LR
- IUR["IUR"] --> Select["Select Renderer"]
- Context["Render Context"] --> Select
- Select --> Validate["Validate IUR"]
- Validate -->|Valid| Process["Process Elements"]
- Validate -->|Invalid| Error["Error Output"]
- Process --> Bind["Apply Bindings"]
- Bind --> Layout["Apply Layout"]
- Layout --> Assets["Resolve Assets"]
- Assets --> Output["Rendered Output"]
+ subgraph AshUI["Ash UI"]
+ Resource["Ash Resource"]
+ Compile["Compile to IUR"]
+ Adapt["Adapt to Canonical IUR"]
+ end
+
+ subgraph Unified["Unified Ecosystem"]
+ Canonical["Canonical IUR"]
+ end
+
+ subgraph Renderers["Renderer Packages"]
+ Live["LiveUI.Renderer"]
+ Web["WebUI.Renderer"]
+ Desktop["DesktopUI.Renderer"]
+ end
+
+ subgraph Output["Output"]
+ LV["LiveView HEEx"]
+ HTML["HTML + Elm"]
+ Native["Desktop UI"]
+ end
+
+ Resource --> Compile --> Adapt --> Canonical
+ Canonical --> Live --> LV
+ Canonical --> Web --> HTML
+ Canonical --> Desktop --> Native
```
-## Renderer Contract
+## Application Usage
+
+Applications select the renderer package to use:
```elixir
-defbehaviour AshUI.Renderer do
- @callback render(IUR.t(), keyword()) :: {:ok, RendererOutput.t()} | {:error, term()}
- @callback can_render?(IUR.t()) :: boolean()
- @callback format() :: atom()
- @callback supports_binding?(BindingRef.t()) :: boolean()
+# In mix.exs - add desired renderer
+def deps do
+ [
+ {:ash_ui, "~> 0.1"},
+ {:unified_iur, "~> 0.1"},
+ {:live_ui, "~> 0.1"} # for LiveView rendering
+ # or
+ {:web_ui, "~> 0.1"} # for static HTML rendering
+ # or
+ {:desktop_ui, "~> 0.1"} # for desktop rendering
+ ]
end
-```
-
-## Output Formats
-| Format | Renderer | Use Case |
-|---|---|---|
-| `:liveview` | LiveViewRenderer | Interactive web UI |
-| `:html` | StaticRenderer | Static pages, SEO |
-| `:json` | JSONRenderer | API responses |
-| `:native` | NativeRenderer | Native mobile (future) |
+# In your application
+iur = AshUI.Compilation.compile(screen)
+canonical_iur = AshUI.Rendering.IURAdapter.to_canonical(iur)
+LiveUI.Renderer.render(canonical_iur, [])
+```
## Related Specifications
+### Ash UI Specifications
- [rendering_contract.md](../contracts/rendering_contract.md)
- [compilation/](../compilation/) - IUR generation
+
+### Unified Ecosystem Specifications
+- [unified_iur](https://github.com/your-org/unified/tree/main/packages/unified_iur)
+- [live_ui](https://github.com/your-org/unified/tree/main/packages/live_ui)
+- [web_ui](https://github.com/your-org/unified/tree/main/packages/web_ui)
+- [desktop_ui](https://github.com/your-org/unified/tree/main/packages/desktop_ui)
+- [Ecosystem Architecture](https://github.com/your-org/unified/blob/main/.spec/specs/architecture.spec.md)
+- [Platform Runtimes](https://github.com/your-org/unified/blob/main/.spec/specs/platform_runtimes.spec.md)
+- [Signal Transport](https://github.com/your-org/unified/blob/main/.spec/specs/signal_transport.spec.md)
diff --git a/specs/topology.md b/specs/topology.md
index 6201a5c7..ea031e50 100644
--- a/specs/topology.md
+++ b/specs/topology.md
@@ -43,9 +43,10 @@ flowchart TB
end
subgraph Rendering["Rendering Control Plane"]
- LiveRenderer["live_ui Renderer"]
- WebRenderer["web_ui Renderer"]
- Presenter["Presenter Layer"]
+ LiveRenderer["live_ui Package
(LiveView Renderer)"]
+ WebRenderer["web_ui Package
(Static HTML Renderer)"]
+ DesktopRenderer["desktop_ui Package
(Desktop Renderer)"]
+ ExternalRegistry["External Renderer Registry"]
end
subgraph Extension["Extension Control Plane"]
@@ -73,7 +74,9 @@ flowchart TB
Validator --> UIScreen
Validator --> UIBinding
- Normalizer --> LiveRenderer
+ Normalizer -->|"Canonical IUR"| LiveRenderer
+ Normalizer -->|"Canonical IUR"| WebRenderer
+ Normalizer -->|"Canonical IUR"| DesktopRenderer
AshAPI --> UIElement
AshAPI --> UIScreen
@@ -85,10 +88,11 @@ flowchart TB
AshResources --> Database
- LiveRenderer --> Presenter
- WebRenderer --> Presenter
+ LiveRenderer -->|"Native Widgets"| WidgetRegistry
+ WebRenderer -->|"Native Widgets"| WidgetRegistry
+ DesktopRenderer -->|"Native Widgets"| WidgetRegistry
- Presenter --> WidgetRegistry
+ Normalizer -->|"IUR Format"| WidgetRegistry
WidgetRegistry --> PluginAdmission
@@ -101,7 +105,7 @@ flowchart TB
class UIElement,UIScreen,UIBinding,AshAPI framework
class ResourceCompiler,IURGenerator,Validator,Normalizer compilation
- class LiveRenderer,WebRenderer,Presenter rendering
+ class LiveRenderer,WebRenderer,DesktopRenderer,ExternalRegistry rendering
class LVSession,EventHandler,LifecycleMgr runtime
class WidgetRegistry,PluginAdmission extension
class AshResources,Database data
@@ -194,18 +198,23 @@ flowchart TB
**Owner**: `AshUI.Rendering`
-**Scope**: Output generation for target platforms
+**Scope**: Output generation for target platforms via external renderer packages
**Components**:
-- live_ui Renderer - LiveView-compatible rendering
-- web_ui Renderer - Static HTML rendering
-- Presenter Layer - Final presentation logic
+- **live_ui** - External package providing LiveView-compatible rendering via `LiveUI.Renderer.render/2`
+- **web_ui** - External package providing static HTML rendering via `WebUI.Renderer.render/2`
+- **desktop_ui** - External package providing desktop rendering via `DesktopUI.Renderer.render/2`
+- **Renderer Registry** - Manages renderer package selection and adapter registration
**Authority**:
-- Defines renderer contracts and interfaces
-- Controls presentation logic and formatting
-- Manages renderer selection and routing
-- Owns output format specifications
+- Compiles Ash Resources to canonical unified_iur format
+- Delegates rendering to external unified renderer packages
+- Manages renderer package selection and routing
+- Validates IUR compatibility with target renderers
+
+**External Dependencies**:
+- `unified_iur` - Canonical intermediate representation format
+- Renderer packages are consumed as dependencies, not owned by Ash UI
### Runtime Control Plane
@@ -251,15 +260,17 @@ sequenceDiagram
participant EH as Event Handler
participant RC as Resource Compiler
participant IUR as IUR Generator
- participant LR as live_ui Renderer
+ participant UI as unified_iur
+ participant LR as LiveUI.Renderer
participant AR as Ash Resources
B->>LV: WebSocket Connect
LV->>LV: Mount Screen
LV->>EH: Request Screen UI
EH->>RC: Compile UI.Screen
- RC->>IUR: Generate IUR
- IUR->>LR: Render to LiveView
+ RC->>IUR: Generate Ash IUR
+ IUR->>UI: Convert to Canonical IUR
+ UI->>LR: LiveUI.Renderer.render(iur)
LR->>LV: HEEx Template
LV->>B: Initial HTML
@@ -269,7 +280,8 @@ sequenceDiagram
AR-->>EH: Result
EH->>RC: Recompute UI
RC->>IUR: Generate Updated IUR
- IUR->>LV: Updated HEEx
+ IUR->>UI: Convert to Canonical IUR
+ UI->>LV: Updated HEEx
LV->>B: Patch Update
```
@@ -279,16 +291,19 @@ sequenceDiagram
sequenceDiagram
participant C as Client
participant SC as Static Controller
+ participant EH as Event Handler
participant RC as Resource Compiler
participant IUR as IUR Generator
- participant WR as web_ui Renderer
+ participant UI as unified_iur
+ participant WR as WebUI.Renderer
participant AR as Ash Resources
C->>SC: HTTP GET /screen
SC->>EH: Build Screen Request
EH->>RC: Compile UI.Screen
- RC->>IUR: Generate IUR
- IUR->>WR: Render to HTML
+ RC->>IUR: Generate Ash IUR
+ IUR->>UI: Convert to Canonical IUR
+ UI->>WR: WebUI.Renderer.render(iur)
WR->>SC: Static HTML
SC->>C: HTML Response
```
@@ -308,9 +323,8 @@ AshUI # Application root
│ ├── Validator # Schema Validator
│ └── Normalizer # Representation Normalizer
├── Rendering # Rendering Control Plane
-│ ├── LiveRenderer # LiveView Renderer
-│ ├── WebRenderer # Static HTML Renderer
-│ └── Presenter # Presentation Logic
+│ ├── IURAdapter # Ash IUR → unified_iur adapter
+│ └── RendererRegistry # External renderer package management
├── Runtime # Runtime Control Plane
│ ├── Session # Session Management
│ ├── EventHandler # Event Processing
@@ -332,8 +346,10 @@ AshUI # Application root
- Phoenix.Template (for HEEx)
### Rendering Dependencies
-- Phoenix.LiveView (LiveView mode)
-- Phoenix.Template (Static mode)
+- `unified_iur` - Canonical intermediate representation
+- `live_ui` - LiveView rendering (optional, application-provided)
+- `web_ui` - Static HTML rendering (optional, application-provided)
+- `desktop_ui` - Desktop rendering (optional, application-provided)
### Runtime Dependencies
- Phoenix.LiveView