Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d9fdb28
refactor: remove minimal catalog from renderers, keep in spec, and ad…
josemontespg Jun 5, 2026
f7e75e9
Merge remote-tracking branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 8, 2026
9e8ecbc
Merge remote-tracking branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 8, 2026
69feee4
Merge branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 8, 2026
b859e57
build: update web_core to use v1_0 spec instead of v0_10
josemontespg Jun 8, 2026
ad7e0c1
fix(spec): update outdated v0_10 references to v1_0 in v1_0 basic cat…
josemontespg Jun 8, 2026
20ac27e
Merge remote-tracking branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 9, 2026
a2402ec
Merge remote-tracking branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 9, 2026
4e87f03
fix(spec): fix capitalize function definition in v1_0 basic catalog
josemontespg Jun 9, 2026
1e56cf5
fix(spec): remove returnType from capitalize function call in v1_0 ex…
josemontespg Jun 9, 2026
4d29601
Merge branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 15, 2026
001458c
refactor: remove minimal catalog from v0.9.1 and v1.0 specification
josemontespg Jun 15, 2026
378b037
Fix residual minimal catalog references and revert basic catalog changes
josemontespg Jun 15, 2026
b7eff2c
refactor: delete invalid v0.9 basic capitalized-text example
josemontespg Jun 15, 2026
891542e
Remove capitalize function from basic catalog and refactor examples t…
josemontespg Jun 17, 2026
fc2faef
Merge branch 'upstream/main' into delete-minimal-catalog
josemontespg Jun 17, 2026
1838d59
Fix invalid text component variant h2 to body in formatted-text examples
josemontespg Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions renderers/angular/a2ui_explorer/src/app/demo-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
*/

import {Injectable} from '@angular/core';
import {z} from 'zod';
import {BasicCatalogBase, BASIC_FUNCTIONS} from '@a2ui/angular/v0_9';
import {customSliderComponentDeclaration} from './custom-slider.component';
import {createFunctionImplementation, FunctionImplementation} from '@a2ui/web_core/v0_9';

/**
* A catalog specific to the demo, extending the basic catalog with custom components.
Expand All @@ -28,26 +26,11 @@ import {createFunctionImplementation, FunctionImplementation} from '@a2ui/web_co
})
export class DemoCatalog extends BasicCatalogBase {
constructor() {
const capitalizeImplementation: FunctionImplementation = createFunctionImplementation(
{
name: 'capitalize',
returnType: 'string',
schema: z.object({value: z.string().optional()}) as any,
},
args => {
const value = String(args.value || '');
return value.charAt(0).toUpperCase() + value.slice(1);
},
);

// Unify functions from both core and angular libraries, plus local demo functions
const functions = [...BASIC_FUNCTIONS, capitalizeImplementation];

super({
id: 'https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json',
components: {},
extraComponents: [customSliderComponentDeclaration],
functions,
functions: BASIC_FUNCTIONS,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

import {Version, getCanvas, loadExample} from '../utils/test_utils';

describe('Example: Complex Layout (minimal) (v0.8)', () => {
describe('Example: Complex Layout (v0.8)', () => {
let textContent: string;

beforeEach(async () => {
await loadExample('Complex Layout (minimal)', Version.V0_8);
await loadExample('Complex Layout', Version.V0_8);
textContent = getCanvas().textContent;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import {ComponentFixture} from '@angular/core/testing';
import {DemoComponent} from '../../demo.component';
import {Version, getCanvas, loadExample, wait} from '../utils/test_utils';

describe('Example: Interactive Button (minimal) (v0.8)', () => {
describe('Example: Interactive Button (v0.8)', () => {
let textContent: string;
let fixture: ComponentFixture<DemoComponent>;

beforeEach(async () => {
fixture = await loadExample('Interactive Button (minimal)', Version.V0_8);
fixture = await loadExample('Interactive Button', Version.V0_8);
textContent = getCanvas().textContent;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

import {Version, getCanvas, loadExample} from '../utils/test_utils';

describe('Example: Row Layout (minimal) (v0.8)', () => {
describe('Example: Row Layout (v0.8)', () => {
let textContent: string;

beforeEach(async () => {
await loadExample('Row Layout (minimal)', Version.V0_8);
await loadExample('Row Layout', Version.V0_8);
textContent = getCanvas().textContent;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import {ComponentFixture} from '@angular/core/testing';
import {DemoComponent} from '../../demo.component';
import {Version, getCanvas, loadExample, wait} from '../utils/test_utils';

describe('Example: Login Form (minimal) (v0.8)', () => {
describe('Example: Simple Login Form (v0.8)', () => {
let textContent: string;
let fixture: ComponentFixture<DemoComponent>;

beforeEach(async () => {
fixture = await loadExample('Login Form (minimal)', Version.V0_8);
fixture = await loadExample('Simple Login Form', Version.V0_8);
textContent = getCanvas().textContent;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

import {Version, getCanvas, loadExample} from '../utils/test_utils';

describe('Example: Simple Text (minimal) (v0.8)', () => {
describe('Example: Simple Text (v0.8)', () => {
let textContent: string;

beforeEach(async () => {
await loadExample('Simple Text (minimal)', Version.V0_8);
await loadExample('Simple Text', Version.V0_8);
textContent = getCanvas().textContent;
});

Expand Down
46 changes: 4 additions & 42 deletions renderers/angular/scripts/generate-examples.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const DEFAULT_OUT_FILE = 'a2ui_explorer/src/app/generated/examples-bundle.ts';
/**
* The default catalogs to generate examples for if none are specified.
*/
const DEFAULT_CATALOGS = ['minimal', 'basic'];
const DEFAULT_CATALOGS = ['basic'];

/**
* The options that this script accepts.
Expand All @@ -35,7 +35,6 @@ const options = {
help: {type: 'boolean', short: 'h'},
'out-file': {type: 'string', short: 'o', default: DEFAULT_OUT_FILE},
catalog: {type: 'string', short: 'c', multiple: true, default: DEFAULT_CATALOGS},
'override-minimal-catalog-id': {type: 'boolean', default: true},
};

/**
Expand All @@ -46,35 +45,13 @@ const HELP_MESSAGE = `Usage: node generate-examples.mjs [options]
Options:
-o, --out-file <path> Output file path (default: ${DEFAULT_OUT_FILE})
-c, --catalog <name> Catalog names to include (can be specified multiple times) (default: ${DEFAULT_CATALOGS.join(', ')})
--no-override-minimal-catalog-id Do not override catalog ID for minimal catalog
Comment thread
josemontespg marked this conversation as resolved.
-h, --help Show this help message
`;

/**
* Overrides the catalog ID for minimal catalog to use basic catalog instead,
* preserving the version in the path.
*/
function overrideMessagesCatalogId(messages) {
const overrideCatalogId = catalogId => {
return catalogId.replace('catalogs/minimal/catalog.json', 'catalogs/basic/catalog.json');
};
for (const msg of messages) {
if (msg.createSurface && msg.createSurface.catalogId) {
// For v0.9 (and up?)
msg.createSurface.catalogId = overrideCatalogId(msg.createSurface.catalogId);
}
// The minimal catalog examples in 0.8 contain a catalogId (but not the basic
// catalog ones). That's probably copy-pasta from when catalogIds were
// introduced later, as the v0.8 renderers didn't use catalogIds. We don't
// need to handle the overrides of the catalogId for the beginRendering
// messages from the v0.8 spec.
}
}

/**
* Reads examples for a given version and catalogs.
*/
function readExamples(specPath, catalogs, overrideCatalogId, version) {
function readExamples(specPath, catalogs, version) {
Comment thread
josemontespg marked this conversation as resolved.
const examples = [];

for (const catalog of catalogs) {
Expand Down Expand Up @@ -117,10 +94,6 @@ function readExamples(specPath, catalogs, overrideCatalogId, version) {
};
}

if (catalog === 'minimal' && overrideCatalogId) {
overrideMessagesCatalogId(example.messages);
}

examples.push(example);
} catch (e) {
throw new Error(`Error parsing ${filePath}`, {cause: e});
Expand All @@ -145,26 +118,15 @@ async function main() {

const outPath = values['out-file'];
const outDir = path.dirname(outPath);
const overrideCatalogId = values['override-minimal-catalog-id'];

if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, {recursive: true});
}

const catalogs = values.catalog;

const examplesV08 = readExamples(
'../../specification/v0_8/json/catalogs',
catalogs,
overrideCatalogId,
'0.8',
);
const examplesV09 = readExamples(
'../../specification/v0_9/catalogs',
catalogs,
overrideCatalogId,
'0.9',
);
const examplesV08 = readExamples('../../specification/v0_8/json/catalogs', catalogs, '0.8');
const examplesV09 = readExamples('../../specification/v0_9_1/catalogs', catalogs, '0.9');

// Generate the file now!
const tsContent = `/**
Expand Down
2 changes: 1 addition & 1 deletion renderers/angular/src/v0_9/catalog/basic/basic-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export const BASIC_CATALOG_OPTIONS = new InjectionToken<BasicCatalogOptions>(
* A basic catalog of components and functions for v0.9 verification.
*
* This catalog includes a wide range of UI components (Text, Button, Row, etc.)
* and utility functions (capitalize, formatString) defined in the A2UI v0.9
* and utility functions (formatString) defined in the A2UI v0.9
* basic catalog specification.
*/
@Injectable({
Expand Down
9 changes: 4 additions & 5 deletions renderers/lit/a2ui_explorer/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# A2UI Local Gallery (Minimal v0.8)
# A2UI Local Gallery (Basic v0.9)

This is a standalone, agentless web application designed to render the A2UI v0.8 minimal examples directly from static JSON files. It serves as a focused environment for testing renderer subset compatibility and protocol compliance.
This is a standalone, agentless web application designed to render the A2UI v0.9 basic examples directly from static JSON files. It serves as a focused environment for testing renderer compatibility and protocol compliance.

## Prerequisites

Expand Down Expand Up @@ -34,13 +34,12 @@ For more details on building the renderers, see:
yarn dev
```
This command will:
- Sync all JSON examples from `specification/v0_8/json/catalogs/minimal/examples/`.
- Generate a manifest file (`index.json`) for dynamic discovery.
- Load all JSON examples from `specification/v0_9/catalogs/basic/examples/`.
- Start the Vite server at `http://localhost:5173`.

## Architecture

- **Agentless**: Unlike other samples, this does not require a running Python agent. It simulates agent responses locally for interactive components (like the Login Form).
- **Dynamic Loading**: The app automatically discovers and loads _all_ `.json` files present in the v0.8 minimal specification folder at build time. To add a new test case, simply drop a JSON file into that specification folder and restart the dev server.
- **Dynamic Loading**: The app automatically discovers and loads _all_ `.json` files present in the v0.9 basic specification folder at build time. To add a new test case, simply drop a JSON file into that specification folder and restart the dev server.
- **Surface Isolation**: Each example is rendered into its own independent `a2ui-surface` with a unique ID derived from the filename.
- **Mock Agent Console**: All user interactions (button clicks, form submissions) are intercepted and logged to a sidebar, demonstrating how the renderer resolves actions and contexts.
4 changes: 2 additions & 2 deletions renderers/lit/a2ui_explorer/scripts/generate-examples.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import path from 'path';

const SPEC_EXAMPLES_DIR = path.resolve(
import.meta.dirname,
'../../../../specification/v0_9/catalogs/basic/examples',
'../../../../specification/v0_9_1/catalogs/basic/examples',
);
const OUT_FILE = path.resolve(import.meta.dirname, '../src/generated/examples-list.ts');

Expand Down Expand Up @@ -48,7 +48,7 @@ function generateExamplesBundle() {

files.forEach((file, index) => {
// Relative path from src/generated/examples-list.ts to the specification examples folder
const relativePath = `../../../../../specification/v0_9/catalogs/basic/examples/${file}`;
const relativePath = `../../../../../specification/v0_9_1/catalogs/basic/examples/${file}`;
const variableName = `example_${index}`;

imports.push(`import ${variableName} from '${relativePath}';`);
Expand Down
6 changes: 3 additions & 3 deletions renderers/lit/src/v0_9/tests/basic-catalog-examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('v0.9 Basic Catalog Examples', () => {
const rootNode = surface.componentsModel.get('root');
assert.ok(rootNode, 'Surface should have a root component');

if (file.includes('capitalized_text')) {
if (file.includes('formatted-text')) {
const textNode = surface.componentsModel.get('result_text');
assert.ok(textNode);

Expand All @@ -75,13 +75,13 @@ describe('v0.9 Basic Catalog Examples', () => {

// Wait a microtask to let initial resolution finish
await new Promise(r => setTimeout(r, 0));
assert.strictEqual(binder.snapshot.text, '');
assert.strictEqual(binder.snapshot.text, 'You typed: ');

// Set value in data model
surface.dataModel.set('/inputValue', 'hello world');

await new Promise(r => setTimeout(r, 0));
assert.strictEqual(binder.snapshot.text, 'Hello world');
assert.strictEqual(binder.snapshot.text, 'You typed: hello world');

sub.unsubscribe();
binder.dispose();
Expand Down
2 changes: 1 addition & 1 deletion renderers/react/a2ui_explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"generate-examples": {
"command": "node scripts/generate-examples.mjs",
"files": [
"../../../specification/v0_9/catalogs/basic/examples/*.json",
"../../../specification/v0_9_1/catalogs/basic/examples/*.json",
"scripts/generate-examples.mjs"
],
"output": [
Expand Down
4 changes: 2 additions & 2 deletions renderers/react/a2ui_explorer/scripts/generate-examples.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import path from 'path';

const SPEC_EXAMPLES_DIR = path.resolve(
import.meta.dirname,
'../../../../specification/v0_9/catalogs/basic/examples',
'../../../../specification/v0_9_1/catalogs/basic/examples',
);
const OUT_FILE = path.resolve(import.meta.dirname, '../src/generated/examples-list.ts');

Expand Down Expand Up @@ -48,7 +48,7 @@ function generateExamplesBundle() {

files.forEach((file, index) => {
// Relative path from src/generated/examples-list.ts to the specification examples folder
const relativePath = `../../../../../specification/v0_9/catalogs/basic/examples/${file}`;
const relativePath = `../../../../../specification/v0_9_1/catalogs/basic/examples/${file}`;
const variableName = `example_${index}`;

imports.push(`import ${variableName} from '${relativePath}';`);
Expand Down
Comment thread
josemontespg marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[
{
"surfaceUpdate": {
"surfaceId": "gallery-complex-layout",
"components": [
{
"id": "root",
"component": {
"Column": {
"children": {
"explicitList": ["header", "form_row", "footer"]
},
"distribution": "spaceBetween",
"alignment": "stretch"
}
}
},
{
"id": "header",
"component": {
"Text": {
"text": {
"literalString": "User Profile Form"
},
"usageHint": "h1"
}
}
},
{
"id": "form_row",
"component": {
"Row": {
"children": {
"explicitList": ["first_name", "last_name"]
},
"distribution": "start",
"alignment": "start"
}
}
},
{
"id": "first_name",
"weight": 1,
"component": {
"TextField": {
"label": {
"literalString": "First Name"
},
"text": {
"path": "/firstName"
}
}
}
},
{
"id": "last_name",
"weight": 1,
"component": {
"TextField": {
"label": {
"literalString": "Last Name"
},
"text": {
"path": "/lastName"
}
}
}
},
{
"id": "footer",
"component": {
"Text": {
"text": {
"literalString": "Please fill out all fields."
},
"usageHint": "caption"
}
}
}
]
}
},
{
"beginRendering": {
"surfaceId": "gallery-complex-layout",
"root": "root"
}
}
]
Loading
Loading