diff --git a/samples/agent/adk/component_gallery/gallery_examples.py b/samples/agent/adk/component_gallery/gallery_examples.py index 8ea8b31f7..9bd037501 100644 --- a/samples/agent/adk/component_gallery/gallery_examples.py +++ b/samples/agent/adk/component_gallery/gallery_examples.py @@ -3,6 +3,31 @@ import json +def _to_value_map(items: list[dict]) -> list[dict]: + """Converts a list of plain dicts into indexed A2UI valueMap entries. + + Each dict's values are mapped to the appropriate A2UI type + (valueString, valueNumber, valueBoolean). Lists of dicts are + recursively converted to nested valueMaps. + """ + result = [] + for i, item in enumerate(items): + entries = [] + for k, v in item.items(): + if isinstance(v, str): + entries.append({"key": k, "valueString": v}) + elif isinstance(v, bool): + entries.append({"key": k, "valueBoolean": v}) + elif isinstance(v, (int, float)): + entries.append({"key": k, "valueNumber": v}) + elif isinstance(v, list): + entries.append({"key": k, "valueMap": _to_value_map(v)}) + else: + entries.append({"key": k, "valueString": str(v)}) + result.append({"key": str(i), "valueMap": entries}) + return result + + def get_gallery_json() -> str: """Returns the JSON structure for the Component Gallery surfaces.""" @@ -23,6 +48,88 @@ def get_gallery_json() -> str: {"key": "favorites", "valueMap": [{"key": "0", "valueString": "A"}]}, {"key": "favoritesChips", "valueMap": []}, {"key": "favoritesFilter", "valueMap": []}, + # Chart demo data + { + "key": "pieChartData", + "valueMap": _to_value_map([ + { + "label": "Apparel", + "value": 41, + "drillDown": [ + {"label": "Tops", "value": 31}, + {"label": "Bottoms", "value": 38}, + {"label": "Outerwear", "value": 20}, + {"label": "Footwear", "value": 11}, + ], + }, + { + "label": "Electronics", + "value": 28, + "drillDown": [ + {"label": "Phones", "value": 25}, + {"label": "Laptops", "value": 27}, + {"label": "TVs", "value": 21}, + ], + }, + { + "label": "Home Goods", + "value": 15, + "drillDown": [ + {"label": "Furniture", "value": 8}, + {"label": "Kitchen", "value": 16}, + {"label": "Decor", "value": 3}, + ], + }, + {"label": "Health", "value": 10}, + {"label": "Other", "value": 6}, + ]), + }, + { + "key": "barChartData", + "valueMap": _to_value_map([ + {"label": "Jan", "value": 12}, + {"label": "Feb", "value": 19}, + {"label": "Mar", "value": 15}, + {"label": "Apr", "value": 22}, + {"label": "May", "value": 30}, + {"label": "Jun", "value": 28}, + ]), + }, + # OrgChart demo data + { + "key": "orgChartChain", + "valueMap": _to_value_map([ + {"title": "CEO", "name": "Alice Johnson"}, + {"title": "SVP Engineering", "name": "Bob Smith"}, + {"title": "VP Product", "name": "Charlie Brown"}, + {"title": "Director", "name": "Diana Prince"}, + {"title": "Software Engineer", "name": "Evan Wright"}, + ]), + }, + { + "key": "doughnutChartData", + "valueMap": _to_value_map([ + { + "label": "Engineering", + "value": 45, + "drillDown": [ + {"label": "Frontend", "value": 18}, + {"label": "Backend", "value": 15}, + {"label": "Infra", "value": 12}, + ], + }, + { + "label": "Marketing", + "value": 20, + "drillDown": [ + {"label": "Digital", "value": 12}, + {"label": "Brand", "value": 8}, + ], + }, + {"label": "Operations", "value": 20}, + {"label": "Sales", "value": 15}, + ]), + }, ], } @@ -440,6 +547,56 @@ def add_demo_surface(surface_id, component_def): }, ) + # 16. Chart (Pie) — with drill-down data + add_demo_surface( + "demo-chart-pie", + { + "Chart": { + "chartType": "pie", + "chartTitle": {"literalString": "Sales by Category"}, + "chartData": {"path": "galleryData/pieChartData"}, + } + }, + ) + + # 17. Chart (Bar) — flat data, no drill-down + add_demo_surface( + "demo-chart-bar", + { + "Chart": { + "chartType": "bar", + "chartTitle": {"literalString": "Monthly Revenue"}, + "chartData": {"path": "galleryData/barChartData"}, + } + }, + ) + + # 18. Chart (Doughnut) — with drill-down data + add_demo_surface( + "demo-chart-doughnut", + { + "Chart": { + "chartType": "doughnut", + "chartTitle": {"literalString": "Budget Allocation"}, + "chartData": {"path": "galleryData/doughnutChartData"}, + } + }, + ) + + # 19. OrgChart — custom component showing organizational hierarchy + add_demo_surface( + "demo-org-chart", + { + "OrgChart": { + "chain": {"path": "galleryData/orgChartChain"}, + "action": { + "name": "org_chart_click", + "context": [], + }, + } + }, + ) + # Response Surface messages.append( {"beginRendering": {"surfaceId": "response-surface", "root": "response-text"}} diff --git a/samples/agent/adk/component_gallery/pyproject.toml b/samples/agent/adk/component_gallery/pyproject.toml new file mode 100644 index 000000000..99d019e5a --- /dev/null +++ b/samples/agent/adk/component_gallery/pyproject.toml @@ -0,0 +1,33 @@ +[project] +name = "component-gallery" +version = "0.1.0" +description = "Sample Google ADK-based component gallery agent that uses a2ui extension and is hosted as an A2A server agent." +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "a2a-sdk>=0.3.0", + "click>=8.1.8", + "google-adk>=1.8.0", + "google-genai>=1.27.0", + "python-dotenv>=1.1.0", + "litellm", + "jsonschema>=4.0.0", + "a2ui-agent", +] + +[tool.hatch.build.targets.wheel] +packages = ["."] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.metadata] +allow-direct-references = true + +[[tool.uv.index]] +url = "https://pypi.org/simple" +default = true + +[tool.uv.sources] +a2ui-agent = { path = "../../../../a2a_agents/python/a2ui_agent", editable = true } diff --git a/samples/agent/adk/pyproject.toml b/samples/agent/adk/pyproject.toml index c63dd5bf8..c5d3a3262 100644 --- a/samples/agent/adk/pyproject.toml +++ b/samples/agent/adk/pyproject.toml @@ -3,7 +3,7 @@ url = "https://pypi.org/simple" default = true [tool.uv.workspace] -members = ["contact_lookup", "contact_multiple_surfaces", "orchestrator", "restaurant_finder", "rizzcharts"] +members = ["component_gallery", "contact_lookup", "contact_multiple_surfaces", "orchestrator", "restaurant_finder", "rizzcharts"] [tool.uv.sources] a2ui-agent = { path = "../../../agent_sdks/python", editable = true } diff --git a/samples/agent/adk/uv.lock b/samples/agent/adk/uv.lock index 706c32cd9..c10191b1b 100644 --- a/samples/agent/adk/uv.lock +++ b/samples/agent/adk/uv.lock @@ -11,6 +11,7 @@ members = [ "a2ui-contact-lookup", "a2ui-contact-multiple-surfaces", "a2ui-restaurant-finder", + "component-gallery", "orchestrator", "rizzcharts", ] @@ -456,6 +457,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "component-gallery" +version = "0.1.0" +source = { editable = "component_gallery" } +dependencies = [ + { name = "a2a-sdk" }, + { name = "a2ui-agent" }, + { name = "click" }, + { name = "google-adk" }, + { name = "google-genai" }, + { name = "jsonschema" }, + { name = "litellm" }, + { name = "python-dotenv" }, +] + +[package.metadata] +requires-dist = [ + { name = "a2a-sdk", specifier = ">=0.3.0" }, + { name = "a2ui-agent", editable = "../../../a2a_agents/python/a2ui_agent" }, + { name = "click", specifier = ">=8.1.8" }, + { name = "google-adk", specifier = ">=1.8.0" }, + { name = "google-genai", specifier = ">=1.27.0" }, + { name = "jsonschema", specifier = ">=4.0.0" }, + { name = "litellm" }, + { name = "python-dotenv", specifier = ">=1.1.0" }, +] + [[package]] name = "cryptography" version = "46.0.5" @@ -1174,6 +1202,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/ab/d26750f2b7242c2b90ea2ad71de70cfcd73a948a49513188a0fc0d6fc15a/greenlet-3.3.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:7ab327905cabb0622adca5971e488064e35115430cec2c35a50fd36e72a315b3", size = 275205, upload-time = "2026-01-23T15:30:24.556Z" }, { url = "https://files.pythonhosted.org/packages/10/d3/be7d19e8fad7c5a78eeefb2d896a08cd4643e1e90c605c4be3b46264998f/greenlet-3.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65be2f026ca6a176f88fb935ee23c18333ccea97048076aef4db1ef5bc0713ac", size = 599284, upload-time = "2026-01-23T16:00:58.584Z" }, { url = "https://files.pythonhosted.org/packages/ae/21/fe703aaa056fdb0f17e5afd4b5c80195bbdab701208918938bd15b00d39b/greenlet-3.3.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a3ae05b3d225b4155bda56b072ceb09d05e974bc74be6c3fc15463cf69f33fd", size = 610274, upload-time = "2026-01-23T16:05:29.312Z" }, + { url = "https://files.pythonhosted.org/packages/06/00/95df0b6a935103c0452dad2203f5be8377e551b8466a29650c4c5a5af6cc/greenlet-3.3.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:12184c61e5d64268a160226fb4818af4df02cfead8379d7f8b99a56c3a54ff3e", size = 624375, upload-time = "2026-01-23T16:15:55.915Z" }, { url = "https://files.pythonhosted.org/packages/cb/86/5c6ab23bb3c28c21ed6bebad006515cfe08b04613eb105ca0041fecca852/greenlet-3.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6423481193bbbe871313de5fd06a082f2649e7ce6e08015d2a76c1e9186ca5b3", size = 612904, upload-time = "2026-01-23T15:32:52.317Z" }, { url = "https://files.pythonhosted.org/packages/c2/f3/7949994264e22639e40718c2daf6f6df5169bf48fb038c008a489ec53a50/greenlet-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:33a956fe78bbbda82bfc95e128d61129b32d66bcf0a20a1f0c08aa4839ffa951", size = 1567316, upload-time = "2026-01-23T16:04:23.316Z" }, { url = "https://files.pythonhosted.org/packages/8d/6e/d73c94d13b6465e9f7cd6231c68abde838bb22408596c05d9059830b7872/greenlet-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b065d3284be43728dd280f6f9a13990b56470b81be20375a207cdc814a983f2", size = 1636549, upload-time = "2026-01-23T15:33:48.643Z" }, @@ -1182,6 +1211,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/fb/011c7c717213182caf78084a9bea51c8590b0afda98001f69d9f853a495b/greenlet-3.3.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:bd59acd8529b372775cd0fcbc5f420ae20681c5b045ce25bd453ed8455ab99b5", size = 275737, upload-time = "2026-01-23T15:32:16.889Z" }, { url = "https://files.pythonhosted.org/packages/41/2e/a3a417d620363fdbb08a48b1dd582956a46a61bf8fd27ee8164f9dfe87c2/greenlet-3.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b31c05dd84ef6871dd47120386aed35323c944d86c3d91a17c4b8d23df62f15b", size = 646422, upload-time = "2026-01-23T16:01:00.354Z" }, { url = "https://files.pythonhosted.org/packages/b4/09/c6c4a0db47defafd2d6bab8ddfe47ad19963b4e30f5bed84d75328059f8c/greenlet-3.3.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:02925a0bfffc41e542c70aa14c7eda3593e4d7e274bfcccca1827e6c0875902e", size = 658219, upload-time = "2026-01-23T16:05:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/b95f2ddcc5f3c2bc09c8ee8d77be312df7f9e7175703ab780f2014a0e781/greenlet-3.3.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3e0f3878ca3a3ff63ab4ea478585942b53df66ddde327b59ecb191b19dbbd62d", size = 671455, upload-time = "2026-01-23T16:15:57.232Z" }, { url = "https://files.pythonhosted.org/packages/80/38/9d42d60dffb04b45f03dbab9430898352dba277758640751dc5cc316c521/greenlet-3.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34a729e2e4e4ffe9ae2408d5ecaf12f944853f40ad724929b7585bca808a9d6f", size = 660237, upload-time = "2026-01-23T15:32:53.967Z" }, { url = "https://files.pythonhosted.org/packages/96/61/373c30b7197f9e756e4c81ae90a8d55dc3598c17673f91f4d31c3c689c3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aec9ab04e82918e623415947921dea15851b152b822661cce3f8e4393c3df683", size = 1615261, upload-time = "2026-01-23T16:04:25.066Z" }, { url = "https://files.pythonhosted.org/packages/fd/d3/ca534310343f5945316f9451e953dcd89b36fe7a19de652a1dc5a0eeef3f/greenlet-3.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:71c767cf281a80d02b6c1bdc41c9468e1f5a494fb11bc8688c360524e273d7b1", size = 1683719, upload-time = "2026-01-23T15:33:50.61Z" }, @@ -1190,6 +1220,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/28/24/cbbec49bacdcc9ec652a81d3efef7b59f326697e7edf6ed775a5e08e54c2/greenlet-3.3.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:3e63252943c921b90abb035ebe9de832c436401d9c45f262d80e2d06cc659242", size = 282706, upload-time = "2026-01-23T15:33:05.525Z" }, { url = "https://files.pythonhosted.org/packages/86/2e/4f2b9323c144c4fe8842a4e0d92121465485c3c2c5b9e9b30a52e80f523f/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76e39058e68eb125de10c92524573924e827927df5d3891fbc97bd55764a8774", size = 651209, upload-time = "2026-01-23T16:01:01.517Z" }, { url = "https://files.pythonhosted.org/packages/d9/87/50ca60e515f5bb55a2fbc5f0c9b5b156de7d2fc51a0a69abc9d23914a237/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9f9d5e7a9310b7a2f416dd13d2e3fd8b42d803968ea580b7c0f322ccb389b97", size = 654300, upload-time = "2026-01-23T16:05:32.199Z" }, + { url = "https://files.pythonhosted.org/packages/7c/25/c51a63f3f463171e09cb586eb64db0861eb06667ab01a7968371a24c4f3b/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9721549a95db96689458a1e0ae32412ca18776ed004463df3a9299c1b257ab", size = 662574, upload-time = "2026-01-23T16:15:58.364Z" }, { url = "https://files.pythonhosted.org/packages/1d/94/74310866dfa2b73dd08659a3d18762f83985ad3281901ba0ee9a815194fb/greenlet-3.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92497c78adf3ac703b57f1e3813c2d874f27f71a178f9ea5887855da413cd6d2", size = 653842, upload-time = "2026-01-23T15:32:55.671Z" }, { url = "https://files.pythonhosted.org/packages/97/43/8bf0ffa3d498eeee4c58c212a3905dd6146c01c8dc0b0a046481ca29b18c/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ed6b402bc74d6557a705e197d47f9063733091ed6357b3de33619d8a8d93ac53", size = 1614917, upload-time = "2026-01-23T16:04:26.276Z" }, { url = "https://files.pythonhosted.org/packages/89/90/a3be7a5f378fc6e84abe4dcfb2ba32b07786861172e502388b4c90000d1b/greenlet-3.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:59913f1e5ada20fde795ba906916aea25d442abcc0593fba7e26c92b7ad76249", size = 1676092, upload-time = "2026-01-23T15:33:52.176Z" }, diff --git a/samples/client/lit/component_gallery/README.md b/samples/client/lit/component_gallery/README.md index f9886269f..98d9f9bb4 100644 --- a/samples/client/lit/component_gallery/README.md +++ b/samples/client/lit/component_gallery/README.md @@ -4,51 +4,120 @@ This is the client-side application for the A2UI Component Gallery. It is a Lit- ## Overview -The client uses the `@a2ui/lit` renderer to interpret the JSON-based UI descriptions sent by the agent and render them as standard Web Components. It demonstrates how to integrate the A2UI renderer into a modern web application build with Vite. +The client uses the `@a2ui/lit` renderer to interpret the JSON-based UI descriptions sent by the agent and render them as standard Web Components. It demonstrates: + +- Rendering all **standard A2UI catalog components** (TextField, Button, Slider, Tabs, etc.) +- Creating and registering **custom components** (Chart, OrgChart, WebFrame) +- **Overriding standard components** with custom implementations (PremiumTextField replaces TextField) +- Sending an **inline catalog** so the agent knows which custom components the client supports + +## Architecture + +The gallery has two halves that work together: + +| Layer | Directory | Role | +|-------|-----------|------| +| **Agent** | `samples/agent/adk/component_gallery/` | Defines surfaces, data models, and action handlers in `gallery_examples.py` | +| **Client** | `samples/client/lit/component_gallery/` | Renders surfaces using the `@a2ui/lit` renderer and custom components | + +The agent sends A2UI JSON messages (via A2A protocol) describing component trees and data models. The client's `` element renders them. For custom components, the client registers them with `componentRegistry` and sets `enableCustomElements` on the surface. ## Getting Started -To fully run the sample, you need to start **both** the Agent (frontend logic) and the Client (web renderer). +To fully run the sample, you need to start **both** the Agent and the Client. ### Prerequisites - Python 3.10+ & `uv` (for Agent) - Node.js 18+ & `npm` (for Client) -### 1. Run the Agent (Backend) +### 1. Build the Renderer + +The client depends on the `@a2ui/lit` renderer. Build it first: + +```bash +cd renderers/lit +npm install +npm run build +``` + +### 2. Run the Agent (Backend) The agent serves the UI definitions and handles user interactions. -1. Navigate to the agent directory: - ```bash - cd samples/agent/adk/component_gallery - ``` +```bash +cd samples/agent/adk/component_gallery +uv run . +``` -2. Install dependencies and start the server: - ```bash - uv run . - ``` - The agent will run on `http://localhost:10005`. +The agent will run on `http://localhost:10005`. + +### 3. Run the Client (Frontend) + +Open a **new terminal**: -### 2. Run the Client (Frontend) +```bash +cd samples/client/lit/component_gallery +npm install +npm run dev +``` -The client connects to the agent and renders the UI. +Open your browser to the URL shown (usually `http://localhost:5173`). -1. Open a **new terminal** and navigate to the client directory: - ```bash - cd samples/client/lit/component_gallery +## Custom Components + +The gallery includes four custom components in [`ui/custom-components/`](ui/custom-components/): + +| Component | Type Name | Description | +|-----------|-----------|-------------| +| **Chart** | `Chart` | Interactive pie/doughnut/bar charts with drill-down (uses Chart.js) | +| **OrgChart** | `OrgChart` | Vertical organizational hierarchy with clickable nodes | +| **WebFrame** | `WebFrame` | Sandboxed iframe for embedding external content | +| **PremiumTextField** | `TextField` | Overrides the standard TextField with a custom-styled version | + +### How Custom Components Work + +1. **Client registers components** at startup in [`register-components.ts`](ui/custom-components/register-components.ts), called from [`client.ts`](client.ts): + + ```typescript + componentRegistry.register("Chart", ChartComponent, "a2ui-custom-chart", { + type: "object", + properties: { chartType: { type: "string" }, /* ... */ }, + required: ["chartType", "chartData"], + }); ``` -2. Install dependencies: - ```bash - npm install +2. **Client sends its catalog** to the agent via the inline catalog mechanism in `A2UIClient.send()`: + + ```typescript + const catalog = componentRegistry.getInlineCatalog(); + const finalMessage = { ...message, metadata: { inlineCatalogs: [catalog] } }; ``` -3. Start the development server: - ```bash - npm run dev +3. **Agent sends surfaces** that reference custom component types (in `gallery_examples.py`): + + ```python + add_demo_surface("demo-chart-pie", { + "Chart": { + "chartType": "pie", + "chartTitle": {"literalString": "Sales by Category"}, + "chartData": {"path": "galleryData/pieChartData"}, + } + }) ``` - Open your browser to the URL shown (usually `http://localhost:5173`). + +4. **Renderer resolves the type** via `componentRegistry.get("Chart")`, instantiates the element, and assigns properties including `processor` and `surfaceId` so the component can resolve path-based data references. + +### Creating a New Custom Component + +1. Create a TypeScript file in `ui/custom-components/` extending `Root` from `@a2ui/lit/ui` +2. Include `...Root.styles` in your `static styles` array +3. Use `this.processor.getData(this.component, path, this.surfaceId)` to resolve `{ path: "..." }` property references +4. Handle Map data — the model processor converts nested objects to Maps internally +5. Register the component in `register-components.ts` with a type name, tag name, and JSON schema +6. Add demo data to `gallery_examples.py` (agent side) and a `DEMO_ITEMS` entry in `component-gallery.ts` + +See [`ui/custom-components/README.md`](ui/custom-components/README.md) for detailed documentation on each component and its properties. ## Attribution diff --git a/samples/client/lit/component_gallery/component-gallery.ts b/samples/client/lit/component_gallery/component-gallery.ts index 021bd7ee8..6bc709a11 100644 --- a/samples/client/lit/component_gallery/component-gallery.ts +++ b/samples/client/lit/component_gallery/component-gallery.ts @@ -37,6 +37,10 @@ const DEMO_ITEMS: DemoItem[] = [ { id: "demo-modal", title: "Modal", description: "Overlay dialog." }, { id: "demo-list", title: "List", description: "Vertical or horizontal list." }, { id: "demo-audio", title: "AudioPlayer", description: "Play audio content." }, + { id: "demo-chart-pie", title: "Chart (Pie)", description: "Pie chart with drill-down." }, + { id: "demo-chart-bar", title: "Chart (Bar)", description: "Bar chart." }, + { id: "demo-chart-doughnut", title: "Chart (Doughnut)", description: "Doughnut chart with drill-down." }, + { id: "demo-org-chart", title: "OrgChart", description: "Organizational hierarchy." }, ]; @customElement("a2ui-component-gallery") @@ -276,6 +280,7 @@ export class A2UIComponentGallery extends SignalWatcher(LitElement) { .surface=${{ ...surface }} .surfaceId=${surfaceId} .processor=${this.#processor} + .enableCustomElements=${true} @a2uiaction=${(evt: any) => this.#handleAction(evt, surfaceId)} > `; diff --git a/samples/client/lit/component_gallery/package-lock.json b/samples/client/lit/component_gallery/package-lock.json new file mode 100644 index 000000000..d8a376f4f --- /dev/null +++ b/samples/client/lit/component_gallery/package-lock.json @@ -0,0 +1,1792 @@ +{ + "name": "@a2ui/component-gallery", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@a2ui/component-gallery", + "version": "0.1.0", + "dependencies": { + "@a2a-js/sdk": "^0.3.4", + "@a2ui/lit": "file:../../../../renderers/lit", + "@lit-labs/signals": "^0.1.3", + "@lit/context": "^1.1.4", + "chart.js": "^4.5.1", + "chartjs-plugin-datalabels": "^2.2.0", + "lit": "^3.3.1" + }, + "devDependencies": { + "@types/node": "^25.3.3", + "dotenv": "^17.2.3", + "typescript": "^5.8.3", + "uuid": "^13.0.0", + "vite": "^7.1.11", + "wireit": "^0.15.0-pre.2" + } + }, + "../../../../renderers/lit": { + "name": "@a2ui/lit", + "version": "0.8.1", + "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", + "markdown-it": "^14.1.0", + "signal-utils": "^0.21.1" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.2", + "@types/node": "^24.10.1", + "google-artifactregistry-auth": "^3.5.0", + "typescript": "^5.8.3", + "wireit": "^0.15.0-pre.2" + } + }, + "node_modules/@a2a-js/sdk": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@a2a-js/sdk/-/sdk-0.3.10.tgz", + "integrity": "sha512-t6w5ctnwJkSOMRl6M9rn95C1FTHCPqixxMR0yWXtzhZXEnF6mF1NAK0CfKlG3cz+tcwTxkmn287QZC3t9XPgrA==", + "license": "Apache-2.0", + "dependencies": { + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@bufbuild/protobuf": "^2.10.2", + "@grpc/grpc-js": "^1.11.0", + "express": "^4.21.2 || ^5.1.0" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + }, + "@grpc/grpc-js": { + "optional": true + }, + "express": { + "optional": true + } + } + }, + "node_modules/@a2a-js/sdk/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@a2ui/lit": { + "resolved": "../../../../renderers/lit", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, + "node_modules/@lit-labs/signals": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@lit-labs/signals/-/signals-0.1.3.tgz", + "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", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.5.1.tgz", + "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/context": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.6.tgz", + "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", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "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", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", + "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/balanced-match": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", + "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^3.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-plugin-datalabels": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz", + "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==", + "license": "MIT", + "peerDependencies": { + "chart.js": ">=3.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "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/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "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/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lit": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.2.tgz", + "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", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "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", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.2.tgz", + "integrity": "sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "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/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-polyfill": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/signal-polyfill/-/signal-polyfill-0.2.2.tgz", + "integrity": "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==", + "license": "Apache-2.0" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "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/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "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 + } + } + }, + "node_modules/wireit": { + "version": "0.15.0-pre.2", + "resolved": "https://registry.npmjs.org/wireit/-/wireit-0.15.0-pre.2.tgz", + "integrity": "sha512-pXOTR56btrL7STFOPQgtq8MjAFWagSqs188E2FflCgcxk5uc0Xbn8CuLIR9FbqK97U3Jw6AK8zDEu/M/9ENqgA==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "vscode-extension", + "website" + ], + "dependencies": { + "brace-expansion": "^4.0.0", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "jsonc-parser": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "bin": { + "wireit": "bin/wireit.js" + }, + "engines": { + "node": ">=18.0.0" + } + } + } +} diff --git a/samples/client/lit/component_gallery/package.json b/samples/client/lit/component_gallery/package.json index db7b663c7..c5ba19b84 100644 --- a/samples/client/lit/component_gallery/package.json +++ b/samples/client/lit/component_gallery/package.json @@ -57,13 +57,16 @@ "@a2ui/lit": "file:../../../../renderers/lit", "@lit-labs/signals": "^0.1.3", "@lit/context": "^1.1.4", + "chart.js": "^4.5.1", + "chartjs-plugin-datalabels": "^2.2.0", "lit": "^3.3.1" }, "devDependencies": { + "@types/node": "^25.3.3", "dotenv": "^17.2.3", "typescript": "^5.8.3", "uuid": "^13.0.0", "vite": "^7.1.11", "wireit": "^0.15.0-pre.2" } -} \ No newline at end of file +} diff --git a/samples/client/lit/component_gallery/ui/custom-components/README.md b/samples/client/lit/component_gallery/ui/custom-components/README.md index 18f611132..b230f2380 100644 --- a/samples/client/lit/component_gallery/ui/custom-components/README.md +++ b/samples/client/lit/component_gallery/ui/custom-components/README.md @@ -1,23 +1,107 @@ -# A2UI custom component integration guide +# A2UI Custom Component Integration Guide -This guide details how to create, register, and use a custom component in the A2UI client. +This directory contains custom Lit components that extend the A2UI standard catalog. They are registered in `register-components.ts` and rendered when the agent sends a matching component type. -## Create the component +## Components -Create a new Lit component file in `lib/src/0.8/ui/custom-components/`. -Example: `my-component.ts` +### Chart (`chart.ts`) + +Interactive chart component — the Lit counterpart to the Angular RIZZcharts Chart. + +**Features:** +- Chart types: `pie`, `doughnut`, `bar` +- One-level drill-down: click a wedge/bar or legend label to view sub-data +- Back button to return to the root view +- On-wedge percentage labels (pie/doughnut only, via `chartjs-plugin-datalabels`) +- Data binding via path references or literal arrays + +**Dependencies:** `chart.js`, `chartjs-plugin-datalabels` + +**Properties:** + +| Property | Type | Description | +|----------|------|-------------| +| `chartType` | `"pie" \| "doughnut" \| "bar"` | The type of chart to render | +| `chartTitle` | `StringValue` | Title displayed above the chart | +| `chartData` | `{ path } \| { literalArray }` | Array of `{ label, value, drillDown? }` items | + +**Data format example:** + +```json +{ + "chartType": "pie", + "chartTitle": { "literalString": "Sales by Category" }, + "chartData": { + "path": "myData/salesData" + } +} +``` + +Where the data model contains: + +```json +[ + { + "label": "Apparel", + "value": 41, + "drillDown": [ + { "label": "Tops", "value": 31 }, + { "label": "Bottoms", "value": 38 } + ] + }, + { "label": "Health", "value": 10 } +] +``` + +Items without `drillDown` are leaf nodes — clicking them does nothing. + +### OrgChart (`org-chart.ts`) + +Displays an organizational hierarchy as a vertical chain of nodes with arrows. + +**Properties:** + +| Property | Type | Description | +|----------|------|-------------| +| `chain` | `Array<{ title, name }>` | Hierarchy nodes from top to bottom | +| `action` | `Action` | A2UI action dispatched when a node is clicked | + +Clicking a node dispatches a `StateEvent` with `clickedNodeTitle` and `clickedNodeName` in the action context. + +### PremiumTextField (`premium-text-field.ts`) + +Demonstrates the **standard component override** pattern. Registered as `"TextField"`, it replaces the default text field with a custom-styled version that shows a "Custom" badge. + +**Properties:** Same as standard `TextField` (`label`, `text`). + +### WebFrame (`web-frame.ts`) + +Sandboxed iframe wrapper for embedding external content. + +**Properties:** + +| Property | Type | Description | +|----------|------|-------------| +| `url` | `string` | URL to load in the iframe | +| `html` | `string` | Inline HTML to render | +| `height` | `number` | Frame height in pixels | +| `interactionMode` | `"readOnly" \| "interactive"` | Controls user interaction | +| `allowedEvents` | `string[]` | Event names allowed via postMessage bridge | + +## Creating a Custom Component + +Create a new Lit component file in this directory extending `Root`: ```typescript import { html, css } from "lit"; import { property } from "lit/decorators.js"; - -import { Root } from "../root.js"; +import { Root } from "@a2ui/lit/ui"; export class MyComponent extends Root { @property() accessor myProp: string = "Default"; static styles = [ - ...Root.styles, // Inherit base styles + ...Root.styles, // Always inherit base styles css` :host { display: block; @@ -38,130 +122,129 @@ export class MyComponent extends Root { } ``` -## Register the component +**Do not** use the `@customElement()` decorator — the registry handles element definition. -Update `lib/src/0.8/ui/custom-components/index.ts` to register your new component. -You must pass the desired tag name as the third argument. +### Resolving Path-Based Properties + +Properties may arrive as literal values or as `{ path: "..." }` references to the data model. Detect and resolve them manually: ```typescript -import { componentRegistry } from "../component-registry.js"; -import { MyComponent } from "./my-component.js"; // Import your component +render() { + let data = this.myData; + + // If it's a path reference, resolve via the processor + const asAny = this.myData as any; + if (asAny && typeof asAny === 'object' && 'path' in asAny && asAny.path) { + if (this.processor) { + const resolved = this.processor.getData( + this.component, asAny.path, this.surfaceId ?? 'default' + ); + if (resolved) data = resolved; + } + } -export function registerCustomComponents() { - // Register with explicit tag name - componentRegistry.register("MyComponent", MyComponent, "my-component"); + // The model processor converts nested objects to Maps — handle both + if (data instanceof Map) { + const entries = Array.from(data.entries()); + entries.sort((a, b) => parseInt(a[0], 10) - parseInt(b[0], 10)); + data = entries.map(e => e[1]); + } } - -export { MyComponent }; // Export for type usage if needed ``` -## Define the schema (server-side) +## Registering the Component -Create a JSON schema for your component properties. This will be used by the server to validate messages. -Example: `lib/my_component_schema.json` +Update `register-components.ts` to register your new component. You must pass a type name, the class, a tag name, and an optional JSON schema: -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "properties": { - "type": { "const": "object" }, - "properties": { - "type": "object", - "additionalProperties": false, - "properties": { - "myProp": { - "type": "string", - "description": "A sample property." - } - }, - "required": ["myProp"] - } +```typescript +import { componentRegistry } from "@a2ui/lit/ui"; +import { MyComponent } from "./my-component.js"; + +componentRegistry.register("MyComponent", MyComponent, "my-component", { + type: "object", + properties: { + myProp: { + type: "string", + description: "A sample property.", + }, }, - "required": ["type", "properties"] -} + required: ["myProp"], +}); ``` -## Use in client application - -In your client application (e.g., `contact` sample), ensure you import and call the registration function. +The registration function is called once at startup from `client.ts`: ```typescript -import { registerCustomComponents } from "@a2ui/lit/ui"; - -// Call this once at startup -registerCustomComponents(); +import { registerContactComponents } from "./ui/custom-components/register-components.js"; +registerContactComponents(); ``` -## Overriding standard components +When the agent sends `{ "MyComponent": { "myProp": "Hello" } }`, the renderer looks up the registry, creates a `` element, and assigns properties. + +## Overriding Standard Components You can replace standard A2UI components (like `TextField`, `Video`, `Button`) with your own custom implementations. -### Steps to override +1. **Create your component** extending `Root` (same as a custom component). +2. **Accept the standard properties** for that component type (e.g., `label` and `text` for `TextField`). +3. **Register it using the standard type name:** -1. **Create your component** extending `Root` (just like a custom component). +```typescript +componentRegistry.register( + "TextField", // Standard type name — overrides default + MyPremiumTextField, + "my-premium-textfield" +); +``` -2. **Ensure it accepts the standard properties** for that component type (e.g., `label` and `text` for `TextField`). +When the server sends a `TextField` component, the client will now render `` instead of the default ``. -3. **Register it** using the **standard type name** (e.g., `"TextField"`). +## Adding to the Gallery - ```typescript - // 1. Define your override - class MyPremiumTextField extends Root { - @property() accessor label = ""; - @property() accessor text = ""; - - static styles = [ - ...Root.styles, - css` - /* your premium styles */ - `, - ]; - - render() { - return html` -
- - -
- `; - } - } +To make the component appear in the Component Gallery: - // 2. Register with the STANDARD type name - import { componentRegistry } from "@a2ui/lit/ui"; - componentRegistry.register( - "TextField", - MyPremiumTextField, - "my-premium-textfield" - ); +1. **Agent side** (`gallery_examples.py`): Add demo data to `gallery_data_content` and a surface via `add_demo_surface()`: + + ```python + add_demo_surface("demo-my-component", { + "MyComponent": { + "myProp": {"literalString": "Hello World"}, + } + }) ``` -**Result:** -When the server sends a `TextField` component, the client will now render `` instead of the default ``. +2. **Client side** (`component-gallery.ts`): Add an entry to the `DEMO_ITEMS` array: + + ```typescript + { id: "demo-my-component", title: "MyComponent", description: "My custom component." }, + ``` ## Verify -You can verify the component by creating a simple HTML test file or by sending a server message with the new component type. +You can verify the component by creating a simple HTML test file (see `test/` directory for examples) or by sending a server message with the new component type. **Server message example:** ```json { - "surfaceId": "main", - "component": { - "type": "MyComponent", - "id": "comp-1", - "properties": { - "myProp": "Hello World" - } + "surfaceUpdate": { + "surfaceId": "main", + "components": [{ + "id": "comp-1", + "component": { + "MyComponent": { + "myProp": { "literalString": "Hello World" } + } + } + }] } } ``` ## Troubleshooting -- **`NotSupportedError`**: If you see "constructor has already been used", ensure you **removed** the `@customElement` decorator from your component class. -- **Component not rendering**: Check if `registerCustomComponents()` is actually called. Verify the tag name in the DOM matches what you registered (e.g., `` vs ``). +- **`NotSupportedError: constructor has already been used`**: Remove the `@customElement()` decorator from your component class — the registry handles element definition via `customElements.define()`. +- **Component not rendering**: Check that `registerContactComponents()` is called in `client.ts`. Verify the tag name in the DOM matches what you registered. - **Styles missing**: Ensure `static styles` includes `...Root.styles`. +- **Path data returns null**: Verify the surface has a `dataModelUpdate` with the data, and that the path matches (e.g., `galleryData/myField`). Check that `this.processor` and `this.surfaceId` are set (they are assigned by `renderCustomComponent` in the renderer). +- **Nested objects are Maps**: The model processor converts JSON objects to `Map` instances internally. Use `instanceof Map` checks and `.get(key)` instead of property access. diff --git a/samples/client/lit/component_gallery/ui/custom-components/chart.ts b/samples/client/lit/component_gallery/ui/custom-components/chart.ts new file mode 100644 index 000000000..2a230b585 --- /dev/null +++ b/samples/client/lit/component_gallery/ui/custom-components/chart.ts @@ -0,0 +1,496 @@ +/** + * A2UI Custom Chart Component — Lit implementation of the RIZZcharts Chart + * contract. + * + * Follows the same data resolution pattern as the OrgChart Lit sample: + * properties may be plain values or { path: string } references resolved + * via processor.getData(). + * + * Supports chart types: pie, doughnut, bar. + * Supports one-level drill-down: click a wedge/bar or legend label to view + * sub-data, then click the back button to return to the root view. + */ + +import {Root} from '@a2ui/lit/ui'; +import {html, css, nothing} from 'lit'; +import {property} from 'lit/decorators.js'; +import { + Chart as ChartJS, + ArcElement, + BarElement, + BarController, + PieController, + DoughnutController, + CategoryScale, + LinearScale, + Tooltip, + Legend, + Title, + type ChartConfiguration, +} from 'chart.js'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; +import type {Context as DatalabelsContext} from 'chartjs-plugin-datalabels'; + +// Register the Chart.js components we need (controllers + elements + plugins) +ChartJS.register( + PieController, + DoughnutController, + BarController, + ArcElement, + BarElement, + CategoryScale, + LinearScale, + Tooltip, + Legend, + Title, + ChartDataLabels +); + +// Palette CSS custom property names and their default values. +// Override via --chart-color-0 … --chart-color-9 on a parent element. +const PALETTE_DEFAULTS = [ + '#4285F4', // blue + '#EA4335', // red + '#FBBC04', // yellow + '#34A853', // green + '#FF6D01', // orange + '#46BDC6', // teal + '#7B61FF', // purple + '#F538A0', // pink + '#00ACC1', // cyan + '#8D6E63', // brown +]; + +interface ChartDataItem { + label: string; + value: number; + drillDown?: ChartDataItem[]; +} + +/** Pre-computed labels + values for a single chart view. */ +interface ChartView { + labels: string[]; + values: number[]; +} + +export class ChartComponent extends Root { + // Properties set by renderCustomComponent via el[prop] = val. + @property({attribute: false}) accessor chartType: string = 'pie'; + @property({attribute: false}) accessor chartTitle: string = ''; + @property({attribute: false}) accessor chartData: unknown = null; + + private chartInstance: ChartJS | null = null; + private selectedCategory: string = 'root'; + + private get isDrillDown(): boolean { + return this.selectedCategory !== 'root'; + } + + static styles = [ + ...Root.styles, + css` + :host { + display: block; + flex: var(--weight); + padding: 16px; + } + + .chart-box { + background: light-dark(var(--n-98, #fafafa), var(--n-10, #1a1a1a)); + border: 1px solid + light-dark(var(--n-90, #e0e0e0), var(--n-25, #3a3a3a)); + border-radius: 12px; + padding: 24px; + max-width: 700px; + margin: 0 auto; + } + + .chart-header { + margin: 0 0 16px 0; + } + + .chart-heading { + font-size: 20px; + font-weight: 600; + margin: 0; + color: light-dark(var(--n-10, #1a1a1a), var(--n-90, #e0e0e0)); + } + + .chart-subtitle { + font-size: 16px; + font-weight: 500; + margin: 4px 0 0 0; + color: light-dark(var(--n-30, #555), var(--n-70, #b0b0b0)); + } + + .back-btn { + display: inline-flex; + align-items: center; + gap: 4px; + background: none; + border: 1px solid light-dark(var(--n-80, #ccc), var(--n-30, #444)); + border-radius: 6px; + padding: 4px 12px; + margin-bottom: 12px; + cursor: pointer; + font-size: 13px; + color: light-dark(var(--n-20, #333), var(--n-80, #ccc)); + } + + .back-btn:hover { + background: light-dark(var(--n-95, #f0f0f0), var(--n-15, #252525)); + } + + .chart-canvas-wrapper { + position: relative; + width: 100%; + max-height: 400px; + } + + canvas { + width: 100% !important; + max-height: 400px; + } + + .no-data { + text-align: center; + padding: 40px; + color: light-dark(var(--n-40, #888), var(--n-60, #999)); + font-style: italic; + } + `, + ]; + + disconnectedCallback() { + super.disconnectedCallback(); + this.destroyChart(); + } + + updated() { + this.buildChart(); + } + + // --------------------------------------------------------------------------- + // Data resolution + // --------------------------------------------------------------------------- + + private resolveStringProp(val: unknown): string { + if (!val) return ''; + if (typeof val === 'string') return val; + const obj = val as Record; + if (obj.literalString) return String(obj.literalString); + if (obj.path && this.processor) { + const resolved = this.processor.getData( + this.component, + String(obj.path), + this.surfaceId ?? 'default' + ); + return typeof resolved === 'string' ? resolved : ''; + } + return ''; + } + + /** Resolves a path reference via the data model processor. */ + private resolveDataFromPath(raw: unknown): unknown { + if ( + raw && + typeof raw === 'object' && + 'path' in (raw as Record) + ) { + const pathObj = raw as {path: string}; + if (this.processor) { + return this.processor.getData( + this.component, + pathObj.path, + this.surfaceId ?? 'default' + ); + } + } + return raw; + } + + /** Converts a Map (from the model processor) to a sorted Array. */ + private normalizeMapToArray(raw: unknown): unknown { + if (raw instanceof Map) { + const entries = Array.from( + raw.entries() as IterableIterator<[string, unknown]> + ); + entries.sort( + (a, b) => parseInt(a[0], 10) - parseInt(b[0], 10) + ); + return entries.map((e) => e[1]); + } + return raw; + } + + /** Reads a named field from an item (Map or plain object). */ + private static fieldFrom( + item: unknown, + key: string + ): unknown { + if (item instanceof Map) return item.get(key); + return (item as Record)?.[key]; + } + + /** Transforms a raw array into typed ChartDataItems. */ + private transformDataItems( + items: unknown[] + ): ChartDataItem[] { + return items.map((item) => { + let rawDrill = ChartComponent.fieldFrom(item, 'drillDown'); + rawDrill = this.normalizeMapToArray(rawDrill); + + let drillDown: ChartDataItem[] | undefined; + if (Array.isArray(rawDrill) && rawDrill.length > 0) { + drillDown = (rawDrill as unknown[]).map((sub) => ({ + label: String( + ChartComponent.fieldFrom(sub, 'label') ?? '' + ), + value: Number( + ChartComponent.fieldFrom(sub, 'value') ?? 0 + ), + })); + } + + return { + label: String( + ChartComponent.fieldFrom(item, 'label') ?? '' + ), + value: Number( + ChartComponent.fieldFrom(item, 'value') ?? 0 + ), + ...(drillDown ? {drillDown} : {}), + }; + }); + } + + private resolveChartData(): ChartDataItem[] { + let raw: unknown = this.resolveDataFromPath(this.chartData); + raw = this.normalizeMapToArray(raw); + if (!Array.isArray(raw)) return []; + return this.transformDataItems(raw as unknown[]); + } + + // --------------------------------------------------------------------------- + // Drill-down map + // --------------------------------------------------------------------------- + + /** + * Build a Map of chart views keyed by category label. + * "root" -> root-level data; each label with drillDown -> sub-data. + */ + private buildDrillDownMap( + items: ChartDataItem[] + ): Map { + const viewMap = new Map(); + + viewMap.set('root', { + labels: items.map((d) => d.label), + values: items.map((d) => d.value), + }); + + for (const item of items) { + if (item.drillDown && item.drillDown.length > 0) { + viewMap.set(item.label, { + labels: item.drillDown.map((d) => d.label), + values: item.drillDown.map((d) => d.value), + }); + } + } + + return viewMap; + } + + // --------------------------------------------------------------------------- + // Chart lifecycle + // --------------------------------------------------------------------------- + + /** Resolves palette colors from CSS custom properties with fallbacks. */ + private resolvePalette(): string[] { + const styles = getComputedStyle(this); + return PALETTE_DEFAULTS.map((fallback, i) => { + const v = styles.getPropertyValue(`--chart-color-${i}`); + return v.trim() || fallback; + }); + } + + private destroyChart() { + if (this.chartInstance) { + this.chartInstance.destroy(); + this.chartInstance = null; + } + } + + private restoreRoot() { + this.selectedCategory = 'root'; + this.requestUpdate(); + } + + private buildChart() { + const canvas = this.renderRoot?.querySelector('canvas') as + | HTMLCanvasElement + | undefined; + if (!canvas) return; + + const items = this.resolveChartData(); + if (items.length === 0) return; + + const drillDownMap = this.buildDrillDownMap(items); + const view = drillDownMap.get(this.selectedCategory); + if (!view) return; + + const {labels, values} = view; + const palette = this.resolvePalette(); + const colors = labels.map( + (_, i) => palette[i % palette.length] + ); + + this.destroyChart(); + + const ct = + (this.chartType || 'pie') as 'pie' | 'doughnut' | 'bar'; + const isPieType = ct === 'pie' || ct === 'doughnut'; + + const config: ChartConfiguration = { + type: isPieType ? ct : 'bar', + data: { + labels, + datasets: [ + { + data: values, + backgroundColor: colors, + borderColor: isPieType + ? colors.map(() => 'rgba(255,255,255,0.8)') + : colors, + borderWidth: isPieType ? 2 : 1, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: true, + // Wedge / bar click -> drill down + onClick: (_event, elements) => { + if (!elements.length || this.isDrillDown) return; + const index = elements[0].index; + const label = labels[index]; + if (label && drillDownMap.has(label)) { + this.selectedCategory = label; + this.requestUpdate(); + } + }, + plugins: { + legend: { + display: isPieType, + position: 'right', + labels: {font: {size: 13}}, + // Legend click -> drill down (pie/doughnut only) + onClick: (_e, legendItem) => { + if (this.isDrillDown) return; + const label = legendItem.text; + if (label && drillDownMap.has(label)) { + this.selectedCategory = label; + this.requestUpdate(); + } + }, + }, + title: {display: false}, + tooltip: { + callbacks: { + label: (ctx) => { + const val = ctx.parsed; + const num = + typeof val === 'number' + ? val + : (typeof val === 'object' && val !== null && + 'y' in val + ? (val as Record).y + : (val as number)); + if (isPieType) { + const total = ( + ctx.dataset.data as number[] + ).reduce((a, b) => a + (b as number), 0); + const pct = ( + ((num as number) / total) * + 100 + ).toFixed(1); + return `${ctx.label}: ${(num as number).toLocaleString()} (${pct}%)`; + } + return `${ctx.label}: ${(num as number).toLocaleString()}`; + }, + }, + }, + // On-wedge percentage labels (pie/doughnut only) + datalabels: isPieType + ? { + formatter: (value: number, ctx: DatalabelsContext) => { + const total = ( + ctx.chart.data.datasets[0].data as number[] + ).reduce((a, b) => a + b, 0); + return `${((value / total) * 100).toFixed(1)}%`; + }, + color: 'white', + font: {size: 14}, + } + : {display: false}, + }, + ...(isPieType + ? {} + : { + scales: { + y: { + beginAtZero: true, + ticks: {font: {size: 12}}, + }, + x: { + ticks: {font: {size: 12}, maxRotation: 45}, + }, + }, + }), + }, + }; + + this.chartInstance = new ChartJS(canvas, config); + } + + // --------------------------------------------------------------------------- + // Render + // --------------------------------------------------------------------------- + + render() { + const heading = this.resolveStringProp(this.chartTitle); + const items = this.resolveChartData(); + + if (items.length === 0) { + return html`
+
No chart data available
+
`; + } + + return html` +
+
+ ${heading + ? html`

${heading}

` + : nothing} + ${this.isDrillDown + ? html`

+ ${this.selectedCategory} +

` + : nothing} +
+ ${this.isDrillDown + ? html`` + : nothing} +
+ +
+
+ `; + } +} diff --git a/samples/client/lit/component_gallery/ui/custom-components/register-components.ts b/samples/client/lit/component_gallery/ui/custom-components/register-components.ts index 047a0438c..38d5bde31 100644 --- a/samples/client/lit/component_gallery/ui/custom-components/register-components.ts +++ b/samples/client/lit/component_gallery/ui/custom-components/register-components.ts @@ -15,11 +15,65 @@ */ import { componentRegistry } from "@a2ui/lit/ui"; +import { ChartComponent } from "./chart.js"; import { OrgChart } from "./org-chart.js"; import { WebFrame } from "./web-frame.js"; import { PremiumTextField } from "./premium-text-field.js"; export function registerContactComponents() { + // Register Chart + componentRegistry.register("Chart", ChartComponent, "a2ui-custom-chart", { + type: "object", + description: + "An interactive chart component. Supports pie, doughnut, and bar chart types with one-level drill-down.", + properties: { + chartType: { + type: "string", + enum: ["pie", "doughnut", "bar"], + }, + chartTitle: { + type: "object", + properties: { + literalString: { type: "string" }, + path: { type: "string" }, + }, + }, + chartData: { + type: "object", + description: + "The data for the chart. Can be a literal array or a data model path.", + properties: { + literalArray: { + type: "array", + items: { + type: "object", + properties: { + label: { type: "string" }, + value: { type: "number" }, + drillDown: { + type: "array", + description: + "An optional list of items for the next level of data.", + items: { + type: "object", + properties: { + label: { type: "string" }, + value: { type: "number" }, + }, + required: ["label", "value"], + }, + }, + }, + required: ["label", "value"], + }, + }, + path: { type: "string" }, + }, + }, + }, + required: ["chartType", "chartData"], + }); + // Register OrgChart componentRegistry.register("OrgChart", OrgChart, "org-chart", { type: "object",