a2ui-project / a2ui-project/a2ui

Implement Node Layer Architecture across Core and Renderers

Offen
#1,282 11 Kommentare 0 Reaktionen 1 zugewiesene Person Beansprucht von @andrewkolos Auf GitHub ansehen
component: angular renderer component: lit renderer component: react renderer P2 type: feature/enhancement
Vorherrschende Sprache
TypeScript
Sterne
16.4k
Forks
1.3k
Ø Merge
2 T. 13 Std.
Gemergte PRs (30 T.)
134

Beschreibung

This is a proposal based on a comment that @yjbanov made on the original design for web_core. After thinking about it a bit more and seeing how the libraries evolved, I think he was right!

### Description and Rationale
We are introducing a "Node Layer" architecture into the A2UI core libraries and refactoring the existing rendering frameworks (React, Angular, Lit) to consume it.

Currently, frameworks are responsible for complex tasks: traversing the flat `SurfaceModel` adjacency list, resolving data bindings using `GenericBinder`, handling template expansions (e.g., `ChildList`), and managing subscription lifecycles. This causes duplicated logic across frameworks, risks memory leaks due to forgotten binder disposals, and makes adding new framework adapters difficult.

The Node Layer centralizes this "business logic" into the core library. It transforms the flat component map into a living, reactive view hierarchy. Renderers will simply receive fully-resolved, type-safe `Node` instances and focus solely on mapping them to native UI primitives (pixels).

### Detailed Architecture & Design
The Node Layer represents a shift from frameworks directly managing components and data to having the `SurfaceModel` manage the entire view hierarchy lifecycle.

#### 1. Core Data Structures: The `Node`
The new architecture introduces a `Node` interface. A `Node` represents a living, fully resolved component instance in the view hierarchy. By utilizing generics, the `Node` interface provides type-safe property access to the rendering frameworks.
- **`instanceId`**: A stable, unique identifier for this instance in the rendered tree. For templated nodes, this incorporates the data path (e.g., `'item-card-[/users/0]'`) to ensure React/Flutter keys remain stable.
- **`props`**: A reactive `Signal`. Dynamic values are primitive strings/numbers/booleans, Actions are callable `() => void` closures, and Structural props (e.g., `child` or `children`) contain actual child `Node` references (not string IDs).
- **Lifecycle**: `onDestroyed` event and a `dispose()` method.

#### 2. Internal Node Engine inside SurfaceModel
The management of the Node tree is an implementation detail inside `SurfaceModel`. Because `SurfaceModel` encapsulates both the `DataModel` and the `SurfaceComponentsModel`, it is perfectly positioned to handle this.
- **Public API**: `SurfaceModel` exposes a single, reactive `rootNode: Signal`. Frameworks simply subscribe to this signal.
- **Internal Engine**: The `SurfaceModel` internally tracks `SurfaceComponentsModel` changes and `DataModel` mutations. It manages a private cache of active nodes, automatically instantiating and destroying child nodes as requested by layout containers.

#### 3. 1-to-N Mapping for Templates (ChildLists)
A primary responsibility of the internal Node engine is managing the 1-to-N mapping between a single `ComponentModel` and multiple rendered `Node` instances.
When an A2UI payload contains a `ChildList` acting as a template (e.g., a `Card` with ID `"user-card"` iterating over `/users`), it will be rendered multiple times.
- The internal engine handles subscribing to the `DataModel` array path (e.g., `/users`).
- If the array has 3 items, the engine spawns 3 unique `Node` instances for the same `componentId`.
- Each spawned node receives its own `instanceId`, scoped `dataPath`, and an isolated `GenericBinder` to resolve its properties.
- If the array length changes (e.g., a 4th user is added), the parent node detects the array mutation and spawns a 4th `Node`, emitting an updated `ChildList` prop containing the 4 nodes.
- When destroyed (e.g., a user is deleted), the Node fires `onDestroyed`, disposes its `GenericBinder`, and destroys its children.

#### 4. Soft Transition for Adapters
Framework adapters will expose `Node` objects for structural properties, but will maintain backward-compatible shims so component authors don't have to rewrite all their UI code immediately.
- **`buildChild` Overload**: The `buildChild` helper will accept `child: string | Node`. If it receives a `Node`, it renders it directly.
- **`Node.toString()`**: Implement `Node.prototype.toString()` to return its `componentId`, preventing breakage for logic using ID comparisons.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.