Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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/catalogs', catalogs, '0.9');

// Generate the file now!
const tsContent = `/**
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.
12 changes: 6 additions & 6 deletions renderers/react/tests/a2ui_explorer/examples.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ describe('examples.ts (Sample Loading Logic)', () => {
describe('processExampleModules', () => {
it('should correctly parse and sort modules', () => {
const mockModules = {
'../../../../specification/v0_9/catalogs/minimal/examples/1_simple.json': {
'../../../../specification/v0_9/catalogs/custom/examples/1_simple.json': {
name: 'Simple',
},
'../../../../specification/v0_9/catalogs/basic/examples/01_card.json': {
default: {name: 'Card'},
},
'../../../../specification/v0_9/catalogs/minimal/examples/2_row.json': {name: 'Row'},
'../../../../specification/v0_9/catalogs/custom/examples/2_row.json': {name: 'Row'},
};

const result = processExampleModules(mockModules);
Expand All @@ -39,12 +39,12 @@ describe('examples.ts (Sample Loading Logic)', () => {
expect(result[0]!.key).toBe('basic_01_card');
expect(result[0]!.data).toEqual({name: 'Card'});

expect(result[1]!.catalog).toBe('Minimal');
expect(result[1]!.key).toBe('minimal_1_simple');
expect(result[1]!.catalog).toBe('Custom');
expect(result[1]!.key).toBe('custom_1_simple');
expect(result[1]!.data).toEqual({name: 'Simple'});

expect(result[2]!.catalog).toBe('Minimal');
expect(result[2]!.key).toBe('minimal_2_row');
expect(result[2]!.catalog).toBe('Custom');
expect(result[2]!.key).toBe('custom_2_row');
expect(result[2]!.data).toEqual({name: 'Row'});
});

Expand Down
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"
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[
{
"surfaceUpdate": {
"surfaceId": "gallery-interactive-button",
"components": [
{
"id": "root",
"component": {
"Column": {
"children": {
"explicitList": ["title", "action_button"]
},
"distribution": "center",
"alignment": "center"
}
}
},
{
"id": "title",
"component": {
"Text": {
"text": {
"literalString": "Click the button below"
},
"usageHint": "body"
}
}
},
{
"id": "action_button",
"component": {
"Button": {
"child": "button_label",
"primary": true,
"action": {
"name": "button_clicked"
}
}
}
},
{
"id": "button_label",
"component": {
"Text": {
"text": {
"literalString": "Click Me"
}
}
}
}
]
}
},
{
"beginRendering": {
"surfaceId": "gallery-interactive-button",
"root": "root"
}
}
]
Loading
Loading