Repository navigation
feat(a2ui_flutter): add the v0.9 basic catalog - #3088
Conversation
A2uiSurface renders a SurfaceModel through a NodeResolver, and NodeView renders each ComponentNode through its catalog entry, rebuilding when the node's props change. ComponentImplementation pairs a component schema with a widget builder, WidgetCatalog is a catalog of them, and ComponentProps reads a node's resolved properties, bindings and actions. A component that has not arrived shows a small progress indicator.
BasicCatalog.v0_9Api() returns the published v0.9 basic catalog document parsed with Catalog.fromJson: its components, function signatures and theme schema, so a framework adapter can read the component schemas without embedding its own copy of the document.
There was a problem hiding this comment.
Code Review
This pull request implements the A2UI v0.9 basic catalog in Flutter using Material widgets, introducing a comprehensive set of basic components (such as Button, CheckBox, ChoicePicker, DateTimeInput, Modal, and Video) along with their respective tests and documentation. The code review identified several important issues to address: a compilation error in ChoicePicker due to missing required parameters in RadioListTile, a potential crash in MediaPlayer when disposing of the controller synchronously, unhandled key events in Modal, a missing empty check before accessing _opened.last, and the need for more robust exception handling when parsing SVG paths in Icon.
package:a2ui_flutter/basic_catalog.dart implements the 18 components of the v0.9 basic catalog with Material widgets, with their schemas from a2ui_core's BasicCatalog.v0_9Api() and the functions of BasicCatalog.v0_9(). basicCatalog() builds the whole catalog under the basic catalog's id; BasicComponents holds each component for a catalog of the app's own. The components apply the surface theme's primaryColor as the primary color, and every specification example renders in a test.
5d9cb3e to
aa0ad11
Compare
| /// returns whether the data model accepted it, or null when the payload gave | ||
| /// [key] no path. A write the data model rejects is reported on the | ||
| /// surface's `onError` with the rejection's code, message and path. | ||
| bool Function(Object? value)? writerFor( |
There was a problem hiding this comment.
I wonder if this should be within the core or framework adapter layers, rather than in the catalog? I guess different catalogs might want different error handling? But our SDKs should provide the most obvious approaches out of the box.
| themeSchema: BasicComponents.api.themeSchema, | ||
| schemaId: BasicComponents.api.schemaId, | ||
| title: BasicComponents.api.title, | ||
| description: BasicComponents.api.description, |
There was a problem hiding this comment.
Should we also forward protocolVersion and instructions from BasicComponents.api here to match BasicCatalog.v0_9() in a2ui_core?
|
I ran the cases I fixed in genui against this catalog. Two didn't carry over: a component's Also, sliders over a whole range snap to integers, but v0.9.1 has no steps (#2737). Is that on purpose? |
…r-core # Conflicts: # blueprints/modules/a2ui_framework_adapter.blueprint.md
A component whose catalogId changes is recreated with the same instance id, like one whose type changes. Keying the subtree by the type name kept the old implementation's state when only the catalog changed. Keying by the resolved implementation resets it in both cases.
Pairs it with CatalogApi in a2ui_core, as ComponentImplementation and FunctionImplementation pair with ComponentApi and FunctionApi. 🤖 Agent-generated
…sic-catalog 🤖 Agent-generated
…iption NodeView wraps every resolved component in a Semantics that carries the component's accessibility label as its label and its description as its hint, so basic and custom components both get them. Without either attribute the Semantics adds no node, and it stays in the tree so that adding one later keeps the component's state. 🤖 Agent-generated
…sic-catalog 🤖 Agent-generated
|
Thanks for adding effca52, the label and description come through now. The README still lists Once this merges I'll send the Slider label fix, plus a small one for Image: when it fails to load, the broken-image icon drops the description, so a screen reader gets nothing. I've also been running genui_gen's generated components on this branch (bindings, write-back, checks, templates) and they all work. |
#3194 renamed A2uiClientAction and A2uiClientError to A2uiRendererAction and A2uiRendererError, MessageProcessor.groupModel to model and getClientDataModel to getRendererDataModel, and replaced EventSource.addListener with subscribe. The adapter's tests and README still used the old names, so the package's tests no longer compiled on main.⚠️ Agent-generated
…r-basic-catalog Main now has the adapter core (#3084, squash-merged) and a2ui_core's generated basic catalogs (#2998, #3129) and renderer renames (#3194). - Drop this branch's a2ui_core change, BasicCatalog.v0_9Api(): main's BasicCatalog.v0_9() now carries the components, so the basic catalog reads its schemas from there. - Pass the catalog's shared definitions along with its components. The generated component schemas reference #/$defs/CatalogComponentCommon, which a catalog without them cannot resolve. The README's custom catalog sample needs the same line. - Adopt the #3194 renames in write_back.dart, the test harness and the README. - Read Icon.name from the allOf envelope in icon_test.⚠️ Agent-generated
…pter-basic-catalog⚠️ Agent-generated
#3205 standardized LICENSE files on http://www.apache.org/licenses/ and made the license check enforce it. a2ui_flutter's LICENSE, added in #3084, used https, so the static-checks job fails on main.⚠️ Agent-generated
…pter-basic-catalog⚠️ Agent-generated
…bspec The package now depends on a2ui_core ^0.3.0 (#3192), but the install sample still showed ^0.2.2.⚠️ Agent-generated
…list NodeView applies a component's accessibility label and description, the two attributes v0.9 defines, since effca52.⚠️ Agent-generated
Thanks for pointing this out :)
Both fixes would be welcome. Feel free to tag me on those if you do make them.
Yes. A range that is a whole number from 2 to 1000 moves in steps of 1, so the common 0 to 100 slider writes integers instead of values like |
genui is being redesigned as a2ui_flutter, and this moves the runtime across. a2ui_core resolves a component against its schema before the builder runs, so the helpers generated code needed to bind values, run actions, build children and evaluate checks on genui are gone. What is left is what a2ui_core does not do: - `GenUiSchemas`, the shapes a2ui_core resolves by, with each property's own description, plus `accessibility` and `weight` on every component and no other property, as on a basic component. - `genUiWriter`, which writes through the binding of a property and reports a write the data model rejects on the surface, and is null for a literal, which a basic control also leaves read-only. - `genUiMissingIn`, which reports a required field missing from bound data once per node, as a validation failure naming its path. - `GenUiClientFunction` on `FunctionImplementation`, whose async and streaming forms keep one answer per set of arguments, so a2ui_core's re-evaluation does not start the call over. a2ui_flutter and the a2ui_core it needs are not published yet, so the workspace overrides both with a checkout of a2ui-project/a2ui#3088. The inspector, the tracing and the testing tools (fuzz, coverage, example surfaces, surface graph) still use genui and do not build yet.
Adds the v0.9 basic catalog to
a2ui_flutter.Stuff in this PR
package:a2ui_flutter/basic_catalog.dart: all 18 components, built from Material widgets, with the functions froma2ui_core'sBasicCatalog.v0_9. UsebasicCatalog()as is, or putBasicComponentsand your own components in a catalog of your own.primaryColoras the Material theme's primary color, and keep the app's other colors.openUrlfails unless the app passes a callback. A boundopenUrlcall runs when it resolves, not only on a tap, so pass one only for an agent you trust.A2uiMarkdown: wrap a surface in it to render Text as Markdown with a package of your choice.a2ui_coreaddsBasicCatalog.v0_9Api(), which returns the v0.9 basic catalog document the core already embeds. The components read their schemas from it instead of copying them.Of the 8.5k added lines,
lib/is ~2.5k and the tests ~5.8k.Design notes
OverlayPortalrather thanshowDialog, so its content stays under the surface's inherited widgets and closes when the Modal goes away.Known differences from the blueprint
onError. Section 2 keeps the basic catalog apart from the surface, but both need the surface model, and they reach it through the ambient context the blueprint has the surface publish, as web_core's basic components do. They import neither the surface widget nor the node view.Known gaps
accessibilityattributes are not applied yet.LayoutBuilder, soIntrinsicWidthandIntrinsicHeightcannot measure them. In anAlertDialog, give the surface a fixed width or use aDialog.video_player, so every app that uses it links that plugin. We made the decision to include this consciously in the past, but it's worth revisiting IMO.minormaxon DateTimeInput is rejected: the schema tells date, time and date-time apart only byformat, whicha2ui_core's validator does not check.Pre-launch Checklist
One time:
For this PR:
If my branch is on a fork, I have verified that scripts/e2e_test.sh passes.