diff --git a/renderers/lit/package-lock.json b/renderers/lit/package-lock.json index 77acb5c38..ba265d813 100644 --- a/renderers/lit/package-lock.json +++ b/renderers/lit/package-lock.json @@ -24,18 +24,18 @@ }, "../web_core": { "name": "@a2ui/web_core", - "version": "0.8.5", + "version": "0.8.6", "license": "Apache-2.0", "dependencies": { "@preact/signals-core": "^1.13.0", "date-fns": "^4.1.0", - "zod": "^3.25.76" + "zod": "^3.25.76", + "zod-to-json-schema": "^3.25.1" }, "devDependencies": { "@types/node": "^24.11.0", "typescript": "^5.8.3", - "wireit": "^0.15.0-pre.2", - "zod-to-json-schema": "^3.25.1" + "wireit": "^0.15.0-pre.2" } }, "../web_core/node_modules/@nodelib/fs.scandir": { @@ -441,7 +441,6 @@ }, "../web_core/node_modules/zod-to-json-schema": { "version": "3.25.1", - "dev": true, "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4" diff --git a/renderers/lit/package.json b/renderers/lit/package.json index 99f8a4191..e4a7c2ffb 100644 --- a/renderers/lit/package.json +++ b/renderers/lit/package.json @@ -13,6 +13,14 @@ "types": "./dist/src/0.8/core.d.ts", "default": "./dist/src/0.8/core.js" }, + "./v0_8": { + "types": "./dist/src/0.8/core.d.ts", + "default": "./dist/src/0.8/core.js" + }, + "./v0_9": { + "types": "./dist/src/v0_9/index.d.ts", + "default": "./dist/src/v0_9/index.js" + }, "./ui": { "types": "./dist/src/0.8/ui/ui.d.ts", "default": "./dist/src/0.8/ui/ui.js" @@ -89,6 +97,7 @@ "@lit/context": "^1.1.4", "lit": "^3.3.1", "signal-utils": "^0.21.1", - "@a2ui/web_core": "file:../web_core" + "@a2ui/web_core": "file:../web_core", + "zod": "^3.25.76" } } diff --git a/renderers/lit/src/0.8/model.test.ts b/renderers/lit/src/0.8/model.test.ts index 7703d11e7..a83aa9ca3 100644 --- a/renderers/lit/src/0.8/model.test.ts +++ b/renderers/lit/src/0.8/model.test.ts @@ -17,9 +17,9 @@ import assert from "node:assert"; import { describe, it, beforeEach } from "node:test"; -import { v0_8 } from "@a2ui/lit"; +import * as v0_8 from "@a2ui/lit/v0_8"; import * as Types from "@a2ui/web_core/types/types"; -import { A2uiStateError } from "@a2ui/web_core/v0_9"; +import { A2uiStateError } from "@a2ui/web_core/v0_8"; // Helper function to strip reactivity for clean comparisons. const toPlainObject = (value: unknown): ReturnType => { diff --git a/renderers/lit/src/index.ts b/renderers/lit/src/index.ts index b70862bd7..be38a8563 100644 --- a/renderers/lit/src/index.ts +++ b/renderers/lit/src/index.ts @@ -14,4 +14,8 @@ * limitations under the License. */ +/** + * @deprecated Import v0.8 from '@a2ui/lit/v0_8'. + * This entrypoint is maintained for backwards compatibility. + */ export * as v0_8 from "./0.8/index.js"; diff --git a/renderers/lit/src/v0_9/adapter.ts b/renderers/lit/src/v0_9/adapter.ts new file mode 100644 index 000000000..ee09339aa --- /dev/null +++ b/renderers/lit/src/v0_9/adapter.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ReactiveController, LitElement } from "lit"; +import { GenericBinder, ComponentContext, ComponentApi, ResolveA2uiProps, InferredComponentApiSchemaType } from "@a2ui/web_core/v0_9"; + +/** + * A Lit ReactiveController that binds an A2UI component context to its API schema. + * + * This controller manages the subscription to the GenericBinder, updating the + * component props and requesting a host update whenever the underlying layer data changes. + * + * @template Api The specific A2UI component API interface this controller is bound to. + */ +export class A2uiController implements ReactiveController { + /** + * The current reactive properties of the A2UI component, matching the expected output schema. + */ + public props: ResolveA2uiProps>; + private binder: GenericBinder>; + private subscription?: { unsubscribe: () => void }; + + /** + * Initializes the controller, binding it to the given Lit element and API schema. + * + * @param host The LitElement acting as the component host. Must provide a component `context`. + * @param api The A2UI component API defining the schema for this element. + */ + constructor(private host: LitElement & { context: ComponentContext }, api: Api) { + this.binder = new GenericBinder(this.host.context, api.schema); + this.props = this.binder.snapshot as ResolveA2uiProps>; + this.host.addController(this); + if (this.host.isConnected) { + this.hostConnected(); + } + } + + /** + * Subscribes to the GenericBinder updates when the host connects. + * + * Triggers a request update on the host element when new props are received. + */ + hostConnected() { + if (!this.subscription) { + this.subscription = this.binder.subscribe((newProps) => { + this.props = newProps as ResolveA2uiProps>; + this.host.requestUpdate(); + }); + } + } + + /** + * Unsubscribes from the GenericBinder updates when the host disconnects. + */ + hostDisconnected() { + this.subscription?.unsubscribe(); + this.subscription = undefined; + } + + /** + * Disposes the underlying GenericBinder to clean up resources from the context. + */ + dispose() { + this.binder.dispose(); + } +} diff --git a/renderers/lit/src/v0_9/base-element.ts b/renderers/lit/src/v0_9/base-element.ts new file mode 100644 index 000000000..c8ef9d95e --- /dev/null +++ b/renderers/lit/src/v0_9/base-element.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LitElement } from "lit"; +import { property } from "lit/decorators.js"; +import { ComponentContext, ComponentApi } from "@a2ui/web_core/v0_9"; +import { A2uiController } from "./adapter.js"; + +/** + * A base class for A2UI Lit elements that manages the A2uiController lifecycle. + * + * This element handles the reactive attachment and detachment of the `A2uiController` + * whenever the component's `context` changes. Subclasses only need to implement + * `createController` to provide their specific schema-bound controller, and `render` + * to define the template based on the controller's reactive props. + * + * @template Api The specific A2UI component API defining the schema for this element. + */ +export abstract class A2uiLitElement extends LitElement { + @property({ type: Object }) accessor context!: ComponentContext; + protected controller!: A2uiController; + + /** + * Instantiates the unique controller for this element's specific bound API. + * + * Subclasses must implement this method to return an `A2uiController` tied to + * their specific component `Api` definition. + * + * @returns A new instance of `A2uiController` matching the component API. + */ + protected abstract createController(): A2uiController; + + /** + * Reacts to changes in the component's properties. + * + * Specifically, when the `context` property changes or is initialized, this method + * cleans up any existing controller and invokes `createController()` to bind to + * the new context. + */ + willUpdate(changedProperties: Map) { + super.willUpdate(changedProperties); + if (changedProperties.has('context') && this.context) { + if (this.controller) { + this.removeController(this.controller); + this.controller.dispose(); + } + this.controller = this.createController(); + } + } +} diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts b/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts new file mode 100644 index 000000000..6ac114292 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/AudioPlayer.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { AudioPlayerApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-audioplayer") +export class A2uiAudioPlayerElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, AudioPlayerApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html` +
+ ${props.description ? html`

${props.description}

` : ""} + +
`; + } +} + +export const A2uiAudioPlayer = { + ...AudioPlayerApi, + tagName: "a2ui-audioplayer" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts new file mode 100644 index 000000000..c65888ede --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Button.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ButtonApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-basic-button") +export class A2uiBasicButtonElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ButtonApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const isDisabled = props.isValid === false; + + const classes = { + "a2ui-button": true, + ["a2ui-button-" + (props.variant || "default")]: true + }; + + return html` + + `; + } +} + +export const A2uiButton = { + ...ButtonApi, + tagName: "a2ui-basic-button" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts new file mode 100644 index 000000000..66f12e974 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Card.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { CardApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-card") +export class A2uiCardElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, CardApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html` +
+ ${props.child ? html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, props.child, this.context.dataContext.path))}` : ""} +
+ `; + } +} + +export const A2uiCard = { + ...CardApi, + tagName: "a2ui-card" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts b/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts new file mode 100644 index 000000000..89146ddc0 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/CheckBox.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { CheckBoxApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-checkbox") +export class A2uiCheckBoxElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, CheckBoxApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html` + + `; + } +} + +export const A2uiCheckBox = { + ...CheckBoxApi, + tagName: "a2ui-checkbox" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/ChoicePicker.ts b/renderers/lit/src/v0_9/catalogs/basic/components/ChoicePicker.ts new file mode 100644 index 000000000..89bd02d33 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/ChoicePicker.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ChoicePickerApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-choicepicker") +export class A2uiChoicePickerElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ChoicePickerApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const selected = Array.isArray(props.value) ? props.value : []; + const isMulti = props.variant === "multipleSelection"; + + const toggle = (val: string) => { + if (!props.setValue) return; + if (isMulti) { + if (selected.includes(val)) props.setValue(selected.filter((v: string) => v !== val)); + else props.setValue([...selected, val]); + } else { + props.setValue([val]); + } + }; + + return html` +
+ ${props.label ? html`` : ""} +
+ ${props.options?.map((opt: any) => html` + + `)} +
+
+ `; + } +} + +export const A2uiChoicePicker = { + ...ChoicePickerApi, + tagName: "a2ui-choicepicker" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Column.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Column.ts new file mode 100644 index 000000000..6fae63ec8 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Column.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ColumnApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-basic-column") +export class A2uiBasicColumnElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ColumnApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const children = Array.isArray(props.children) ? props.children : []; + const styles = { display: "flex", flexDirection: "column", flex: props.weight !== undefined ? String(props.weight) : "initial", gap: "8px" }; + + return html` +
)}> + ${map(children, (child: any) => { + if (typeof child === 'string') { + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child, this.context.dataContext.path))}`; + } + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child.id, child.basePath))}`; + })} +
+ `; + } +} + +export const A2uiColumn = { + ...ColumnApi, + tagName: "a2ui-basic-column" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/DateTimeInput.ts b/renderers/lit/src/v0_9/catalogs/basic/components/DateTimeInput.ts new file mode 100644 index 000000000..57570d5be --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/DateTimeInput.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { DateTimeInputApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-datetimeinput") +export class A2uiDateTimeInputElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, DateTimeInputApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const type = (props.enableDate && props.enableTime) ? "datetime-local" : (props.enableDate ? "date" : "time"); + return html` +
+ ${props.label ? html`` : ""} + props.setValue?.((e.target as HTMLInputElement).value)} + /> +
+ `; + } +} + +export const A2uiDateTimeInput = { + ...DateTimeInputApi, + tagName: "a2ui-datetimeinput" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Divider.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Divider.ts new file mode 100644 index 000000000..acaf46aa3 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Divider.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { DividerApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-divider") +export class A2uiDividerElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, DividerApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return props.axis === "vertical" + ? html`
` + : html`
`; + } +} + +export const A2uiDivider = { + ...DividerApi, + tagName: "a2ui-divider" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Icon.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Icon.ts new file mode 100644 index 000000000..e5b6be308 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Icon.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { IconApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-icon") +export class A2uiIconElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, IconApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const name = typeof props.name === 'string' ? props.name : (props.name as any)?.path; + return html`${name}`; + } +} + +export const A2uiIcon = { + ...IconApi, + tagName: "a2ui-icon" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Image.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Image.ts new file mode 100644 index 000000000..be60ea958 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Image.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ImageApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-image") +export class A2uiImageElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ImageApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const styles = { objectFit: props.fit || "fill", width: "100%" }; + return html``; + } +} + +export const A2uiImage = { + ...ImageApi, + tagName: "a2ui-image" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/List.ts b/renderers/lit/src/v0_9/catalogs/basic/components/List.ts new file mode 100644 index 000000000..9d5c80935 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/List.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ListApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-list") +export class A2uiListElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ListApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const children = Array.isArray(props.children) ? props.children : []; + const styles = { display: "flex", flexDirection: props.direction === "horizontal" ? "row" : "column", overflow: "auto", gap: "8px" }; + return html` +
)}> + ${map(children, (child: any) => { + if (typeof child === 'string') { + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child, this.context.dataContext.path))}`; + } + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child.id, child.basePath))}`; + })} +
+ `; + } +} + +export const A2uiList = { + ...ListApi, + tagName: "a2ui-list" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Modal.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Modal.ts new file mode 100644 index 000000000..edfa9f705 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Modal.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html , nothing} from "lit"; +import { customElement, query } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { ModalApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-modal") +export class A2uiLitModal extends A2uiLitElement { + protected createController() { return new A2uiController(this, ModalApi); } + @query("dialog") accessor dialog!: HTMLDialogElement; + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html` +
this.dialog?.showModal()}> + ${props.trigger ? html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, props.trigger, this.context.dataContext.path))}` : ''} +
+ +
+ ${props.content ? html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, props.content, this.context.dataContext.path))}` : ''} +
+ `; + } +} + +export const A2uiModal = { + ...ModalApi, + tagName: "a2ui-modal" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Row.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Row.ts new file mode 100644 index 000000000..e048536a2 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Row.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { RowApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-basic-row") +export class A2uiBasicRowElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, RowApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const children = Array.isArray(props.children) ? props.children : []; + const styles = { display: "flex", flexDirection: "row", flex: props.weight !== undefined ? String(props.weight) : "initial", gap: "8px" }; + + return html` +
)}> + ${map(children, (child: any) => { + if (typeof child === 'string') { + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child, this.context.dataContext.path))}`; + } + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child.id, child.basePath))}`; + })} +
+ `; + } +} + +export const A2uiRow = { + ...RowApi, + tagName: "a2ui-basic-row" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Slider.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Slider.ts new file mode 100644 index 000000000..5d3d8d255 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Slider.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { SliderApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-slider") +export class A2uiSliderElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, SliderApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html` +
+ ${props.label ? html`` : ""} + props.setValue?.(Number((e.target as HTMLInputElement).value))} + /> + ${props.value} +
+ `; + } +} + +export const A2uiSlider = { + ...SliderApi, + tagName: "a2ui-slider" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Tabs.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Tabs.ts new file mode 100644 index 000000000..cc72a552f --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Tabs.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html , nothing} from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { TabsApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-tabs") +export class A2uiLitTabs extends A2uiLitElement { + protected createController() { return new A2uiController(this, TabsApi); } + @state() accessor activeIndex = 0; + + render() { + const props = this.controller.props; + if (!props || !props.tabs) return nothing; + return html` +
+
+ ${props.tabs.map((tab: any, i: number) => html` + + `)} +
+
+ ${props.tabs[this.activeIndex] + ? html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, props.tabs[this.activeIndex].child, this.context.dataContext.path))}` + : ''} +
+
+ `; + } +} + +export const A2uiTabs = { + ...TabsApi, + tagName: "a2ui-tabs" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Text.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Text.ts new file mode 100644 index 000000000..b4082c650 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Text.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { TextApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-basic-text") +export class A2uiBasicTextElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, TextApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const variant = props.variant ?? "body"; + switch (variant) { + case "h1": return html`

${props.text}

`; + case "h2": return html`

${props.text}

`; + case "h3": return html`

${props.text}

`; + case "h4": return html`

${props.text}

`; + case "h5": return html`
${props.text}
`; + case "caption": return html`${props.text}`; + default: return html`

${props.text}

`; + } + } +} + +export const A2uiText = { + ...TextApi, + tagName: "a2ui-basic-text" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/TextField.ts b/renderers/lit/src/v0_9/catalogs/basic/components/TextField.ts new file mode 100644 index 000000000..eddd01929 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/TextField.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { TextFieldApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-basic-textfield") +export class A2uiBasicTextFieldElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, TextFieldApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const isInvalid = props.isValid === false; + const onInput = (e: Event) => props.setValue?.((e.target as HTMLInputElement).value); + let type = "text"; + if (props.variant === "number") type = "number"; + if (props.variant === "obscured") type = "password"; + + const classes = {"a2ui-textfield": true, "invalid": isInvalid}; + + return html` +
+ ${props.label ? html`` : ""} + ${props.variant === "longText" + ? html`` + : html`` + } + ${isInvalid && props.validationErrors?.length ? html`
${props.validationErrors[0]}
` : ""} +
+ `; + } +} + +export const A2uiTextField = { + ...TextFieldApi, + tagName: "a2ui-basic-textfield" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/components/Video.ts b/renderers/lit/src/v0_9/catalogs/basic/components/Video.ts new file mode 100644 index 000000000..f2bf6266a --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/components/Video.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { VideoApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-video") +export class A2uiVideoElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, VideoApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + return html``; + } +} + +export const A2uiVideo = { + ...VideoApi, + tagName: "a2ui-video" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/basic/index.ts b/renderers/lit/src/v0_9/catalogs/basic/index.ts new file mode 100644 index 000000000..c84889b7f --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/basic/index.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Catalog } from "@a2ui/web_core/v0_9"; +import { LitComponentImplementation } from "../../types.js"; +import { BASIC_FUNCTIONS } from "@a2ui/web_core/v0_9/basic_catalog"; + +import { A2uiText } from "./components/Text.js"; +import { A2uiButton } from "./components/Button.js"; +import { A2uiTextField } from "./components/TextField.js"; +import { A2uiRow } from "./components/Row.js"; +import { A2uiColumn } from "./components/Column.js"; +import { A2uiList } from "./components/List.js"; +import { A2uiImage } from "./components/Image.js"; +import { A2uiIcon } from "./components/Icon.js"; +import { A2uiVideo } from "./components/Video.js"; +import { A2uiAudioPlayer } from "./components/AudioPlayer.js"; +import { A2uiCard } from "./components/Card.js"; +import { A2uiDivider } from "./components/Divider.js"; +import { A2uiCheckBox } from "./components/CheckBox.js"; +import { A2uiSlider } from "./components/Slider.js"; +import { A2uiDateTimeInput } from "./components/DateTimeInput.js"; +import { A2uiChoicePicker } from "./components/ChoicePicker.js"; +import { A2uiTabs } from "./components/Tabs.js"; +import { A2uiModal } from "./components/Modal.js"; + +export const basicCatalog = new Catalog( + "https://a2ui.org/specification/v0_9/basic_catalog.json", + [ + A2uiText, A2uiButton, A2uiTextField, A2uiRow, A2uiColumn, A2uiList, A2uiImage, A2uiIcon, + A2uiVideo, A2uiAudioPlayer, A2uiCard, A2uiDivider, A2uiCheckBox, A2uiSlider, A2uiDateTimeInput, + A2uiChoicePicker, A2uiTabs, A2uiModal + ], + BASIC_FUNCTIONS as any +); diff --git a/renderers/lit/src/v0_9/catalogs/minimal/components/Button.ts b/renderers/lit/src/v0_9/catalogs/minimal/components/Button.ts new file mode 100644 index 000000000..59743588d --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/components/Button.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ButtonApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +@customElement("a2ui-button") +export class A2uiButtonElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ButtonApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const isDisabled = props.isValid === false; + + const onClick = () => { + if (!isDisabled && props.action) { + props.action(); + } + }; + + const classes = { + "a2ui-button": true, + "a2ui-button-primary": props.variant === "primary", + "a2ui-button-borderless": props.variant === "borderless", + }; + + return html` + + `; + } +} + +export const A2uiButton = { + ...ButtonApi, + tagName: "a2ui-button" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/components/Column.ts b/renderers/lit/src/v0_9/catalogs/minimal/components/Column.ts new file mode 100644 index 000000000..2e0c988d1 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/components/Column.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { ColumnApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +function mapJustify(justify: string | undefined): string { + switch (justify) { + case "start": return "flex-start"; + case "center": return "center"; + case "end": return "flex-end"; + case "spaceBetween": return "space-between"; + case "spaceAround": return "space-around"; + case "spaceEvenly": return "space-evenly"; + case "stretch": return "stretch"; + default: return "flex-start"; + } +} + +function mapAlign(align: string | undefined): string { + switch (align) { + case "start": return "flex-start"; + case "center": return "center"; + case "end": return "flex-end"; + case "stretch": return "stretch"; + default: return "stretch"; + } +} + +@customElement("a2ui-column") +export class A2uiColumnElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, ColumnApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const childrenArray = Array.isArray(props.children) ? props.children : []; + + const styles = { + display: "flex", + flexDirection: "column", + justifyContent: mapJustify(props.justify), + alignItems: mapAlign(props.align), + flex: props.weight !== undefined ? String(props.weight) : "initial", + }; + + return html` +
)}> + ${map(childrenArray, (child: any) => { + if (typeof child === 'string') { + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child, this.context.dataContext.path))}`; + } + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child.id, child.basePath))}`; + })} +
+ `; + } +} + +export const A2uiColumn = { + ...ColumnApi, + tagName: "a2ui-column" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/components/Row.ts b/renderers/lit/src/v0_9/catalogs/minimal/components/Row.ts new file mode 100644 index 000000000..cb88c6825 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/components/Row.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; +import { styleMap } from "lit/directives/style-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { RowApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "../../../surface/render-node.js"; + +function mapJustify(justify: string | undefined): string { + switch (justify) { + case "start": return "flex-start"; + case "center": return "center"; + case "end": return "flex-end"; + case "spaceBetween": return "space-between"; + case "spaceAround": return "space-around"; + case "spaceEvenly": return "space-evenly"; + case "stretch": return "stretch"; + default: return "flex-start"; + } +} + +function mapAlign(align: string | undefined): string { + switch (align) { + case "start": return "flex-start"; + case "center": return "center"; + case "end": return "flex-end"; + case "stretch": return "stretch"; + default: return "stretch"; + } +} + +@customElement("a2ui-row") +export class A2uiRowElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, RowApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const childrenArray = Array.isArray(props.children) ? props.children : []; + + const styles = { + display: "flex", + flexDirection: "row", + justifyContent: mapJustify(props.justify), + alignItems: mapAlign(props.align), + flex: props.weight !== undefined ? String(props.weight) : "initial", + }; + + return html` +
)}> + ${map(childrenArray, (child: any) => { + if (typeof child === 'string') { + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child, this.context.dataContext.path))}`; + } + return html`${renderA2uiNode(new ComponentContext(this.context.dataContext.surface, child.id, child.basePath))}`; + })} +
+ `; + } +} + +export const A2uiRow = { + ...RowApi, + tagName: "a2ui-row" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/components/Text.ts b/renderers/lit/src/v0_9/catalogs/minimal/components/Text.ts new file mode 100644 index 000000000..4c4c63663 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/components/Text.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { TextApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-text") +export class A2uiTextElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, TextApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const variant = props.variant ?? "body"; + + switch (variant) { + case "h1": return html`

${props.text}

`; + case "h2": return html`

${props.text}

`; + case "h3": return html`

${props.text}

`; + case "h4": return html`

${props.text}

`; + case "h5": return html`
${props.text}
`; + case "caption": return html`${props.text}`; + default: return html`

${props.text}

`; + } + } +} + +export const A2uiText = { + ...TextApi, + tagName: "a2ui-text" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/components/TextField.ts b/renderers/lit/src/v0_9/catalogs/minimal/components/TextField.ts new file mode 100644 index 000000000..6b0836538 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/components/TextField.ts @@ -0,0 +1,83 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing} from "lit"; +import { customElement } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; +import { A2uiLitElement } from "../../../base-element.js"; +import { A2uiController } from "../../../adapter.js"; +import { TextFieldApi } from "@a2ui/web_core/v0_9/basic_catalog"; + +@customElement("a2ui-textfield") +export class A2uiTextFieldElement extends A2uiLitElement { + protected createController() { return new A2uiController(this, TextFieldApi); } + + render() { + const props = this.controller.props; + if (!props) return nothing; + + const isInvalid = props.isValid === false; + + const onInput = (e: Event) => { + const target = e.target as HTMLInputElement; + if (props.setValue) { + props.setValue(target.value); + } + }; + + const classes = { + "a2ui-textfield": true, + "a2ui-textfield-invalid": isInvalid, + }; + + let type = "text"; + if (props.variant === "number") type = "number"; + if (props.variant === "obscured") type = "password"; + + return html` +
+ ${props.label ? html`` : ""} + + ${props.variant === "longText" + ? html` + ` + : html` + ` + } + + ${isInvalid && props.validationErrors && props.validationErrors.length > 0 + ? html`
${props.validationErrors[0]}
` + : ""} +
+ `; + } +} + +export const A2uiTextField = { + ...TextFieldApi, + tagName: "a2ui-textfield" +}; \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/functions/capitalize.ts b/renderers/lit/src/v0_9/catalogs/minimal/functions/capitalize.ts new file mode 100644 index 000000000..e9b2d02d5 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/functions/capitalize.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { z } from "zod"; +import { createFunctionImplementation } from "@a2ui/web_core/v0_9"; + +export const CapitalizeApi = { + name: "capitalize" as const, + returnType: "string" as const, + schema: z.object({ + value: z.preprocess(v => v === undefined ? undefined : String(v), z.string()).optional() + }) as z.ZodType +}; + +export const CapitalizeImplementation = createFunctionImplementation( + CapitalizeApi as any, + (args) => { + if (!args.value) return ""; + return args.value.charAt(0).toUpperCase() + args.value.slice(1); + } +); \ No newline at end of file diff --git a/renderers/lit/src/v0_9/catalogs/minimal/index.ts b/renderers/lit/src/v0_9/catalogs/minimal/index.ts new file mode 100644 index 000000000..6c7321fd5 --- /dev/null +++ b/renderers/lit/src/v0_9/catalogs/minimal/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Catalog } from "@a2ui/web_core/v0_9"; +import { LitComponentImplementation } from "../../types.js"; +import { A2uiText } from "./components/Text.js"; +import { A2uiButton } from "./components/Button.js"; +import { A2uiTextField } from "./components/TextField.js"; +import { A2uiRow } from "./components/Row.js"; +import { A2uiColumn } from "./components/Column.js"; +import { CapitalizeImplementation } from "./functions/capitalize.js"; + +export const minimalCatalog = new Catalog( + "https://a2ui.org/specification/v0_9/catalogs/minimal/minimal_catalog.json", + [A2uiText, A2uiButton, A2uiTextField, A2uiRow, A2uiColumn], + [CapitalizeImplementation] +); diff --git a/renderers/lit/src/v0_9/index.ts b/renderers/lit/src/v0_9/index.ts new file mode 100644 index 000000000..58ede475c --- /dev/null +++ b/renderers/lit/src/v0_9/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from "./types.js"; +export * from "./adapter.js"; +export * from "./surface/A2uiSurface.js"; +export * from "./surface/render-node.js"; +export * from "./catalogs/minimal/index.js"; +export * from "./catalogs/basic/index.js"; diff --git a/renderers/lit/src/v0_9/surface/A2uiSurface.ts b/renderers/lit/src/v0_9/surface/A2uiSurface.ts new file mode 100644 index 000000000..eaba6d6e2 --- /dev/null +++ b/renderers/lit/src/v0_9/surface/A2uiSurface.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { html, nothing, LitElement, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { SurfaceModel, ComponentContext } from "@a2ui/web_core/v0_9"; +import { renderA2uiNode } from "./render-node.js"; + +@customElement("a2ui-surface") +export class A2uiSurface extends LitElement { + @property({ type: Object }) accessor surface: SurfaceModel | undefined = undefined; + + @state() accessor _hasRoot = false; + private unsub?: () => void; + + protected willUpdate(changedProperties: PropertyValues) { + if (changedProperties.has('surface')) { + if (this.unsub) { + this.unsub(); + this.unsub = undefined; + } + this._hasRoot = !!this.surface?.componentsModel.get("root"); + + if (this.surface && !this._hasRoot) { + const sub = this.surface.componentsModel.onCreated.subscribe((comp) => { + if (comp.id === "root") { + this._hasRoot = true; + this.unsub?.(); + this.unsub = undefined; + } + }); + this.unsub = () => sub.unsubscribe(); + } + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (this.unsub) { + this.unsub(); + this.unsub = undefined; + } + } + + render() { + if (!this.surface) return nothing; + if (!this._hasRoot) { + return html`
Loading surface...
`; + } + + try { + const rootContext = new ComponentContext(this.surface, "root", "/"); + return html`${renderA2uiNode(rootContext)}`; + } catch (e) { + console.error("Error creating root context:", e); + return html`
Error rendering surface
`; + } + } +} diff --git a/renderers/lit/src/v0_9/surface/render-node.ts b/renderers/lit/src/v0_9/surface/render-node.ts new file mode 100644 index 000000000..d4bafbfee --- /dev/null +++ b/renderers/lit/src/v0_9/surface/render-node.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { nothing } from "lit"; +import { html, unsafeStatic } from "lit/static-html.js"; +import { ComponentContext } from "@a2ui/web_core/v0_9"; +import { LitComponentImplementation } from "../types.js"; + +/** + * Pure function that acts as a generic container for A2UI components. + * + * It dynamically resolves and renders the specific Lit component implementation + * based on the component type provided in the context, returning a TemplateResult directly + * to avoid duplicate DOM node wrapping. + * + * @param context The component context defining the data model and type to render. + * @returns A Lit TemplateResult representing the resolved component, or `nothing` if the component is invalid or unresolvable. + * + * For examples on how to use `renderA2uiNode` when defining custom A2UI components that contain children, + * refer to the implementation of `A2uiSurface`, or structural catalog components like `Row` and `Column`. + */ +export function renderA2uiNode(context: ComponentContext) { + const type = context.componentModel.type; + const catalog = context.dataContext.surface.catalog; + const implementation = catalog.components.get(type) as LitComponentImplementation | undefined; + + if (!implementation) { + console.warn(`Component implementation not found for type: ${type}`); + return nothing; + } + + const tag = unsafeStatic(implementation.tagName); + return html`<${tag} .context=${context}>`; +} diff --git a/renderers/lit/src/v0_9/tests/integration.test.ts b/renderers/lit/src/v0_9/tests/integration.test.ts new file mode 100644 index 000000000..365090d4c --- /dev/null +++ b/renderers/lit/src/v0_9/tests/integration.test.ts @@ -0,0 +1,70 @@ +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { MessageProcessor, GenericBinder, ComponentContext } from "@a2ui/web_core/v0_9"; +import { minimalCatalog } from "../catalogs/minimal/index.js"; +import { TextApi } from "@a2ui/web_core/v0_9/basic_catalog"; +import fs from "fs"; +import path from "path"; + +describe("v0.9 Minimal Catalog Examples", () => { + const examplesDir = path.resolve( + process.cwd(), + "../../specification/v0_9/json/catalogs/minimal/examples" + ); + + const files = fs.readdirSync(examplesDir).filter((f) => f.endsWith(".json")); + + for (const file of files) { + it(`should successfully process ${file}`, async () => { + const content = fs.readFileSync(path.join(examplesDir, file), "utf-8"); + const data = JSON.parse(content); + const messages = Array.isArray(data) ? data : data.messages || []; + + let surfaceId = file.replace(".json", ""); + const createMsg = messages.find((m: any) => m.createSurface); + if (createMsg) { + surfaceId = createMsg.createSurface.surfaceId; + } else { + messages.unshift({ + version: "v0.9", + createSurface: { + surfaceId, + catalogId: minimalCatalog.id, + }, + }); + } + + const processor = new MessageProcessor([minimalCatalog]); + + processor.processMessages(messages); + + const surface = processor.model.getSurface(surfaceId); + assert.ok(surface, `Surface ${surfaceId} should exist`); + + const rootNode = surface.componentsModel.get("root"); + assert.ok(rootNode, "Surface should have a root component"); + + if (file.includes("capitalized_text")) { + const textNode = surface.componentsModel.get("result_text"); + assert.ok(textNode); + + const context = new ComponentContext(surface, "result_text"); + const binder = new GenericBinder(context, TextApi.schema); + const sub = binder.subscribe(() => {}); // Force connection + + // Wait a microtask to let initial resolution finish + await new Promise((r) => setTimeout(r, 0)); + assert.strictEqual(binder.snapshot.text, ""); + + // 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"); + + sub.unsubscribe(); + binder.dispose(); + } + }); + } +}); \ No newline at end of file diff --git a/renderers/lit/src/v0_9/types.ts b/renderers/lit/src/v0_9/types.ts new file mode 100644 index 000000000..95e98c507 --- /dev/null +++ b/renderers/lit/src/v0_9/types.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentApi } from "@a2ui/web_core/v0_9"; + +/** + * Interface representing an A2UI component implementation in Lit. + * + * Extends the framework-agnostic component API to include the Lit custom element tag name. + * Used by A2uiNode to dynamically render the corresponding Lit component, and as the type parameter when defining custom Catalogs. + */ +export interface LitComponentImplementation extends ComponentApi { + tagName: string; +} diff --git a/renderers/web_core/src/v0_9/basic_catalog/components/basic_components.ts b/renderers/web_core/src/v0_9/basic_catalog/components/basic_components.ts index c846556f6..4eb628368 100644 --- a/renderers/web_core/src/v0_9/basic_catalog/components/basic_components.ts +++ b/renderers/web_core/src/v0_9/basic_catalog/components/basic_components.ts @@ -33,16 +33,16 @@ const CommonProps = { weight: z.number().describe("The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column.").optional(), }; -export const TextApi: ComponentApi = { +export const TextApi = { name: "Text", schema: z.object({ ...CommonProps, text: DynamicStringSchema.describe("The text content to display. While simple Markdown formatting is supported (i.e. without HTML, images, or links), utilizing dedicated UI components is generally preferred for a richer and more structured presentation."), variant: z.enum(["h1", "h2", "h3", "h4", "h5", "caption", "body"]).default("body").describe("A hint for the base text style.").optional(), }).strict(), -}; +} satisfies ComponentApi; -export const ImageApi: ComponentApi = { +export const ImageApi = { name: "Image", schema: z.object({ ...CommonProps, @@ -57,7 +57,7 @@ export const ImageApi: ComponentApi = { "header", ]).default("mediumFeature").describe("A hint for the image size and style.").optional(), }).strict(), -}; +} satisfies ComponentApi; const ICON_NAMES = [ "accountCircle", "add", "arrowBack", "arrowForward", "attachFile", @@ -73,7 +73,7 @@ const ICON_NAMES = [ "volumeOff", "volumeUp", "warning" ] as const; -export const IconApi: ComponentApi = { +export const IconApi = { name: "Icon", schema: z.object({ ...CommonProps, @@ -84,26 +84,26 @@ export const IconApi: ComponentApi = { }).strict(), ]).describe("The name of the icon to display."), }).strict(), -}; +} satisfies ComponentApi; -export const VideoApi: ComponentApi = { +export const VideoApi = { name: "Video", schema: z.object({ ...CommonProps, url: DynamicStringSchema.describe("The URL of the video to display."), }).strict(), -}; +} satisfies ComponentApi; -export const AudioPlayerApi: ComponentApi = { +export const AudioPlayerApi = { name: "AudioPlayer", schema: z.object({ ...CommonProps, url: DynamicStringSchema.describe("The URL of the audio to be played."), description: DynamicStringSchema.describe("A description of the audio, such as a title or summary.").optional(), }).strict(), -}; +} satisfies ComponentApi; -export const RowApi: ComponentApi = { +export const RowApi = { name: "Row", schema: z.object({ ...CommonProps, @@ -119,9 +119,9 @@ export const RowApi: ComponentApi = { ]).default("start").describe("Defines the arrangement of children along the main axis (horizontally). Use 'spaceBetween' to push items to the edges, or 'start'/'end'/'center' to pack them together.").optional(), align: z.enum(["start", "center", "end", "stretch"]).default("stretch").describe("Defines the alignment of children along the cross axis (vertically). This is similar to the CSS 'align-items' property, but uses camelCase values (e.g., 'start').").optional(), }).strict().describe("A layout component that arranges its children horizontally. To create a grid layout, nest Columns within this Row."), -}; +} satisfies ComponentApi; -export const ColumnApi: ComponentApi = { +export const ColumnApi = { name: "Column", schema: z.object({ ...CommonProps, @@ -137,9 +137,9 @@ export const ColumnApi: ComponentApi = { ]).default("start").describe("Defines the arrangement of children along the main axis (vertically). Use 'spaceBetween' to push items to the edges (e.g. header at top, footer at bottom), or 'start'/'end'/'center' to pack them together.").optional(), align: z.enum(["center", "end", "start", "stretch"]).default("stretch").describe("Defines the alignment of children along the cross axis (horizontally). This is similar to the CSS 'align-items' property.").optional(), }).strict().describe("A layout component that arranges its children vertically. To create a grid layout, nest Rows within this Column."), -}; +} satisfies ComponentApi; -export const ListApi: ComponentApi = { +export const ListApi = { name: "List", schema: z.object({ ...CommonProps, @@ -147,17 +147,17 @@ export const ListApi: ComponentApi = { direction: z.enum(["vertical", "horizontal"]).default("vertical").describe("The direction in which the list items are laid out.").optional(), align: z.enum(["start", "center", "end", "stretch"]).default("stretch").describe("Defines the alignment of children along the cross axis.").optional(), }).strict(), -}; +} satisfies ComponentApi; -export const CardApi: ComponentApi = { +export const CardApi = { name: "Card", schema: z.object({ ...CommonProps, child: ComponentIdSchema.describe("The ID of the single child component to be rendered inside the card. To display multiple elements, you MUST wrap them in a layout component (like Column or Row) and pass that container's ID here. Do NOT pass multiple IDs or a non-existent ID. Do NOT define the child component inline."), }).strict(), -}; +} satisfies ComponentApi; -export const TabsApi: ComponentApi = { +export const TabsApi = { name: "Tabs", schema: z.object({ ...CommonProps, @@ -168,26 +168,26 @@ export const TabsApi: ComponentApi = { }).strict() ).min(1).describe("An array of objects, where each object defines a tab with a title and a child component."), }).strict(), -}; +} satisfies ComponentApi; -export const ModalApi: ComponentApi = { +export const ModalApi = { name: "Modal", schema: z.object({ ...CommonProps, trigger: ComponentIdSchema.describe("The ID of the component that opens the modal when interacted with (e.g., a button). Do NOT define the component inline."), content: ComponentIdSchema.describe("The ID of the component to be displayed inside the modal. Do NOT define the component inline."), }).strict(), -}; +} satisfies ComponentApi; -export const DividerApi: ComponentApi = { +export const DividerApi = { name: "Divider", schema: z.object({ ...CommonProps, axis: z.enum(["horizontal", "vertical"]).default("horizontal").describe("The orientation of the divider.").optional(), }).strict(), -}; +} satisfies ComponentApi; -export const ButtonApi: ComponentApi = { +export const ButtonApi = { name: "Button", schema: z.object({ ...CommonProps, @@ -196,9 +196,9 @@ export const ButtonApi: ComponentApi = { action: ActionSchema, checks: CheckableSchema.shape.checks, }).strict(), -}; +} satisfies ComponentApi; -export const TextFieldApi: ComponentApi = { +export const TextFieldApi = { name: "TextField", schema: z.object({ ...CommonProps, @@ -208,9 +208,9 @@ export const TextFieldApi: ComponentApi = { validationRegexp: z.string().describe("A regular expression used for client-side validation of the input.").optional(), checks: CheckableSchema.shape.checks, }).strict(), -}; +} satisfies ComponentApi; -export const CheckBoxApi: ComponentApi = { +export const CheckBoxApi = { name: "CheckBox", schema: z.object({ ...CommonProps, @@ -218,9 +218,9 @@ export const CheckBoxApi: ComponentApi = { value: DynamicBooleanSchema.describe("The current state of the checkbox (true for checked, false for unchecked)."), checks: CheckableSchema.shape.checks, }).strict(), -}; +} satisfies ComponentApi; -export const ChoicePickerApi: ComponentApi = { +export const ChoicePickerApi = { name: "ChoicePicker", schema: z.object({ ...CommonProps, @@ -237,9 +237,9 @@ export const ChoicePickerApi: ComponentApi = { filterable: z.boolean().default(false).describe("If true, displays a search input to filter the options.").optional(), checks: CheckableSchema.shape.checks, }).strict().describe("A component that allows selecting one or more options from a list."), -}; +} satisfies ComponentApi; -export const SliderApi: ComponentApi = { +export const SliderApi = { name: "Slider", schema: z.object({ ...CommonProps, @@ -249,9 +249,9 @@ export const SliderApi: ComponentApi = { value: DynamicNumberSchema.describe("The current value of the slider."), checks: CheckableSchema.shape.checks, }).strict(), -}; +} satisfies ComponentApi; -export const DateTimeInputApi: ComponentApi = { +export const DateTimeInputApi = { name: "DateTimeInput", schema: z.object({ ...CommonProps, @@ -273,7 +273,7 @@ export const DateTimeInputApi: ComponentApi = { label: DynamicStringSchema.describe("The text label for the input field.").optional(), checks: CheckableSchema.shape.checks, }).strict(), -}; +} satisfies ComponentApi; export const BASIC_COMPONENTS: ComponentApi[] = [ TextApi, diff --git a/renderers/web_core/src/v0_9/catalog/types.ts b/renderers/web_core/src/v0_9/catalog/types.ts index 3700fc6ee..668431f79 100644 --- a/renderers/web_core/src/v0_9/catalog/types.ts +++ b/renderers/web_core/src/v0_9/catalog/types.ts @@ -83,7 +83,7 @@ import { FunctionInvoker } from "./function_invoker.js"; * This interface defines the contract for a component's capabilities and properties, * independent of any specific rendering implementation. */ -export interface ComponentApi { +export interface ComponentApi { /** The name of the component as it appears in the A2UI JSON (e.g., 'Button'). */ name: string; @@ -93,9 +93,11 @@ export interface ComponentApi { * - MUST include catalog-specific common properties (e.g. 'weight', 'accessibility'). * - MUST NOT include 'component' or 'id' as those are handled by the framework/envelope. */ - readonly schema: z.ZodType; + readonly schema: Schema; } +export type InferredComponentApiSchemaType = z.infer; + /** * A collection of available components and functions. */ diff --git a/samples/client/lit/gallery_v0_9/.gitignore b/samples/client/lit/gallery_v0_9/.gitignore new file mode 100644 index 000000000..f8ebe148f --- /dev/null +++ b/samples/client/lit/gallery_v0_9/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +public/specs diff --git a/samples/client/lit/gallery_v0_9/README.md b/samples/client/lit/gallery_v0_9/README.md new file mode 100644 index 000000000..cbdaf4ffc --- /dev/null +++ b/samples/client/lit/gallery_v0_9/README.md @@ -0,0 +1,55 @@ +# A2UI Local Gallery (Minimal v0.8) + +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. + +## Prerequisites + +Before running this gallery, you **must** build the shared A2UI renderers. + +### 1. Build Shared Renderers + +Navigate to the project root and run: + +```bash +# Build Web Core +cd renderers/web_core +npm install +npm run build + +# Build Lit Renderer +cd ../lit +npm install +npm run build +``` + +For more details on building the renderers, see: +- [Web Core README](../../../../renderers/web_core/README.md) +- [Lit Renderer README](../../../../renderers/lit/README.md) + +## Getting Started + +1. **Navigate to this directory**: + ```bash + cd samples/client/lit/local_gallery + ``` + +2. **Install dependencies**: + ```bash + npm install + ``` + +3. **Run the development server**: + ```bash + npm run 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. + - 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. +- **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/samples/client/lit/gallery_v0_9/index.html b/samples/client/lit/gallery_v0_9/index.html new file mode 100644 index 000000000..9ac3b2c0e --- /dev/null +++ b/samples/client/lit/gallery_v0_9/index.html @@ -0,0 +1,41 @@ + + + + + + + + A2UI Local Gallery (Minimal v0.9) + + + + + + + + + + + diff --git a/samples/client/lit/gallery_v0_9/package-lock.json b/samples/client/lit/gallery_v0_9/package-lock.json new file mode 100644 index 000000000..cc343a2cf --- /dev/null +++ b/samples/client/lit/gallery_v0_9/package-lock.json @@ -0,0 +1,1200 @@ +{ + "name": "@a2ui/local-gallery-v09", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@a2ui/local-gallery-v09", + "version": "0.1.0", + "dependencies": { + "@a2ui/lit": "file:../../../../renderers/lit", + "@a2ui/markdown-it": "file:../../../../renderers/markdown/markdown-it", + "@a2ui/web_core": "file:../../../../renderers/web_core", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1" + }, + "devDependencies": { + "typescript": "^5.8.3", + "vite": "^7.1.11" + } + }, + "../../../../renderers/lit": { + "name": "@a2ui/lit", + "version": "0.8.2", + "license": "Apache-2.0", + "dependencies": { + "@a2ui/web_core": "file:../web_core", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1", + "signal-utils": "^0.21.1" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "google-artifactregistry-auth": "^3.5.0", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "../../../../renderers/markdown/markdown-it": { + "name": "@a2ui/markdown-it", + "version": "0.0.2", + "license": "Apache-2.0", + "dependencies": { + "dompurify": "^3.3.1", + "markdown-it": "^14.1.0" + }, + "devDependencies": { + "@a2ui/web_core": "file:../../web_core", + "@types/dompurify": "^3.0.5", + "@types/jsdom": "^28.0.0", + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "jsdom": "^28.1.0", + "prettier": "^3.4.2", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + }, + "peerDependencies": { + "@a2ui/web_core": "file:../../web_core" + } + }, + "../../../../renderers/web_core": { + "name": "@a2ui/web_core", + "version": "0.8.6", + "license": "Apache-2.0", + "dependencies": { + "@preact/signals-core": "^1.13.0", + "date-fns": "^4.1.0", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.25.1" + }, + "devDependencies": { + "@types/node": "^24.11.0", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "node_modules/@a2ui/lit": { + "resolved": "../../../../renderers/lit", + "link": true + }, + "node_modules/@a2ui/markdown-it": { + "resolved": "../../../../renderers/markdown/markdown-it", + "link": true + }, + "node_modules/@a2ui/web_core": { + "resolved": "../../../../renderers/web_core", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@lit-labs/signals": { + "version": "0.1.3", + "integrity": "sha512-P0yWgH5blwVyEwBg+WFspLzeu1i0ypJP1QB0l1Omr9qZLIPsUu0p4Fy2jshOg7oQyha5n163K3GJGeUhQQ682Q==", + "license": "BSD-3-Clause", + "dependencies": { + "lit": "^2.0.0 || ^3.0.0", + "signal-polyfill": "^0.2.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.5.1", + "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/context": { + "version": "1.1.6", + "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.2 || ^2.1.0" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.3", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lit": { + "version": "3.3.2", + "integrity": "sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.2", + "integrity": "sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/signal-polyfill": { + "version": "0.2.2", + "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", + "license": "Apache-2.0" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/samples/client/lit/gallery_v0_9/package.json b/samples/client/lit/gallery_v0_9/package.json new file mode 100644 index 000000000..e012ef8b5 --- /dev/null +++ b/samples/client/lit/gallery_v0_9/package.json @@ -0,0 +1,25 @@ +{ + "name": "@a2ui/local-gallery-v09", + "private": true, + "version": "0.1.0", + "description": "A2UI Local Gallery (Agentless)", + "type": "module", + "scripts": { + "sync-specs": "node scripts/sync-specs.js", + "dev": "npm run sync-specs && vite", + "build": "npm run sync-specs && tsc && vite build", + "serve": "vite preview" + }, + "dependencies": { + "@a2ui/lit": "file:../../../../renderers/lit", + "@a2ui/web_core": "file:../../../../renderers/web_core", + "@a2ui/markdown-it": "file:../../../../renderers/markdown/markdown-it", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "lit": "^3.3.1" + }, + "devDependencies": { + "typescript": "^5.8.3", + "vite": "^7.1.11" + } +} diff --git a/samples/client/lit/gallery_v0_9/scripts/sync-specs.js b/samples/client/lit/gallery_v0_9/scripts/sync-specs.js new file mode 100644 index 000000000..538635385 --- /dev/null +++ b/samples/client/lit/gallery_v0_9/scripts/sync-specs.js @@ -0,0 +1,51 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +function syncDir(sourceDir, targetDir) { + console.log(`Syncing specs from ${sourceDir} to ${targetDir}...`); + fs.mkdirSync(targetDir, { recursive: true }); + + const files = fs.readdirSync(sourceDir).filter(f => f.endsWith('.json')); + + files.forEach(f => { + const sourcePath = path.join(sourceDir, f); + const targetPath = path.join(targetDir, f); + fs.copyFileSync(sourcePath, targetPath); + console.log(` Copied ${f}`); + }); + + const indexFiles = fs.readdirSync(targetDir).filter(f => f.endsWith('.json') && f !== 'index.json'); + fs.writeFileSync(path.join(targetDir, 'index.json'), JSON.stringify(indexFiles, null, 2)); + + console.log(`Generated manifest for ${indexFiles.length} files.`); +} + +syncDir( + path.resolve(process.cwd(), '../../../../specification/v0_9/json/catalogs/minimal/examples'), + path.resolve(process.cwd(), 'public/specs/v0_9/minimal/examples') +); + +syncDir( + path.resolve(process.cwd(), '../../../../specification/v0_9/json/catalogs/basic/examples'), + path.resolve(process.cwd(), 'public/specs/v0_9/basic/examples') +); \ No newline at end of file diff --git a/samples/client/lit/gallery_v0_9/src/local-gallery.ts b/samples/client/lit/gallery_v0_9/src/local-gallery.ts new file mode 100644 index 000000000..358536190 --- /dev/null +++ b/samples/client/lit/gallery_v0_9/src/local-gallery.ts @@ -0,0 +1,374 @@ +import { LitElement, html, css, nothing, PropertyValues } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { MessageProcessor } from "@a2ui/web_core/v0_9"; +import { minimalCatalog, basicCatalog } from "@a2ui/lit/v0_9"; +// Try avoiding direct deep import if A2uiMessage is not exported at the top level, using any for now as this is just a type for the array of messages +interface DemoItem { + id: string; + title: string; + filename: string; + description: string; + messages: any[]; + isBasic?: boolean; +} + +@customElement("local-gallery") +export class LocalGallery extends LitElement { + @state() accessor mockLogs: string[] = []; + @state() accessor demoItems: DemoItem[] = []; + @state() accessor activeItemIndex = 0; + @state() accessor processedMessageCount = 0; + @state() accessor currentDataModelText = "{}"; + + private processor = new MessageProcessor([minimalCatalog, basicCatalog], (action: any) => { + this.log(`Action dispatched: ${action.surfaceId}`, action); + }); + + + private dataModelSubscription?: { unsubscribe: () => void }; + + static styles = [ + css` + :host { + display: flex; + flex-direction: column; + height: 100vh; + width: 100vw; + overflow: hidden; + background: #0f172a; + color: #f1f5f9; + font-family: system-ui, sans-serif; + } + + header { + padding: 16px 24px; + background: rgba(15, 23, 42, 0.8); + border-bottom: 1px solid rgba(148, 163, 184, 0.1); + display: flex; + justify-content: space-between; + align-items: center; + flex-shrink: 0; + } + + h1 { margin: 0; font-size: 1.5rem; } + p.subtitle { color: #94a3b8; margin: 4px 0 0 0; font-size: 0.9rem; } + + main { + flex: 1; + display: flex; + overflow: hidden; + } + + .nav-pane { + width: 250px; + background: #1e293b; + border-right: 1px solid rgba(148, 163, 184, 0.1); + display: flex; + flex-direction: column; + overflow-y: auto; + } + + .nav-item { + padding: 16px; + cursor: pointer; + border-bottom: 1px solid rgba(148, 163, 184, 0.05); + transition: background 0.2s; + } + + .nav-item:hover { background: rgba(255, 255, 255, 0.05); } + .nav-item.active { + background: rgba(56, 189, 248, 0.1); + border-left: 4px solid #38bdf8; + } + + .nav-title { margin: 0 0 4px 0; font-size: 0.95rem; font-weight: 500; } + .nav-desc { margin: 0; font-size: 0.8rem; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;} + + .gallery-pane { + flex: 1; + display: flex; + flex-direction: column; + background: #0f172a; + overflow: hidden; + } + + .preview-header { + padding: 16px; + background: #1e293b; + border-bottom: 1px solid rgba(148, 163, 184, 0.1); + display: flex; + justify-content: space-between; + align-items: center; + } + + .stepper-controls { + display: flex; + gap: 8px; + align-items: center; + } + + button { + background: #38bdf8; + color: #0f172a; + border: none; + padding: 6px 12px; + border-radius: 4px; + font-weight: 600; + cursor: pointer; + } + button:hover { background: #7dd3fc; } + button:disabled { background: #475569; color: #94a3b8; cursor: not-allowed; } + + .preview-content { + flex: 1; + padding: 24px; + overflow-y: auto; + display: flex; + justify-content: center; + } + + .surface-container { + width: 100%; + max-width: 600px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(148, 163, 184, 0.2); + border-radius: 8px; + padding: 24px; + } + + .inspector-pane { + width: 400px; + display: flex; + flex-direction: column; + border-left: 1px solid rgba(148, 163, 184, 0.1); + background: #020617; + } + + .inspector-section { + flex: 1; + display: flex; + flex-direction: column; + border-bottom: 1px solid rgba(148, 163, 184, 0.1); + overflow: hidden; + } + + .inspector-header { + padding: 12px 16px; + background: #1e293b; + font-weight: bold; + font-size: 0.8rem; + text-transform: uppercase; + color: #94a3b8; + } + + .inspector-body { + flex: 1; + overflow-y: auto; + padding: 16px; + font-family: 'JetBrains Mono', monospace; + font-size: 0.8rem; + white-space: pre-wrap; + } + + .log-list { + display: flex; + flex-direction: column-reverse; + gap: 8px; + } + + .log-entry { + padding: 8px; + background: rgba(255, 255, 255, 0.03); + border-radius: 4px; + border-left: 2px solid #38bdf8; + } + ` + ]; + + async connectedCallback() { + super.connectedCallback(); + + this.processor.model.onSurfaceCreated.subscribe((surface: any) => { + surface.onError.subscribe((err: any) => { + this.log(`Error on surface ${surface.id}: ${err.message}`, err); + }); + }); + + await this.loadExamples(); + } + + async loadExamples() { + try { + const items: DemoItem[] = []; + await this.fetchExamplesFrom('./specs/v0_9/minimal/examples', items, false); + await this.fetchExamplesFrom('./specs/v0_9/basic/examples', items, true); + + this.demoItems = items; + if (items.length > 0) { + this.selectItem(0); + } + } catch (err) { + console.error(`Failed to initiate gallery:`, err); + } + } + + async fetchExamplesFrom(dir: string, items: DemoItem[], isBasic: boolean) { + try { + const indexResp = await fetch(`${dir}/index.json`); + if (!indexResp.ok) throw new Error(`Could not load manifest from ${dir}`); + const filenames = await indexResp.json() as string[]; + + for (const filename of filenames) { + try { + const response = await fetch(`${dir}/${filename}`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + const data = await response.json(); + const messages = Array.isArray(data) ? data : (data.messages || []); + + let surfaceId = filename.replace('.json', ''); + const createMsg = messages.find((m: any) => m.createSurface); + if (createMsg) { + surfaceId = createMsg.createSurface.surfaceId; + } else { + messages.unshift({ + version: "v0.9", + createSurface: { + surfaceId, + catalogId: isBasic ? basicCatalog.id : minimalCatalog.id + } + }); + } + + items.push({ + id: surfaceId, + title: filename.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ').replace('.json', ''), + filename: filename, + description: data.description || `Source: ${filename}`, + messages: messages, + isBasic + }); + + } catch (err) { + console.error(`Error loading ${filename}:`, err); + } + } + } catch (e) { + console.warn(`Could not load ${dir}`, e); + } + } + + selectItem(index: number) { + this.activeItemIndex = index; + this.resetSurface(); + this.advanceMessages(true); + } + + resetSurface() { + this.processedMessageCount = 0; + this.mockLogs = []; + this.currentDataModelText = "{}"; + + // Clear old surface and subscriptions + if (this.dataModelSubscription) { + this.dataModelSubscription.unsubscribe(); + this.dataModelSubscription = undefined; + } + + const item = this.demoItems[this.activeItemIndex]; + if (item && this.processor.model.getSurface(item.id)) { + this.processor.processMessages([{ version: "v0.9", deleteSurface: { surfaceId: item.id } }]); + } + } + + advanceMessages(all = false) { + const item = this.demoItems[this.activeItemIndex]; + if (!item) return; + + const toProcess = all + ? item.messages.slice(this.processedMessageCount) + : [item.messages[this.processedMessageCount]]; + + if (toProcess.length === 0) return; + + this.processor.processMessages(toProcess); + this.processedMessageCount += toProcess.length; + + // Subscribe to data model on first advance if not already subscribed + if (!this.dataModelSubscription) { + const surface = this.processor.model.getSurface(item.id); + if (surface) { + this.dataModelSubscription = surface.dataModel.subscribe("/", (val) => { + this.currentDataModelText = JSON.stringify(val || {}, null, 2); + }); + } + } + } + + log(msg: string, detail?: any) { + const time = new Date().toLocaleTimeString(); + const entry = detail ? `${msg}\n${JSON.stringify(detail, null, 2)}` : msg; + this.mockLogs = [...this.mockLogs, `[${time}] ${entry}`]; + } + + render() { + const activeItem = this.demoItems[this.activeItemIndex]; + const surface = activeItem ? this.processor.model.getSurface(activeItem.id) : undefined; + const canAdvance = activeItem && this.processedMessageCount < activeItem.messages.length; + + return html` +
+
+

A2UI Local Gallery

+

v0.9 Minimal Catalog

+
+
+
+ + + + + +
+ `; + } +} \ No newline at end of file diff --git a/samples/client/lit/gallery_v0_9/src/main.ts b/samples/client/lit/gallery_v0_9/src/main.ts new file mode 100644 index 000000000..30ea147ed --- /dev/null +++ b/samples/client/lit/gallery_v0_9/src/main.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import './local-gallery.js'; diff --git a/samples/client/lit/gallery_v0_9/src/theme.ts b/samples/client/lit/gallery_v0_9/src/theme.ts new file mode 100644 index 000000000..c7e91cd1d --- /dev/null +++ b/samples/client/lit/gallery_v0_9/src/theme.ts @@ -0,0 +1,456 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { v0_8 } from "@a2ui/lit"; + +/** Elements */ + +const a = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-as-n": true, + "layout-dis-iflx": true, + "layout-al-c": true, + "typography-td-none": true, + "color-c-p40": true, +}; + +const audio = { + "layout-w-100": true, +}; + +const body = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-mt-0": true, + "layout-mb-2": true, + "typography-sz-bm": true, + "color-c-n10": true, +}; + +const button = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-pt-3": true, + "layout-pb-3": true, + "layout-pl-5": true, + "layout-pr-5": true, + "layout-mb-1": true, + "border-br-16": true, + "border-bw-0": true, + "border-c-n70": true, + "border-bs-s": true, + "color-bgc-s30": true, + "behavior-ho-80": true, +}; + +const heading = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-500": true, + "layout-mt-0": true, + "layout-mb-2": true, +}; + +const iframe = { + "behavior-sw-n": true, +}; + +const input = { + "typography-f-sf": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-pl-4": true, + "layout-pr-4": true, + "layout-pt-2": true, + "layout-pb-2": true, + "border-br-6": true, + "border-bw-1": true, + "color-bc-s70": true, + "border-bs-s": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const p = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const orderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const unorderedList = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const listItem = { + "typography-f-s": true, + "typography-fs-n": true, + "typography-w-400": true, + "layout-m-0": true, + "typography-sz-bm": true, + "layout-as-n": true, + "color-c-n10": true, +}; + +const pre = { + "typography-f-c": true, + "typography-fs-n": true, + "typography-w-400": true, + "typography-sz-bm": true, + "typography-ws-p": true, + "layout-as-n": true, +}; + +const textarea = { + ...input, + "layout-r-none": true, + "layout-fs-c": true, +}; + +const video = { + "layout-el-cv": true, +}; + +const aLight = v0_8.Styles.merge(a, {}); +const inputLight = v0_8.Styles.merge(input, {}); +const textareaLight = v0_8.Styles.merge(textarea, {}); +const buttonLight = v0_8.Styles.merge(button, {}); +const bodyLight = v0_8.Styles.merge(body, {}); +const pLight = v0_8.Styles.merge(p, {}); +const preLight = v0_8.Styles.merge(pre, {}); +const orderedListLight = v0_8.Styles.merge(orderedList, {}); +const unorderedListLight = v0_8.Styles.merge(unorderedList, {}); +const listItemLight = v0_8.Styles.merge(listItem, {}); + +export const theme: v0_8.Types.Theme = { + additionalStyles: { + Button: { + "--n-35": "var(--n-100)", + "--n-10": "var(--n-0)", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + boxShadow: "0 4px 15px rgba(102, 126, 234, 0.4)", + padding: "12px 28px", + textTransform: "uppercase", + }, + Text: { + h1: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h2: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h3: { + color: "transparent", + background: + "linear-gradient(135deg, light-dark(#818cf8, #06b6d4) 0%, light-dark(#a78bfa, #3b82f6) 100%)", + "-webkit-background-clip": "text", + "background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + h4: {}, + h5: {}, + body: {}, + caption: {}, + }, + Card: { + background: + "radial-gradient(circle at top left, light-dark(transparent, rgba(6, 182, 212, 0.15)), transparent 40%), radial-gradient(circle at bottom right, light-dark(transparent, rgba(139, 92, 246, 0.15)), transparent 40%), linear-gradient(135deg, light-dark(rgba(255, 255, 255, 0.7), rgba(30, 41, 59, 0.7)), light-dark(rgba(255, 255, 255, 0.7), rgba(15, 23, 42, 0.8)))", + }, + TextField: { + "--p-0": "light-dark(var(--n-0), #1e293b)", + }, + Modal: { + background: + "linear-gradient(135deg, light-dark(rgba(255, 255, 255, 0.9), rgba(30, 41, 59, 1)), light-dark(rgba(255, 255, 255, 0.95), rgba(15, 23, 42, 1)))", + boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.2)", + borderRadius: "8px", + padding: "16px", + minWidth: "300px", + maxWidth: "80vw", + display: "flex", + flexDirection: "column", + }, + MultipleChoice: { + "--md-sys-color-secondary-container-high": "#e8def8", + }, + }, + components: { + AudioPlayer: {}, + Button: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-12": true, + "border-bw-0": true, + "border-bs-s": true, + "color-bgc-p30": true, + "behavior-ho-70": true, + "typography-w-400": true, + }, + Card: { "border-br-9": true, "layout-p-4": true, "color-bgc-n100": true }, + CheckBox: { + element: { + "layout-m-0": true, + "layout-mr-2": true, + "layout-p-2": true, + "border-br-12": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + label: { + "color-c-p30": true, + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-flx-1": true, + "typography-sz-ll": true, + }, + container: { + "layout-dsp-iflex": true, + "layout-al-c": true, + }, + }, + Column: { + "layout-g-2": true, + }, + DateTimeInput: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "typography-ws-nw": true, + }, + label: { + "color-c-p30": true, + "typography-sz-bm": true, + }, + element: { + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-2": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Divider: {}, + Image: { + all: { + "border-br-5": true, + "layout-el-cv": true, + "layout-w-100": true, + "layout-h-100": true, + }, + avatar: { "is-avatar": true }, + header: {}, + icon: {}, + largeFeature: {}, + mediumFeature: {}, + smallFeature: {}, + }, + Icon: {}, + List: { + "layout-g-4": true, + "layout-p-2": true, + }, + Modal: { + backdrop: { "color-bbgc-p60_20": true }, + element: { + "border-br-2": true, + "color-bgc-p100": true, + "layout-p-4": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bc-p80": true, + }, + }, + MultipleChoice: { + container: {}, + label: {}, + element: {}, + }, + Row: { + "layout-g-4": true, + }, + Slider: { + container: {}, + label: {}, + element: {}, + }, + Tabs: { + container: {}, + controls: { all: {}, selected: {} }, + element: {}, + }, + Text: { + all: { + "layout-w-100": true, + "layout-g-2": true, + }, + h1: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-hs": true, + }, + h2: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h3: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-tl": true, + }, + h4: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-bl": true, + }, + h5: { + "typography-f-sf": true, + "typography-v-r": true, + "typography-w-400": true, + "layout-m-0": true, + "layout-p-0": true, + "typography-sz-bm": true, + }, + body: {}, + caption: {}, + }, + TextField: { + container: { + "typography-sz-bm": true, + "layout-w-100": true, + "layout-g-2": true, + "layout-dsp-flexhor": true, + "layout-al-c": true, + "typography-ws-nw": true, + }, + label: { + "layout-flx-0": true, + "color-c-p30": true, + }, + element: { + "typography-sz-bm": true, + "layout-pt-2": true, + "layout-pb-2": true, + "layout-pl-3": true, + "layout-pr-3": true, + "border-br-2": true, + "border-bw-1": true, + "border-bs-s": true, + "color-bgc-p100": true, + "color-bc-p60": true, + "color-c-n30": true, + "color-c-p30": true, + }, + }, + Video: { + "border-br-5": true, + "layout-el-cv": true, + }, + }, + elements: { + a: aLight, + audio, + body: bodyLight, + button: buttonLight, + h1: heading, + h2: heading, + h3: heading, + h4: heading, + h5: heading, + iframe, + input: inputLight, + p: pLight, + pre: preLight, + textarea: textareaLight, + video, + }, + markdown: { + p: [...Object.keys(pLight)], + h1: [...Object.keys(heading)], + h2: [...Object.keys(heading)], + h3: [...Object.keys(heading)], + h4: [...Object.keys(heading)], + h5: [...Object.keys(heading)], + ul: [...Object.keys(unorderedListLight)], + ol: [...Object.keys(orderedListLight)], + li: [...Object.keys(listItemLight)], + a: [...Object.keys(aLight)], + strong: [], + em: [], + }, +}; diff --git a/samples/client/lit/gallery_v0_9/tsconfig.json b/samples/client/lit/gallery_v0_9/tsconfig.json new file mode 100644 index 000000000..afabdb9d2 --- /dev/null +++ b/samples/client/lit/gallery_v0_9/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es2022", + "module": "es2022", + "moduleResolution": "bundler", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "strict": true, + "skipLibCheck": true, + "useDefineForClassFields": false, + "rootDir": ".", + "outDir": "dist", + "sourceMap": true, + "resolveJsonModule": true, + "isolatedModules": true + } +} diff --git a/samples/client/lit/gallery_v0_9/vite.config.ts b/samples/client/lit/gallery_v0_9/vite.config.ts new file mode 100644 index 000000000..57de5443f --- /dev/null +++ b/samples/client/lit/gallery_v0_9/vite.config.ts @@ -0,0 +1,26 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineConfig } from 'vite'; + +export default defineConfig({ + build: { + target: 'esnext', + }, + resolve: { + dedupe: ['lit'], + }, +}); diff --git a/samples/client/lit/package.json b/samples/client/lit/package.json index 6bf74d9b4..0b66302d7 100644 --- a/samples/client/lit/package.json +++ b/samples/client/lit/package.json @@ -12,10 +12,12 @@ "serve:agent:contact_lookup": "cd ../../agent/adk/contact_lookup && uv run .", "serve:agent:orchestrator": "cd ../../agent/adk/orchestrator && uv run .", "serve:shell": "cd shell && npm run dev", + "serve:gallery09": "cd gallery_v0_9 && npm install && npm run dev", "build:renderer": "cd ../../../renderers && for dir in 'web_core' 'markdown/markdown-it' 'lit'; do (cd \"$dir\" && npm install && npm run build); done", "demo:restaurant": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\"", "demo:contact": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,CONT1\" -c \"magenta,green\" \"npm run serve:shell\" \"npm run serve:agent:contact_lookup\"", - "demo:orchestrator": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,ORCH\" -c \"magenta,cyan\" \"npm run serve:shell\" \"npm run serve:agent:orchestrator\"" + "demo:orchestrator": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,ORCH\" -c \"magenta,cyan\" \"npm run serve:shell\" \"npm run serve:agent:orchestrator\"", + "demo:gallery09": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,GALLERY09\" -c \"magenta,yellow\" \"npm run serve:gallery09\"" }, "devDependencies": { "concurrently": "9.2.1"