From e84c537f3f6024745d7db8d85307b906421a9fbf Mon Sep 17 00:00:00 2001 From: Pascal Charbonneau Date: Thu, 19 Mar 2026 05:10:09 -0600 Subject: [PATCH] Update Ash UI specs to integrate with unified-ui ecosystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update rendering_contract.md to delegate to external renderer packages (live_ui, web_ui, desktop_ui) - Update topology.md to show Ash UI → canonical IUR → unified renderers flow - Update control_plane_ownership_matrix.md to reflect delegated rendering authority - Update compilation_contract.md to reference canonical unified_iur format - Update guides to show simplified API (mount_ui_screen/3) - Add RFC-0002 proposing Ash UI as pure Ash Framework integration layer This positions Ash UI as an Ash-specific integration layer for the unified-ui ecosystem, focusing on storage, authorization, and data binding while delegating widgets, layouts, compilation, and rendering to the unified packages. --- README.md | 57 ++- .../DG-0001-architecture-overview.md | 96 +++- guides/user/UG-0001-getting-started.md | 54 ++- rfcs/RFC-0002-ash-ui-unified-integration.md | 390 ++++++++++++++++ rfcs/index.md | 8 +- specs/contracts/compilation_contract.md | 24 +- .../control_plane_ownership_matrix.md | 32 +- specs/contracts/rendering_contract.md | 421 +++++++++++------- specs/rendering/README.md | 154 +++++-- specs/topology.md | 72 +-- 10 files changed, 1019 insertions(+), 289 deletions(-) create mode 100644 rfcs/RFC-0002-ash-ui-unified-integration.md 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