diff --git a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/5_complex_layout.spec.ts b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_complex-layout.spec.ts similarity index 88% rename from renderers/angular/a2ui_explorer/src/app/tests/v0_8/5_complex_layout.spec.ts rename to renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_complex-layout.spec.ts index ce9ca9e393..5551e278aa 100644 --- a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/5_complex_layout.spec.ts +++ b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_complex-layout.spec.ts @@ -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; }); diff --git a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/3_interactive_button.spec.ts b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_interactive-button.spec.ts similarity index 92% rename from renderers/angular/a2ui_explorer/src/app/tests/v0_8/3_interactive_button.spec.ts rename to renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_interactive-button.spec.ts index 412c8c864b..58146499a6 100644 --- a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/3_interactive_button.spec.ts +++ b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_interactive-button.spec.ts @@ -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; beforeEach(async () => { - fixture = await loadExample('Interactive Button (minimal)', Version.V0_8); + fixture = await loadExample('Interactive Button', Version.V0_8); textContent = getCanvas().textContent; }); diff --git a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/2_row_layout.spec.ts b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_row-layout.spec.ts similarity index 88% rename from renderers/angular/a2ui_explorer/src/app/tests/v0_8/2_row_layout.spec.ts rename to renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_row-layout.spec.ts index 4349352282..aad383ce1b 100644 --- a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/2_row_layout.spec.ts +++ b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_row-layout.spec.ts @@ -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; }); diff --git a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/4_login_form.spec.ts b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-login-form.spec.ts similarity index 94% rename from renderers/angular/a2ui_explorer/src/app/tests/v0_8/4_login_form.spec.ts rename to renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-login-form.spec.ts index 9252a0c827..ffcecd5fb6 100644 --- a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/4_login_form.spec.ts +++ b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-login-form.spec.ts @@ -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; beforeEach(async () => { - fixture = await loadExample('Login Form (minimal)', Version.V0_8); + fixture = await loadExample('Simple Login Form', Version.V0_8); textContent = getCanvas().textContent; }); diff --git a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/1_simple_text.spec.ts b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-text.spec.ts similarity index 88% rename from renderers/angular/a2ui_explorer/src/app/tests/v0_8/1_simple_text.spec.ts rename to renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-text.spec.ts index 5fb610da36..5556fa5742 100644 --- a/renderers/angular/a2ui_explorer/src/app/tests/v0_8/1_simple_text.spec.ts +++ b/renderers/angular/a2ui_explorer/src/app/tests/v0_8/00_simple-text.spec.ts @@ -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; }); diff --git a/renderers/angular/scripts/generate-examples.mjs b/renderers/angular/scripts/generate-examples.mjs index 6b3a6efe8d..24d88c3d14 100644 --- a/renderers/angular/scripts/generate-examples.mjs +++ b/renderers/angular/scripts/generate-examples.mjs @@ -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. @@ -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}, }; /** @@ -46,35 +45,13 @@ const HELP_MESSAGE = `Usage: node generate-examples.mjs [options] Options: -o, --out-file Output file path (default: ${DEFAULT_OUT_FILE}) -c, --catalog 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 -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) { const examples = []; for (const catalog of catalogs) { @@ -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}); @@ -145,7 +118,6 @@ 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}); @@ -153,18 +125,8 @@ async function main() { 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 = `/** diff --git a/renderers/lit/a2ui_explorer/README.md b/renderers/lit/a2ui_explorer/README.md index b27c866141..fa8a6d39cd 100644 --- a/renderers/lit/a2ui_explorer/README.md +++ b/renderers/lit/a2ui_explorer/README.md @@ -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 @@ -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. diff --git a/renderers/lit/a2ui_explorer/scripts/generate-examples.mjs b/renderers/lit/a2ui_explorer/scripts/generate-examples.mjs index 9bc3dcb8d8..a5b25cb19f 100644 --- a/renderers/lit/a2ui_explorer/scripts/generate-examples.mjs +++ b/renderers/lit/a2ui_explorer/scripts/generate-examples.mjs @@ -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'); @@ -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}';`); diff --git a/renderers/react/a2ui_explorer/package.json b/renderers/react/a2ui_explorer/package.json index 7ccb1987bb..7ddf0e9d51 100644 --- a/renderers/react/a2ui_explorer/package.json +++ b/renderers/react/a2ui_explorer/package.json @@ -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": [ diff --git a/renderers/react/a2ui_explorer/scripts/generate-examples.mjs b/renderers/react/a2ui_explorer/scripts/generate-examples.mjs index 429c34e9bc..dbf1d1cd7a 100644 --- a/renderers/react/a2ui_explorer/scripts/generate-examples.mjs +++ b/renderers/react/a2ui_explorer/scripts/generate-examples.mjs @@ -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'); @@ -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}';`); diff --git a/renderers/react/visual-parity/package.json b/renderers/react/visual-parity/package.json index 6dbad52f42..74139fe666 100644 --- a/renderers/react/visual-parity/package.json +++ b/renderers/react/visual-parity/package.json @@ -38,4 +38,4 @@ "typescript": "5.9.3", "vite": "^8.0.16" } -} \ No newline at end of file +} diff --git a/specification/v0_8/json/catalogs/basic/examples/00_complex-layout.json b/specification/v0_8/json/catalogs/basic/examples/00_complex-layout.json new file mode 100644 index 0000000000..70dabf8dc5 --- /dev/null +++ b/specification/v0_8/json/catalogs/basic/examples/00_complex-layout.json @@ -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" + } + } +] diff --git a/specification/v0_8/json/catalogs/basic/examples/00_interactive-button.json b/specification/v0_8/json/catalogs/basic/examples/00_interactive-button.json new file mode 100644 index 0000000000..93adc6e6fa --- /dev/null +++ b/specification/v0_8/json/catalogs/basic/examples/00_interactive-button.json @@ -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" + } + } +] diff --git a/specification/v0_8/json/catalogs/basic/examples/00_row-layout.json b/specification/v0_8/json/catalogs/basic/examples/00_row-layout.json new file mode 100644 index 0000000000..2d55b4ef8e --- /dev/null +++ b/specification/v0_8/json/catalogs/basic/examples/00_row-layout.json @@ -0,0 +1,49 @@ +[ + { + "surfaceUpdate": { + "surfaceId": "gallery-row-layout", + "components": [ + { + "id": "root", + "component": { + "Row": { + "children": { + "explicitList": ["left_text", "right_text"] + }, + "distribution": "spaceBetween", + "alignment": "center" + } + } + }, + { + "id": "left_text", + "component": { + "Text": { + "text": { + "literalString": "Left Content" + }, + "usageHint": "body" + } + } + }, + { + "id": "right_text", + "component": { + "Text": { + "text": { + "literalString": "Right Content" + }, + "usageHint": "caption" + } + } + } + ] + } + }, + { + "beginRendering": { + "surfaceId": "gallery-row-layout", + "root": "root" + } + } +] diff --git a/specification/v0_8/json/catalogs/basic/examples/00_simple-login-form.json b/specification/v0_8/json/catalogs/basic/examples/00_simple-login-form.json new file mode 100644 index 0000000000..d1b016d824 --- /dev/null +++ b/specification/v0_8/json/catalogs/basic/examples/00_simple-login-form.json @@ -0,0 +1,118 @@ +[ + { + "dataModelUpdate": { + "surfaceId": "gallery-simple-login-form", + "path": "/", + "contents": [ + { + "key": "username", + "valueString": "" + }, + { + "key": "password", + "valueString": "" + } + ] + } + }, + { + "surfaceUpdate": { + "surfaceId": "gallery-simple-login-form", + "components": [ + { + "id": "root", + "component": { + "Column": { + "children": { + "explicitList": ["form_title", "username_field", "password_field", "submit_button"] + }, + "distribution": "start", + "alignment": "stretch" + } + } + }, + { + "id": "form_title", + "component": { + "Text": { + "text": { + "literalString": "Login" + }, + "usageHint": "h2" + } + } + }, + { + "id": "username_field", + "component": { + "TextField": { + "label": { + "literalString": "Username" + }, + "text": { + "path": "/username" + }, + "textFieldType": "shortText" + } + } + }, + { + "id": "password_field", + "component": { + "TextField": { + "label": { + "literalString": "Password" + }, + "text": { + "path": "/password" + }, + "textFieldType": "obscured" + } + } + }, + { + "id": "submit_button", + "component": { + "Button": { + "child": "submit_label", + "primary": true, + "action": { + "name": "login_submitted", + "context": [ + { + "key": "user", + "value": { + "path": "/username" + } + }, + { + "key": "pass", + "value": { + "path": "/password" + } + } + ] + } + } + } + }, + { + "id": "submit_label", + "component": { + "Text": { + "text": { + "literalString": "Sign In" + } + } + } + } + ] + } + }, + { + "beginRendering": { + "surfaceId": "gallery-simple-login-form", + "root": "root" + } + } +] diff --git a/specification/v0_8/json/catalogs/basic/examples/00_simple-text.json b/specification/v0_8/json/catalogs/basic/examples/00_simple-text.json new file mode 100644 index 0000000000..9670e5b2ee --- /dev/null +++ b/specification/v0_8/json/catalogs/basic/examples/00_simple-text.json @@ -0,0 +1,26 @@ +[ + { + "surfaceUpdate": { + "surfaceId": "gallery-simple-text", + "components": [ + { + "id": "root", + "component": { + "Text": { + "text": { + "literalString": "Hello, Minimal Catalog!" + }, + "usageHint": "h1" + } + } + } + ] + } + }, + { + "beginRendering": { + "surfaceId": "gallery-simple-text", + "root": "root" + } + } +] diff --git a/specification/v0_9_1/catalogs/minimal/examples/5_complex_layout.json b/specification/v0_9/catalogs/basic/examples/00_complex-layout.json similarity index 88% rename from specification/v0_9_1/catalogs/minimal/examples/5_complex_layout.json rename to specification/v0_9/catalogs/basic/examples/00_complex-layout.json index f1e01e3fd6..2f36f8a679 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/5_complex_layout.json +++ b/specification/v0_9/catalogs/basic/examples/00_complex-layout.json @@ -1,18 +1,18 @@ { "name": "Complex Layout", - "description": "Nested rows and columns", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_5", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json" + "surfaceId": "gallery-complex-layout", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_5", + "surfaceId": "gallery-complex-layout", "components": [ { "id": "root", diff --git a/specification/v0_9_1/catalogs/minimal/examples/7_incremental.json b/specification/v0_9/catalogs/basic/examples/00_incremental.json similarity index 88% rename from specification/v0_9_1/catalogs/minimal/examples/7_incremental.json rename to specification/v0_9/catalogs/basic/examples/00_incremental.json index af60475b7a..c6e121dca4 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/7_incremental.json +++ b/specification/v0_9/catalogs/basic/examples/00_incremental.json @@ -1,18 +1,18 @@ { - "name": "Incremental List", - "description": "Demonstrates progressive rendering of a list with templates and data model reactivity.", + "name": "Incremental", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_7", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json" + "surfaceId": "gallery-incremental", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateDataModel": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "path": "/", "value": { "restaurants": [ @@ -38,7 +38,7 @@ { "version": "v0.9", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "root", @@ -54,7 +54,7 @@ { "version": "v0.9", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "restaurant_card", @@ -88,7 +88,7 @@ { "version": "v0.9", "updateDataModel": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "path": "/restaurants/3", "value": { "title": "Spice Route", @@ -100,7 +100,7 @@ { "version": "v0.9", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "restaurant_card", diff --git a/specification/v0_9_1/catalogs/minimal/examples/3_interactive_button.json b/specification/v0_9/catalogs/basic/examples/00_interactive-button.json similarity index 84% rename from specification/v0_9_1/catalogs/minimal/examples/3_interactive_button.json rename to specification/v0_9/catalogs/basic/examples/00_interactive-button.json index bd4756da3f..d1eec8b2c7 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/3_interactive_button.json +++ b/specification/v0_9/catalogs/basic/examples/00_interactive-button.json @@ -1,18 +1,18 @@ { "name": "Interactive Button", - "description": "Button with click event", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_3", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json" + "surfaceId": "gallery-interactive-button", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_3", + "surfaceId": "gallery-interactive-button", "components": [ { "id": "root", diff --git a/specification/v0_9_1/catalogs/minimal/examples/2_row_layout.json b/specification/v0_9/catalogs/basic/examples/00_row-layout.json similarity index 81% rename from specification/v0_9_1/catalogs/minimal/examples/2_row_layout.json rename to specification/v0_9/catalogs/basic/examples/00_row-layout.json index a8c7fc1d23..4e43324984 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/2_row_layout.json +++ b/specification/v0_9/catalogs/basic/examples/00_row-layout.json @@ -1,18 +1,18 @@ { "name": "Row Layout", - "description": "Two components side-by-side", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_2", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json" + "surfaceId": "gallery-row-layout", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_2", + "surfaceId": "gallery-row-layout", "components": [ { "id": "root", diff --git a/specification/v0_9_1/catalogs/minimal/examples/4_login_form.json b/specification/v0_9/catalogs/basic/examples/00_simple-login-form.json similarity index 88% rename from specification/v0_9_1/catalogs/minimal/examples/4_login_form.json rename to specification/v0_9/catalogs/basic/examples/00_simple-login-form.json index a72d945084..aab65a76f9 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/4_login_form.json +++ b/specification/v0_9/catalogs/basic/examples/00_simple-login-form.json @@ -1,19 +1,19 @@ { - "name": "Login Form", - "description": "Form with input fields and action", + "name": "Simple Login Form", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_4", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json", + "surfaceId": "gallery-simple-login-form", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "sendDataModel": true } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_4", + "surfaceId": "gallery-simple-login-form", "components": [ { "id": "root", diff --git a/specification/v0_9_1/catalogs/minimal/examples/1_simple_text.json b/specification/v0_9/catalogs/basic/examples/00_simple-text.json similarity index 70% rename from specification/v0_9_1/catalogs/minimal/examples/1_simple_text.json rename to specification/v0_9/catalogs/basic/examples/00_simple-text.json index 8be9f50739..81cab829f5 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/1_simple_text.json +++ b/specification/v0_9/catalogs/basic/examples/00_simple-text.json @@ -1,18 +1,18 @@ { "name": "Simple Text", - "description": "Basic text rendering", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_1", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json" + "surfaceId": "gallery-simple-text", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_1", + "surfaceId": "gallery-simple-text", "components": [ { "id": "root", diff --git a/specification/v0_9/docs/renderer_guide.md b/specification/v0_9/docs/renderer_guide.md index 86d5f0ed0f..468440850f 100644 --- a/specification/v0_9/docs/renderer_guide.md +++ b/specification/v0_9/docs/renderer_guide.md @@ -743,8 +743,8 @@ Thoroughly review: - `specification/v0_9/docs/a2ui_protocol.md` (protocol rules) - `specification/v0_9/json/common_types.json` (dynamic binding types) - `specification/v0_9/json/server_to_client.json` (message envelopes) -- `specification/v0_9/json/catalogs/minimal/minimal_catalog.json` (your initial target) -- `specification/v0_9/docs/basic_catalog_implementation_guide.md` (for rendering and spacing rules for when you get to the basic catalog) +- `specification/v0_9/catalogs/basic/catalog.json` (your target) +- `specification/v0_9/docs/basic_catalog_implementation_guide.md` (for rendering and spacing rules) ### 2. Key Architecture Decisions (Write a Plan Document) @@ -776,13 +776,13 @@ Implement the bridge between models and native UI (Section 5 & 6). - Implement the `Surface` view/widget that recurses through components. - Implement subscription lifecycle management (lazy mounting, unmounting disposal). -### 5. Minimal Catalog Support +### 5. Initial Basic Catalog Support -Target the `minimal_catalog.json` first. +Target a foundational subset of components in `basic/catalog.json` first to bootstrap your implementation. -- Implement the pure API schemas for `Text`, `Row`, `Column`, `Button`, `TextField`. +- Implement the pure API schemas for `Text`, `Row`, `Column`, `Button`, `TextField` (which are part of the standard Basic Catalog). - Implement the specific native UI rendering components for these. -- Implement the `capitalize` function. +- Implement the `formatString` function (which is required for basic text rendering, see Section 7). - Bundle these into a `Catalog`. - **Action**: Write unit tests verifying that properties update reactively when data changes. @@ -790,16 +790,15 @@ Target the `minimal_catalog.json` first. Build the Gallery App following the requirements in **Section 8**. -- Load JSON samples from `specification/v0_9/json/catalogs/minimal/examples/`. +- Load JSON samples from `specification/v0_9/catalogs/basic/examples/` (focusing on the simpler ones first, such as those using only the bootstrapped components). - Verify progressive rendering and reactivity. - **STOP HERE. Ask the user for approval of the architecture and gallery application before proceeding to step 7.** -### 7. Basic Catalog Support +### 7. Complete Basic Catalog Support -Once the minimal architecture is proven robust, refer to the [Basic Catalog Implementation Guide](basic_catalog_implementation_guide.md) and: +Once the initial architecture is proven robust, complete the implementation of the Basic Catalog: -- **Core Library**: Implement the full suite of basic functions. It is crucial to note that string interpolation and expression parsing should ONLY happen within the `formatString` function. Do not attempt to add global string interpolation to all strings. +- **Core Library**: Implement the remaining basic functions. It is crucial to note that string interpolation and expression parsing should ONLY happen within the `formatString` function. Do not attempt to add global string interpolation to all strings. - **Core Library**: Create definitions/binders for the remaining Basic Catalog components. - **Framework Library**: Implement all remaining UI widgets. - **Tests**: Look at existing reference implementations (e.g., `web_core`) to formulate and run comprehensive unit and integration test cases for data coercion and function logic. -- Update the Gallery App to load samples from `specification/v0_9/json/catalogs/basic/examples/`. diff --git a/specification/v0_9_1/catalogs/basic/catalog.json b/specification/v0_9_1/catalogs/basic/catalog.json index cefc2b98bb..5e57459b8f 100644 --- a/specification/v0_9_1/catalogs/basic/catalog.json +++ b/specification/v0_9_1/catalogs/basic/catalog.json @@ -1240,6 +1240,30 @@ }, "required": ["call", "args"], "unevaluatedProperties": false + }, + "capitalize": { + "type": "object", + "description": "Converts an input string to a capitalized version.", + "properties": { + "call": { + "const": "capitalize" + }, + "args": { + "type": "object", + "properties": { + "value": { + "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString" + } + }, + "required": ["value"], + "unevaluatedProperties": false + }, + "returnType": { + "const": "string" + } + }, + "required": ["call", "args"], + "unevaluatedProperties": false } }, "$defs": { @@ -1376,6 +1400,9 @@ }, { "$ref": "#/functions/not" + }, + { + "$ref": "#/functions/capitalize" } ] } diff --git a/specification/v0_9_1/catalogs/minimal/examples/6_capitalized_text.json b/specification/v0_9_1/catalogs/basic/examples/00_capitalized-text.json similarity index 87% rename from specification/v0_9_1/catalogs/minimal/examples/6_capitalized_text.json rename to specification/v0_9_1/catalogs/basic/examples/00_capitalized-text.json index 08480706c1..0b3de42f70 100644 --- a/specification/v0_9_1/catalogs/minimal/examples/6_capitalized_text.json +++ b/specification/v0_9_1/catalogs/basic/examples/00_capitalized-text.json @@ -1,19 +1,19 @@ { "name": "Capitalized Text", - "description": "Client-side function example", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v0.9", "createSurface": { - "surfaceId": "example_6", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json", + "surfaceId": "gallery-capitalized-text", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", "sendDataModel": true } }, { "version": "v0.9", "updateComponents": { - "surfaceId": "example_6", + "surfaceId": "gallery-capitalized-text", "components": [ { "id": "root", diff --git a/specification/v0_9_1/catalogs/basic/examples/00_complex-layout.json b/specification/v0_9_1/catalogs/basic/examples/00_complex-layout.json new file mode 100644 index 0000000000..2f36f8a679 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_complex-layout.json @@ -0,0 +1,65 @@ +{ + "name": "Complex Layout", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-complex-layout", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-complex-layout", + "components": [ + { + "id": "root", + "component": "Column", + "children": ["header", "form_row", "footer"], + "justify": "spaceBetween", + "align": "stretch" + }, + { + "id": "header", + "component": "Text", + "text": "User Profile Form", + "variant": "h1" + }, + { + "id": "form_row", + "component": "Row", + "children": ["first_name", "last_name"], + "justify": "start", + "align": "start" + }, + { + "id": "first_name", + "component": "TextField", + "label": "First Name", + "value": { + "path": "/firstName" + }, + "weight": 1 + }, + { + "id": "last_name", + "component": "TextField", + "label": "Last Name", + "value": { + "path": "/lastName" + }, + "weight": 1 + }, + { + "id": "footer", + "component": "Text", + "text": "Please fill out all fields.", + "variant": "caption" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/basic/examples/00_incremental.json b/specification/v0_9_1/catalogs/basic/examples/00_incremental.json new file mode 100644 index 0000000000..c6e121dca4 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_incremental.json @@ -0,0 +1,134 @@ +{ + "name": "Incremental", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-incremental", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" + } + }, + { + "version": "v0.9", + "updateDataModel": { + "surfaceId": "gallery-incremental", + "path": "/", + "value": { + "restaurants": [ + { + "title": "The Golden Fork", + "subtitle": "Fine Dining & Spirits", + "address": "123 Gastronomy Lane" + }, + { + "title": "Ocean's Bounty", + "subtitle": "Fresh Daily Seafood", + "address": "456 Shoreline Dr" + }, + { + "title": "Pizzeria Roma", + "subtitle": "Authentic Wood-Fired Pizza", + "address": "789 Napoli Way" + } + ] + } + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-incremental", + "components": [ + { + "id": "root", + "component": "Column", + "children": { + "path": "/restaurants", + "componentId": "restaurant_card" + } + } + ] + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-incremental", + "components": [ + { + "id": "restaurant_card", + "component": "Column", + "children": ["rc_title", "rc_subtitle", "rc_address"] + }, + { + "id": "rc_title", + "component": "Text", + "text": { + "path": "title" + } + }, + { + "id": "rc_subtitle", + "component": "Text", + "text": { + "path": "subtitle" + } + }, + { + "id": "rc_address", + "component": "Text", + "text": { + "path": "address" + } + } + ] + } + }, + { + "version": "v0.9", + "updateDataModel": { + "surfaceId": "gallery-incremental", + "path": "/restaurants/3", + "value": { + "title": "Spice Route", + "subtitle": "Exotic Flavors from the East", + "address": "101 Silk Road St" + } + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-incremental", + "components": [ + { + "id": "restaurant_card", + "component": "Column", + "children": ["rc_title", "rc_subtitle", "rc_address", "rc_button"] + }, + { + "id": "rc_button", + "component": "Button", + "child": "rc_button_label", + "action": { + "event": { + "name": "book_now", + "context": { + "restaurantName": { + "path": "title" + } + } + } + } + }, + { + "id": "rc_button_label", + "component": "Text", + "text": "Book now" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/basic/examples/00_interactive-button.json b/specification/v0_9_1/catalogs/basic/examples/00_interactive-button.json new file mode 100644 index 0000000000..d1eec8b2c7 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_interactive-button.json @@ -0,0 +1,51 @@ +{ + "name": "Interactive Button", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-interactive-button", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-interactive-button", + "components": [ + { + "id": "root", + "component": "Column", + "children": ["title", "action_button"], + "justify": "center", + "align": "center" + }, + { + "id": "title", + "component": "Text", + "text": "Click the button below", + "variant": "body" + }, + { + "id": "action_button", + "component": "Button", + "child": "button_label", + "variant": "primary", + "action": { + "event": { + "name": "button_clicked", + "context": {} + } + } + }, + { + "id": "button_label", + "component": "Text", + "text": "Click Me" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/basic/examples/00_row-layout.json b/specification/v0_9_1/catalogs/basic/examples/00_row-layout.json new file mode 100644 index 0000000000..4e43324984 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_row-layout.json @@ -0,0 +1,40 @@ +{ + "name": "Row Layout", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-row-layout", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-row-layout", + "components": [ + { + "id": "root", + "component": "Row", + "children": ["left_text", "right_text"], + "justify": "spaceBetween", + "align": "center" + }, + { + "id": "left_text", + "component": "Text", + "text": "Left Content", + "variant": "body" + }, + { + "id": "right_text", + "component": "Text", + "text": "Right Content", + "variant": "caption" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/basic/examples/00_simple-login-form.json b/specification/v0_9_1/catalogs/basic/examples/00_simple-login-form.json new file mode 100644 index 0000000000..aab65a76f9 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_simple-login-form.json @@ -0,0 +1,77 @@ +{ + "name": "Simple Login Form", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-simple-login-form", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json", + "sendDataModel": true + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-simple-login-form", + "components": [ + { + "id": "root", + "component": "Column", + "children": ["form_title", "username_field", "password_field", "submit_button"], + "justify": "start", + "align": "stretch" + }, + { + "id": "form_title", + "component": "Text", + "text": "Login", + "variant": "h2" + }, + { + "id": "username_field", + "component": "TextField", + "label": "Username", + "value": { + "path": "/username" + }, + "variant": "shortText" + }, + { + "id": "password_field", + "component": "TextField", + "label": "Password", + "value": { + "path": "/password" + }, + "variant": "obscured" + }, + { + "id": "submit_button", + "component": "Button", + "child": "submit_label", + "variant": "primary", + "action": { + "event": { + "name": "login_submitted", + "context": { + "user": { + "path": "/username" + }, + "pass": { + "path": "/password" + } + } + } + } + }, + { + "id": "submit_label", + "component": "Text", + "text": "Sign In" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/basic/examples/00_simple-text.json b/specification/v0_9_1/catalogs/basic/examples/00_simple-text.json new file mode 100644 index 0000000000..81cab829f5 --- /dev/null +++ b/specification/v0_9_1/catalogs/basic/examples/00_simple-text.json @@ -0,0 +1,27 @@ +{ + "name": "Simple Text", + "description": "Simple example demonstrating basic catalog components.", + "messages": [ + { + "version": "v0.9", + "createSurface": { + "surfaceId": "gallery-simple-text", + "catalogId": "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json" + } + }, + { + "version": "v0.9", + "updateComponents": { + "surfaceId": "gallery-simple-text", + "components": [ + { + "id": "root", + "component": "Text", + "text": "Hello, Minimal Catalog!", + "variant": "h1" + } + ] + } + } + ] +} diff --git a/specification/v0_9_1/catalogs/minimal/README.md b/specification/v0_9_1/catalogs/minimal/README.md deleted file mode 100644 index e5f3f6b8f1..0000000000 --- a/specification/v0_9_1/catalogs/minimal/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal A2UI Catalog - -This folder contains a minimal A2UI component catalog (`minimal_catalog.json`) to be used as a test bed for testing new renderer implementations. - -## Purpose - -The basic A2UI catalog is comprehensive and features many components, functions, and layout primitives. Building a new renderer from scratch to support the entire catalog can be overwhelming. The minimal catalog reduces the surface area to a core set of fundamental components: - -- **Text**: For rendering text strings. -- **Row**: For horizontal flex layouts. -- **Column**: For vertical flex layouts. -- **Button**: For basic interactivity and action dispatching. -- **TextField**: For two-way data-bound user inputs. - -By targeting this minimal catalog first, new renderer implementations can establish a solid foundation—covering layout algorithms, component nesting, data binding, and event handling—before scaling up to the full basic catalog. - -## Examples - -The `examples/` directory contains 5 JSON arrays of layout messages (`server_to_client_list` format) demonstrating various UI scenarios using only the components defined in this minimal catalog. They serve as basic integration tests. diff --git a/specification/v0_9_1/catalogs/minimal/catalog.json b/specification/v0_9_1/catalogs/minimal/catalog.json deleted file mode 100644 index cf5b3ead36..0000000000 --- a/specification/v0_9_1/catalogs/minimal/catalog.json +++ /dev/null @@ -1,202 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json", - "title": "A2UI Minimal Catalog", - "description": "A minimal A2UI catalog for testing renderers.", - "catalogId": "https://a2ui.org/specification/v0_9/catalogs/minimal/catalog.json", - "components": { - "Text": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Text"}, - "text": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString" - }, - "variant": { - "type": "string", - "enum": ["h1", "h2", "h3", "h4", "h5", "caption", "body"] - } - }, - "required": ["component", "text"] - } - ], - "unevaluatedProperties": false - }, - "Row": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Row"}, - "children": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ChildList" - }, - "justify": { - "type": "string", - "enum": [ - "center", - "end", - "spaceAround", - "spaceBetween", - "spaceEvenly", - "start", - "stretch" - ] - }, - "align": { - "type": "string", - "enum": ["start", "center", "end", "stretch"] - } - }, - "required": ["component", "children"] - } - ], - "unevaluatedProperties": false - }, - "Column": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Column"}, - "children": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ChildList" - }, - "justify": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly", - "stretch" - ] - }, - "align": { - "type": "string", - "enum": ["center", "end", "start", "stretch"] - } - }, - "required": ["component", "children"] - } - ], - "unevaluatedProperties": false - }, - "Button": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/Checkable"}, - { - "type": "object", - "properties": { - "component": {"const": "Button"}, - "child": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentId" - }, - "variant": { - "type": "string", - "enum": ["primary", "borderless"] - }, - "action": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/Action" - } - }, - "required": ["component", "child", "action"] - } - ], - "unevaluatedProperties": false - }, - "TextField": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/Checkable"}, - { - "type": "object", - "properties": { - "component": {"const": "TextField"}, - "label": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString" - }, - "value": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString" - }, - "variant": { - "type": "string", - "enum": ["longText", "number", "shortText", "obscured"] - }, - "validationRegexp": {"type": "string"} - }, - "required": ["component", "label"] - } - ], - "unevaluatedProperties": false - } - }, - "functions": { - "capitalize": { - "type": "object", - "description": "Converts an input string to a capitalized version.", - "properties": { - "call": {"const": "capitalize"}, - "args": { - "type": "object", - "properties": { - "value": { - "$ref": "https://a2ui.org/specification/v0_9/common_types.json#/$defs/DynamicString" - } - }, - "required": ["value"], - "unevaluatedProperties": false - }, - "returnType": {"const": "string"} - }, - "required": ["call", "args"], - "unevaluatedProperties": false - } - }, - "$defs": { - "CatalogComponentCommon": { - "type": "object", - "properties": { - "weight": {"type": "number"} - } - }, - "theme": { - "type": "object", - "properties": { - "primaryColor": {"type": "string", "pattern": "^#[0-9a-fA-F]{6}$"} - }, - "additionalProperties": true - }, - "anyComponent": { - "oneOf": [ - {"$ref": "#/components/Text"}, - {"$ref": "#/components/Row"}, - {"$ref": "#/components/Column"}, - {"$ref": "#/components/Button"}, - {"$ref": "#/components/TextField"} - ], - "discriminator": {"propertyName": "component"} - }, - "anyFunction": { - "oneOf": [{"$ref": "#/functions/capitalize"}] - } - } -} diff --git a/specification/v1_0/catalogs/basic/catalog.json b/specification/v1_0/catalogs/basic/catalog.json index 9d9767e146..e49d7d177d 100644 --- a/specification/v1_0/catalogs/basic/catalog.json +++ b/specification/v1_0/catalogs/basic/catalog.json @@ -1219,6 +1219,28 @@ }, "required": ["call", "args"], "unevaluatedProperties": false + }, + "capitalize": { + "type": "object", + "description": "Converts an input string to a capitalized version.", + "returnType": "string", + "properties": { + "call": { + "const": "capitalize" + }, + "args": { + "type": "object", + "properties": { + "value": { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" + } + }, + "required": ["value"], + "unevaluatedProperties": false + } + }, + "required": ["call", "args"], + "unevaluatedProperties": false } }, "$defs": { @@ -1350,6 +1372,9 @@ }, { "$ref": "#/functions/not" + }, + { + "$ref": "#/functions/capitalize" } ] } diff --git a/specification/v1_0/catalogs/minimal/examples/6_capitalized_text.json b/specification/v1_0/catalogs/basic/examples/00_capitalized-text.json similarity index 85% rename from specification/v1_0/catalogs/minimal/examples/6_capitalized_text.json rename to specification/v1_0/catalogs/basic/examples/00_capitalized-text.json index 6035e5a639..e8a955dbc6 100644 --- a/specification/v1_0/catalogs/minimal/examples/6_capitalized_text.json +++ b/specification/v1_0/catalogs/basic/examples/00_capitalized-text.json @@ -1,19 +1,19 @@ { "name": "Capitalized Text", - "description": "Client-side function example", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_6", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json", + "surfaceId": "gallery-capitalized-text", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "sendDataModel": true } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_6", + "surfaceId": "gallery-capitalized-text", "components": [ { "id": "root", @@ -46,8 +46,7 @@ "value": { "path": "/inputValue" } - }, - "returnType": "string" + } }, "variant": "h2" } diff --git a/specification/v1_0/catalogs/minimal/examples/5_complex_layout.json b/specification/v1_0/catalogs/basic/examples/00_complex-layout.json similarity index 88% rename from specification/v1_0/catalogs/minimal/examples/5_complex_layout.json rename to specification/v1_0/catalogs/basic/examples/00_complex-layout.json index d2ad3275f1..7fc1311984 100644 --- a/specification/v1_0/catalogs/minimal/examples/5_complex_layout.json +++ b/specification/v1_0/catalogs/basic/examples/00_complex-layout.json @@ -1,18 +1,18 @@ { "name": "Complex Layout", - "description": "Nested rows and columns", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_5", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json" + "surfaceId": "gallery-complex-layout", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_5", + "surfaceId": "gallery-complex-layout", "components": [ { "id": "root", diff --git a/specification/v1_0/catalogs/minimal/examples/7_incremental.json b/specification/v1_0/catalogs/basic/examples/00_incremental.json similarity index 88% rename from specification/v1_0/catalogs/minimal/examples/7_incremental.json rename to specification/v1_0/catalogs/basic/examples/00_incremental.json index 8bafd0b91d..86c11dbbc3 100644 --- a/specification/v1_0/catalogs/minimal/examples/7_incremental.json +++ b/specification/v1_0/catalogs/basic/examples/00_incremental.json @@ -1,18 +1,18 @@ { - "name": "Incremental List", - "description": "Demonstrates progressive rendering of a list with templates and data model reactivity.", + "name": "Incremental", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_7", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json" + "surfaceId": "gallery-incremental", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "v1.0", "updateDataModel": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "path": "/", "value": { "restaurants": [ @@ -38,7 +38,7 @@ { "version": "v1.0", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "root", @@ -54,7 +54,7 @@ { "version": "v1.0", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "restaurant_card", @@ -88,7 +88,7 @@ { "version": "v1.0", "updateDataModel": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "path": "/restaurants/3", "value": { "title": "Spice Route", @@ -100,7 +100,7 @@ { "version": "v1.0", "updateComponents": { - "surfaceId": "example_7", + "surfaceId": "gallery-incremental", "components": [ { "id": "restaurant_card", diff --git a/specification/v1_0/catalogs/minimal/examples/3_interactive_button.json b/specification/v1_0/catalogs/basic/examples/00_interactive-button.json similarity index 84% rename from specification/v1_0/catalogs/minimal/examples/3_interactive_button.json rename to specification/v1_0/catalogs/basic/examples/00_interactive-button.json index b3b8bf0170..c565fc1c87 100644 --- a/specification/v1_0/catalogs/minimal/examples/3_interactive_button.json +++ b/specification/v1_0/catalogs/basic/examples/00_interactive-button.json @@ -1,18 +1,18 @@ { "name": "Interactive Button", - "description": "Button with click event", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_3", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json" + "surfaceId": "gallery-interactive-button", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_3", + "surfaceId": "gallery-interactive-button", "components": [ { "id": "root", diff --git a/specification/v1_0/catalogs/minimal/examples/2_row_layout.json b/specification/v1_0/catalogs/basic/examples/00_row-layout.json similarity index 81% rename from specification/v1_0/catalogs/minimal/examples/2_row_layout.json rename to specification/v1_0/catalogs/basic/examples/00_row-layout.json index a9c9977ff8..dfb878ff84 100644 --- a/specification/v1_0/catalogs/minimal/examples/2_row_layout.json +++ b/specification/v1_0/catalogs/basic/examples/00_row-layout.json @@ -1,18 +1,18 @@ { "name": "Row Layout", - "description": "Two components side-by-side", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_2", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json" + "surfaceId": "gallery-row-layout", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_2", + "surfaceId": "gallery-row-layout", "components": [ { "id": "root", diff --git a/specification/v1_0/catalogs/minimal/examples/4_login_form.json b/specification/v1_0/catalogs/basic/examples/00_simple-login-form.json similarity index 88% rename from specification/v1_0/catalogs/minimal/examples/4_login_form.json rename to specification/v1_0/catalogs/basic/examples/00_simple-login-form.json index 5202a8cb76..7c998ded2f 100644 --- a/specification/v1_0/catalogs/minimal/examples/4_login_form.json +++ b/specification/v1_0/catalogs/basic/examples/00_simple-login-form.json @@ -1,19 +1,19 @@ { - "name": "Login Form", - "description": "Form with input fields and action", + "name": "Simple Login Form", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_4", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json", + "surfaceId": "gallery-simple-login-form", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "sendDataModel": true } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_4", + "surfaceId": "gallery-simple-login-form", "components": [ { "id": "root", diff --git a/specification/v1_0/catalogs/minimal/examples/1_simple_text.json b/specification/v1_0/catalogs/basic/examples/00_simple-text.json similarity index 70% rename from specification/v1_0/catalogs/minimal/examples/1_simple_text.json rename to specification/v1_0/catalogs/basic/examples/00_simple-text.json index 14a52e72dc..d93d49c16a 100644 --- a/specification/v1_0/catalogs/minimal/examples/1_simple_text.json +++ b/specification/v1_0/catalogs/basic/examples/00_simple-text.json @@ -1,18 +1,18 @@ { "name": "Simple Text", - "description": "Basic text rendering", + "description": "Simple example demonstrating basic catalog components.", "messages": [ { "version": "v1.0", "createSurface": { - "surfaceId": "example_1", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json" + "surfaceId": "gallery-simple-text", + "catalogId": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json" } }, { "version": "v1.0", "updateComponents": { - "surfaceId": "example_1", + "surfaceId": "gallery-simple-text", "components": [ { "id": "root", diff --git a/specification/v1_0/catalogs/minimal/README.md b/specification/v1_0/catalogs/minimal/README.md deleted file mode 100644 index e5f3f6b8f1..0000000000 --- a/specification/v1_0/catalogs/minimal/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal A2UI Catalog - -This folder contains a minimal A2UI component catalog (`minimal_catalog.json`) to be used as a test bed for testing new renderer implementations. - -## Purpose - -The basic A2UI catalog is comprehensive and features many components, functions, and layout primitives. Building a new renderer from scratch to support the entire catalog can be overwhelming. The minimal catalog reduces the surface area to a core set of fundamental components: - -- **Text**: For rendering text strings. -- **Row**: For horizontal flex layouts. -- **Column**: For vertical flex layouts. -- **Button**: For basic interactivity and action dispatching. -- **TextField**: For two-way data-bound user inputs. - -By targeting this minimal catalog first, new renderer implementations can establish a solid foundation—covering layout algorithms, component nesting, data binding, and event handling—before scaling up to the full basic catalog. - -## Examples - -The `examples/` directory contains 5 JSON arrays of layout messages (`server_to_client_list` format) demonstrating various UI scenarios using only the components defined in this minimal catalog. They serve as basic integration tests. diff --git a/specification/v1_0/catalogs/minimal/catalog.json b/specification/v1_0/catalogs/minimal/catalog.json deleted file mode 100644 index 84d0f17007..0000000000 --- a/specification/v1_0/catalogs/minimal/catalog.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json", - "title": "A2UI Minimal Catalog", - "description": "A minimal A2UI catalog for testing renderers.", - "catalogId": "https://a2ui.org/specification/v1_0/catalogs/minimal/catalog.json", - "components": { - "Text": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Text"}, - "text": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" - }, - "variant": { - "type": "string", - "enum": ["h1", "h2", "h3", "h4", "h5", "caption", "body"] - } - }, - "required": ["component", "text"] - } - ], - "unevaluatedProperties": false - }, - "Row": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Row"}, - "children": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ChildList" - }, - "justify": { - "type": "string", - "enum": [ - "center", - "end", - "spaceAround", - "spaceBetween", - "spaceEvenly", - "start", - "stretch" - ] - }, - "align": { - "type": "string", - "enum": ["start", "center", "end", "stretch"] - } - }, - "required": ["component", "children"] - } - ], - "unevaluatedProperties": false - }, - "Column": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - { - "type": "object", - "properties": { - "component": {"const": "Column"}, - "children": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ChildList" - }, - "justify": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly", - "stretch" - ] - }, - "align": { - "type": "string", - "enum": ["center", "end", "start", "stretch"] - } - }, - "required": ["component", "children"] - } - ], - "unevaluatedProperties": false - }, - "Button": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable"}, - { - "type": "object", - "properties": { - "component": {"const": "Button"}, - "child": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentId" - }, - "variant": { - "type": "string", - "enum": ["primary", "borderless"] - }, - "action": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Action" - } - }, - "required": ["component", "child", "action"] - } - ], - "unevaluatedProperties": false - }, - "TextField": { - "type": "object", - "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable"}, - { - "type": "object", - "properties": { - "component": {"const": "TextField"}, - "label": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" - }, - "value": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" - }, - "variant": { - "type": "string", - "enum": ["longText", "number", "shortText", "obscured"] - }, - "validationRegexp": {"type": "string"} - }, - "required": ["component", "label"] - } - ], - "unevaluatedProperties": false - } - }, - "functions": { - "capitalize": { - "type": "object", - "description": "Converts an input string to a capitalized version.", - "returnType": "string", - "properties": { - "call": {"const": "capitalize"}, - "args": { - "type": "object", - "properties": { - "value": { - "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" - } - }, - "required": ["value"], - "unevaluatedProperties": false - } - }, - "required": ["call", "args"], - "unevaluatedProperties": false - } - }, - "$defs": { - "CatalogComponentCommon": { - "type": "object", - "properties": { - "weight": {"type": "number"} - } - }, - "surfaceProperties": { - "type": "object", - "properties": {}, - "additionalProperties": true - }, - "anyComponent": { - "oneOf": [ - {"$ref": "#/components/Text"}, - {"$ref": "#/components/Row"}, - {"$ref": "#/components/Column"}, - {"$ref": "#/components/Button"}, - {"$ref": "#/components/TextField"} - ], - "discriminator": {"propertyName": "component"} - }, - "anyFunction": { - "oneOf": [{"$ref": "#/functions/capitalize"}] - } - } -} diff --git a/specification/v1_0/docs/evolution_guide.md b/specification/v1_0/docs/evolution_guide.md index c8397c2afd..ce3500e34b 100644 --- a/specification/v1_0/docs/evolution_guide.md +++ b/specification/v1_0/docs/evolution_guide.md @@ -26,14 +26,14 @@ Version 1.0 differs from 0.9 in the following ways: - Supported standard JSON Schema metadata fields (`$schema`, `$id`, `title`, and `description`) in the Catalog object definition. Since the Catalog schema restricts properties with `additionalProperties: false`, this ensures inline catalogs containing standard schema metadata do not fail schema validation. - Enforced Unicode Standard Annex #31 (UAX #31) identifier naming constraints (`XID_Start`, `XID_Continue`) across component names, function names, and argument keys. -### 2.2. Standard catalogs (basic and minimal) +### 2.2. Standard catalogs (basic) - Added `posterUrl` property to the `Video` component in `catalogs/basic/catalog.json`, allowing a preview image to be displayed before the video plays. - Added `placeholder` prop to the `TextField` component schema. - Added a `steps` property to the `Slider` component schema to snap values to discrete intervals. - Added an optional `instructions` field to the `Catalog` schema (`catalogs/basic/catalog.json`) to embed Markdown guidelines/rules directly, replacing the external `rules.txt` file. - Renamed `svgPath` to `path` in the custom SVG icon definition object schema. -- Renamed `$defs/theme` to `$defs/surfaceProperties` in both the basic and minimal catalogs. +- Renamed `$defs/theme` to `$defs/surfaceProperties` in the basic catalog. ### 2.3. Server-to-client messages diff --git a/specification/v1_0/docs/renderer_guide.md b/specification/v1_0/docs/renderer_guide.md index 9b0234aeb4..87af707147 100644 --- a/specification/v1_0/docs/renderer_guide.md +++ b/specification/v1_0/docs/renderer_guide.md @@ -743,8 +743,8 @@ Thoroughly review: - `specification/v1_0/docs/a2ui_protocol.md` (protocol rules) - `specification/v1_0/json/common_types.json` (dynamic binding types) - `specification/v1_0/json/server_to_client.json` (message envelopes) -- `specification/v1_0/json/catalogs/minimal/minimal_catalog.json` (your initial target) -- `specification/v1_0/docs/basic_catalog_implementation_guide.md` (for rendering and spacing rules for when you get to the basic catalog) +- `specification/v1_0/catalogs/basic/catalog.json` (your target) +- `specification/v1_0/docs/basic_catalog_implementation_guide.md` (for rendering and spacing rules) ### 2. Key Architecture Decisions (Write a Plan Document) @@ -776,13 +776,13 @@ Implement the bridge between models and native UI (Section 5 & 6). - Implement the `Surface` view/widget that recurses through components. - Implement subscription lifecycle management (lazy mounting, unmounting disposal). -### 5. Minimal Catalog Support +### 5. Initial Basic Catalog Support -Target the `minimal_catalog.json` first. +Target a foundational subset of components in `basic/catalog.json` first to bootstrap your implementation. -- Implement the pure API schemas for `Text`, `Row`, `Column`, `Button`, `TextField`. +- Implement the pure API schemas for `Text`, `Row`, `Column`, `Button`, `TextField` (which are part of the standard Basic Catalog). - Implement the specific native UI rendering components for these. -- Implement the `capitalize` function. +- Implement the `formatString` function (which is required for basic text rendering, see Section 7). - Bundle these into a `Catalog`. - **Action**: Write unit tests verifying that properties update reactively when data changes. @@ -790,13 +790,13 @@ Target the `minimal_catalog.json` first. Build the Gallery App following the requirements in **Section 8**. -- Load JSON samples from `specification/v1_0/json/catalogs/minimal/examples/`. +- Load JSON samples from `specification/v1_0/catalogs/basic/examples/`. - Verify progressive rendering and reactivity. - **STOP HERE. Ask the user for approval of the architecture and gallery application before proceeding to step 7.** -### 7. Basic Catalog Support +### 7. Full Basic Catalog Support -Once the minimal architecture is proven robust, refer to the [Basic Catalog Implementation Guide](basic_catalog_implementation_guide.md) and: +Once the initial architecture is proven robust, refer to the [Basic Catalog Implementation Guide](basic_catalog_implementation_guide.md) and: - **Core Library**: Implement the full suite of basic functions. It is crucial to note that string interpolation and expression parsing should ONLY happen within the `formatString` function. Do not attempt to add global string interpolation to all strings. - **Core Library**: Create definitions/binders for the remaining Basic Catalog components. diff --git a/specification/v1_0/test/run_tests.py b/specification/v1_0/test/run_tests.py index 32b053cacc..2d60255a7e 100755 --- a/specification/v1_0/test/run_tests.py +++ b/specification/v1_0/test/run_tests.py @@ -202,7 +202,6 @@ def validate_catalogs_structure(): catalogs_to_validate = [ ("catalogs/basic/catalog.json", os.path.join(SPEC_DIR, "catalogs/basic/catalog.json")), - ("catalogs/minimal/catalog.json", os.path.join(SPEC_DIR, "catalogs/minimal/catalog.json")), ("test/testing_catalog.json", os.path.join(TEST_DIR, "testing_catalog.json")), ] @@ -243,7 +242,6 @@ def validate_catalogs_identifiers(): """ catalogs_to_validate = [ ("catalogs/basic/catalog.json", os.path.join(SPEC_DIR, "catalogs/basic/catalog.json")), - ("catalogs/minimal/catalog.json", os.path.join(SPEC_DIR, "catalogs/minimal/catalog.json")), ("test/testing_catalog.json", os.path.join(TEST_DIR, "testing_catalog.json")), ]