Support for version 0.9 of the A2UI protocol in the Angular renderer#820
Support for version 0.9 of the A2UI protocol in the Angular renderer#820gspencergoog wants to merge 11 commits intogoogle:mainfrom
Conversation
Core Logic & Error Handling - **`renderers/web_core/src/v0_9/basic_catalog/functions/basic_functions.ts`** - Updated arithmetic (`add`, `subtract`, `multiply`, `divide`) and comparison (`equals`, `not_equals`, `greater_than`, `less_than`) functions to throw `A2uiExpressionError` when arguments are invalid or missing, replacing previous quiet failures (like returning `NaN`). - Added descriptive error messages to aid debugging. Components & Typings - **`renderers/web_core/src/v0_9/basic_catalog/components.ts`** **[NEW]** - Created a definition file summarizing TypeScript interfaces for standard components like `ButtonProps`, `TextProps`, `ImageProps`, `CardProps`, `RowProps`, `ColumnProps`, `ListNode`, and items. These form the building blocks for creating surfaces in the setup with properties.
- Fixed a2a-chat-canvas resolver to appropriately handle surface styles. - Fixed orchestrator middleware catalog pass-through to avoid stripping custom capabilities. - Fixed toolbar catalog sort order to prioritize Custom above Standard model. - Enabled provideMarkdownRenderer in app config for markdown-it. - Removed debug console trace logs from chart component views.
…tiply, greater_than, and less_than to match divide behavior
…ypes Simplifies component Node types (ButtonNode, ImageNode, etc.) by removing explicit property intersections that are already defined in their corresponding Props interfaces, aligning with the pattern used for TextNode.
There was a problem hiding this comment.
Code Review
This pull request introduces a significant upgrade to the Angular renderer, transitioning from A2UI protocol v0.8 to v0.9. This involves creating new versioned directories (v0_8 and v0_9) for components, catalogs, and related utilities, and updating the main Angular public API to expose the v0.9 implementation. Dependency updates include @a2ui/web_core and the addition of ajv packages. Key changes in v0.9 components include revised input bindings for layout components (Row, Column), updated property names (e.g., usageHint to variant for Text and Image, textFieldType to variant for TextField, selections to value for MultipleChoice, tabItems to tabs for Tabs, minValue/maxValue to min/max for Slider), and a new ChoicePicker component. The DynamicComponent base class in v0.9 now handles component property updates via subscriptions and uses a more robust DataContext for value resolution. The MessageProcessor is updated to handle v0.9 messages and dispatch client events. Additionally, the .gitignore file is updated to include coverage/. Review comments highlight a CSS naming issue in v0_9/components/surface.ts where --0 should be --p-0 for consistency and validity, and a potential bug in v0_9/components/text.ts where String(null) could lead to unintended rendering of the literal string "null" instead of an empty string, suggesting the use of the nullish coalescing operator.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
jacobsimionato
left a comment
There was a problem hiding this comment.
Hey I think the v0.9 code here still uses very v0.8 patterns.. I wonder if it's worth vibecoding a new v0.9 renderer from scratch, as an experiment. I tried it out for React here: #827
There was a problem hiding this comment.
This is defined in web_core - we should use it from there!
There was a problem hiding this comment.
This is a leftover file that isn't actually used. Removed.
| } | ||
|
|
||
| @Injectable({ providedIn: 'root' }) | ||
| export class MessageProcessor extends A2uiMessageProcessor<any> { |
There was a problem hiding this comment.
This should also be in web_core!
|
|
||
| readonly surfaceId = input<Types.SurfaceID>(); | ||
| readonly component = input.required<T>(); | ||
| readonly weight = input.required<string | number>(); |
There was a problem hiding this comment.
This is part of the basic catalog, not the spec, so it shouldn't be here
| private viewContainerRef = inject(ViewContainerRef); | ||
| private catalog = inject(CatalogToken); | ||
|
|
||
| private processor = inject(A2UI_PROCESSOR); |
There was a problem hiding this comment.
This should accept just SurfaceModel, not a2uiprocessor overall
| } | ||
| `, | ||
| }) | ||
| export class Button extends DynamicComponent<Types.ButtonNode> { |
There was a problem hiding this comment.
We want to stop depending on these types and try to completely separate the component implementations from the core renderer codebase.
Sure, I can give that a try. This started from the 0.8 source, which is probably why it's using those patterns. I've been trying to cure it of some of the patterns (treating the basic_catalog as separate, making things use web_core), but not entirely successful, as you see. |
|
|
||
| @Component({ | ||
| selector: 'a2ui-audio', | ||
| changeDetection: ChangeDetectionStrategy.Eager, |
There was a problem hiding this comment.
Why not make the components OnPush?
| import { MessageProcessor, A2uiClientMessage } from '../data'; | ||
| import { ComponentModel } from '@a2ui/web_core/v0_9'; | ||
|
|
||
| let idCounter = 0; |
There was a problem hiding this comment.
Can this just be a static private member of the class?
| }) | ||
| export abstract class DynamicComponent<T extends Types.AnyComponentNode = Types.AnyComponentNode> implements OnInit, OnDestroy { | ||
| protected readonly id = `a2ui-${idCounter++}`; | ||
| protected processor = inject(A2UI_PROCESSOR) as MessageProcessor; |
There was a problem hiding this comment.
Can this be readonly ? (Same for cdr.)
| export type LinkNode = Component<LinkProps>; | ||
| export type Link = LinkNode; | ||
|
|
||
| export type CardNode = import('@a2ui/web_core/v0_9/basic_catalog').CardNode; |
There was a problem hiding this comment.
Should this be in the list abvoe?
| observe<T>(path: string): Observable<T> { | ||
| const resolvedPath = this.resolvePath(path); | ||
| return new Observable<T>((subscriber) => { | ||
| // Emit initial value |
There was a problem hiding this comment.
Can we update the comment to explain why we're eating the initial value?
| subscriber.next(this.model.get(resolvedPath)); | ||
|
|
||
| const subscription = this.model.subscribe<T>(resolvedPath, (value) => { | ||
| if (value !== undefined) { |
There was a problem hiding this comment.
Does this mean that if there is a value at the path, and then it gets updated to be undefined, the subscribers won't know the value has been changed?
| // The base class processMessages expects v0.9 messages. | ||
| // We trust the server sends valid v0.9 messages. | ||
| override processMessages(messages: any[]): void { | ||
| console.log('[MessageProcessor] Received messages to process:', messages); |
There was a problem hiding this comment.
Should these console.log lines be removed?
| expect(component).toBeTruthy(); | ||
| }); | ||
|
|
||
| // Tests for resolve/data binding would need to be checked against how DynamicComponent uses them. |
There was a problem hiding this comment.
These look like LLM-generated comments?
Should we implement additional tests?
| }, | ||
| }) | ||
| export abstract class DynamicComponent<T extends Types.AnyComponentNode = Types.AnyComponentNode> implements OnInit, OnDestroy { | ||
| protected readonly id = `a2ui-${idCounter++}`; |
There was a problem hiding this comment.
Should we use getUniqueId() here?
Or should we inject the IdGenerator and use that here and replace the getUniqueId with that?
|
Sorry, this PR is clearly not ready. I was excited that I got both v0.8 and v0.9 rendering working, but was premature in sending it out. Sorry to waste your time with the reviews. I'll try a clean implementation and see if I can get closer to something correct: I think this one suffers a lot from iteration creep and cruft where I started off with porting the v0.8 implementation to a pure v0.9 implementation, then moved a bunch of that into |
Description
Overview
This Pull Request introduces initial support for version 0.9 of the A2UI protocol in the Angular renderer and provides updates/strict validations in
web_core. This is the first part of a two-part change, with the second part following in theangular_rendererbranch. It leaves the samples using v0.8, and a later change will convert them to use 0.9.There some minimal import changes necessary, which is a breaking change, since v0.9 is now the default, and 0.8 needs to be explicitly imported.
Key Changes
Angular Renderer (
renderers/angular)src/lib/v0_8/to make room for future proofing.v0_9/directory containing:audio,button,card,checkbox,choice-picker,column,divider,icon,image,list,modal,slider,surface,tabs,text,text-field,video).dynamic-component,renderer, andid-generatornode traversal strategies.styles.tscontaining ported CSS utility tokens for layout, palette-mapping, and response behaviors.data-context.ts) and markdown parsing hooks.public-api.tsnow defaults to exporting the v0.9 pipeline set.Web Core Maintenance (
renderers/web_core)BASIC_FUNCTIONS(arithmetic, logical operators, string lookups, validations) to explicitly throwA2uiExpressionErroron missing or malformed arguments, preventing undefined fallback behavior passes.Samples & Configurations (
samples/client/angular)orchestrator,gallery,rizzcharts,contact) to consume the v0.9 renderer exports safely.