diff --git a/examples/material-ui-chat/.env.example b/examples/material-ui-chat/.env.example
new file mode 100644
index 000000000..f750f24de
--- /dev/null
+++ b/examples/material-ui-chat/.env.example
@@ -0,0 +1,3 @@
+# OpenAI API key used by the chat route (src/app/api/chat/route.ts).
+# Copy this file to `.env.local` and fill in your key.
+OPENAI_API_KEY=
diff --git a/examples/material-ui-chat/.gitignore b/examples/material-ui-chat/.gitignore
new file mode 100644
index 000000000..09d505398
--- /dev/null
+++ b/examples/material-ui-chat/.gitignore
@@ -0,0 +1,5 @@
+.next
+node_modules
+.env
+.env.local
+*.tsbuildinfo
diff --git a/examples/material-ui-chat/README.md b/examples/material-ui-chat/README.md
new file mode 100644
index 000000000..3a24912db
--- /dev/null
+++ b/examples/material-ui-chat/README.md
@@ -0,0 +1,136 @@
+# Material UI Chat Example
+
+A full-stack generative UI chatbot that wires [OpenUI Lang](https://www.openui.com/docs/openui-lang/overview) to a custom component library built on [Material UI](https://mui.com/material-ui/). Instead of replying with plain text, the LLM generates structured UI markup that the client renders as MUI components — cards, tables, charts, forms, tabs, accordions, alerts, lists, and more — in real time as tokens stream in.
+
+This is the MUI counterpart to the [`shadcn-chat`](../shadcn-chat) example. It shows how to map OpenUI Lang nodes onto an existing design system (MUI components + theme), including light/dark mode.
+
+## How It Works
+
+The LLM is given a system prompt that describes every available Material UI component — its name, props, and when to use it. Instead of prose, the model responds in **OpenUI Lang**, a declarative markup that maps directly to React components:
+
+```
+root = Card([header, tbl])
+header = CardHeader("Q1 Sales")
+tbl = Table([Col("Product"), Col("Revenue", "number")], [["Widget", 1200]])
+```
+
+On the client, `` from `@openuidev/react-ui` manages conversation state, streaming, input, and rendering. It parses the incoming SSE stream with `openAIAdapter()` and renders each OpenUI Lang node using `muiChatLibrary` — the custom component library defined in `src/lib/mui-genui/`.
+
+## Architecture
+
+```
+┌────────────────────────────────────┐ ┌────────────────────────────────────┐
+│ Browser │ HTTP │ Next.js API Route │
+│ │ ──────►│ │
+│ • manages UI │ │ • Loads system-prompt.txt │
+│ • openAIAdapter() parses SSE │◄────── │ • Calls LLM with runTools │
+│ • muiChatLibrary renders nodes │ SSE │ • Executes tools server-side │
+│ • MUI ThemeProvider + CssBaseline │ │ • Streams response as SSE events │
+└────────────────────────────────────┘ └────────────────────────────────────┘
+```
+
+1. The user types a message. `` calls `processMessage`, which `POST`s to `/api/chat` with the conversation history formatted via `openAIMessageFormat.toApi()`.
+2. The API route reads `src/generated/system-prompt.txt`, instantiates an OpenAI client, and calls `runTools` — the OpenAI SDK's multi-step tool-execution loop.
+3. Tool calls run server-side; results are fed back to the model automatically and emitted as SSE events.
+4. The LLM streams a final OpenUI Lang response. The stream ends with `data: [DONE]`.
+5. The client parses the events with `openAIAdapter()` and renders each node as a Material UI component as it streams in.
+
+## Project Structure
+
+```
+material-ui-chat/
+├── src/
+│ ├── library.ts # Entry the OpenUI CLI reads to generate the prompt
+│ ├── app/
+│ │ ├── api/chat/route.ts # Streaming chat endpoint (OpenAI SDK + SSE)
+│ │ ├── page.tsx # Mounts + color-mode toggle
+│ │ ├── layout.tsx # Root layout with ColorModeProvider
+│ │ └── globals.css # Minimal full-height reset
+│ ├── hooks/
+│ │ └── use-system-theme.tsx # MUI ThemeProvider + light/dark color mode
+│ ├── lib/
+│ │ └── mui-genui/ # Custom OpenUI component library (Material UI)
+│ │ ├── index.tsx # Library export — createLibrary() call
+│ │ ├── theme.ts # MUI theme factory + chart palette
+│ │ ├── action.ts # Button action Zod schemas
+│ │ ├── helpers.ts # Chart data builders for @mui/x-charts
+│ │ ├── rules.ts # Form validation rule schemas
+│ │ ├── unions.ts # Zod union types for component children
+│ │ └── components/ # One file per component (MUI wrappers)
+│ └── generated/
+│ └── system-prompt.txt # Auto-generated — do not edit manually
+└── package.json
+```
+
+## Components
+
+The library exposes a representative subset of Material UI components mapped to OpenUI Lang:
+
+| Category | Components |
+| ---------- | ---------- |
+| Content | `CardHeader`, `TextContent`, `Heading`, `Alert`, `List` / `ListItem`, `Separator`, `Progress` |
+| Tables | `Table` / `Col` |
+| Charts | `BarChart`, `LineChart`, `PieChart` (via `@mui/x-charts`) with `Series` / `Slice` |
+| Forms | `Form`, `FormControl`, `Input`, `Select` / `SelectItem`, `SwitchGroup` / `SwitchItem` |
+| Buttons | `Button`, `Buttons` |
+| Layout | `Tabs` / `TabItem`, `Accordion` / `AccordionItem` |
+| Follow-ups | `FollowUpBlock` / `FollowUpItem` |
+
+Each component is defined with `defineComponent({ name, props, description, component })` where `props` is a Zod schema. The schema and description are serialized into the system prompt by `pnpm generate:prompt` (the OpenUI CLI reads `src/library.ts`), and `component` renders the node with Material UI primitives.
+
+## Theming
+
+The app wraps everything in MUI's `ThemeProvider` + `CssBaseline` via `ColorModeProvider` (`src/hooks/use-system-theme.tsx`). It follows the OS color scheme by default and exposes a manual light/dark toggle (top-right of the screen). All generated components — and the chat surface — re-theme automatically. Customize `createAppTheme()` in `src/lib/mui-genui/theme.ts`.
+
+## Getting Started
+
+### Prerequisites
+
+- Node.js 20.x
+- pnpm 9+
+- An OpenAI API key
+
+### Setup
+
+From the monorepo root, install dependencies (this example is part of the pnpm workspace):
+
+```bash
+pnpm install
+```
+
+Provide your API key:
+
+```bash
+cd examples/material-ui-chat
+cp .env.example .env.local
+# edit .env.local and set OPENAI_API_KEY
+```
+
+### Develop
+
+```bash
+pnpm dev
+```
+
+`pnpm dev` first runs `generate:prompt` to (re)generate `src/generated/system-prompt.txt` from the library, then starts Next.js on http://localhost:3000.
+
+### Regenerate the system prompt
+
+Whenever you add or change a component, regenerate the prompt:
+
+```bash
+pnpm generate:prompt
+```
+
+### Build
+
+```bash
+pnpm build
+```
+
+## Adding a Component
+
+1. Create `src/lib/mui-genui/components/.tsx` and export a `defineComponent({ ... })`.
+2. If it can appear inside other containers, add its `.ref` to `ContentChildUnion` in `unions.ts`.
+3. Register it in the `components` array (and a `componentGroups` entry) in `index.tsx`.
+4. Run `pnpm generate:prompt` so the LLM learns about it.
diff --git a/examples/material-ui-chat/next-env.d.ts b/examples/material-ui-chat/next-env.d.ts
new file mode 100644
index 000000000..c4b7818fb
--- /dev/null
+++ b/examples/material-ui-chat/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+import "./.next/dev/types/routes.d.ts";
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/examples/material-ui-chat/next.config.ts b/examples/material-ui-chat/next.config.ts
new file mode 100644
index 000000000..b08275c80
--- /dev/null
+++ b/examples/material-ui-chat/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ turbopack: {},
+};
+
+export default nextConfig;
diff --git a/examples/material-ui-chat/package.json b/examples/material-ui-chat/package.json
new file mode 100644
index 000000000..6ce7ee5d5
--- /dev/null
+++ b/examples/material-ui-chat/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "material-ui-chat",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "generate:prompt": "pnpm --filter @openuidev/cli build && pnpm exec openui generate src/library.ts --out src/generated/system-prompt.txt",
+ "dev": "pnpm generate:prompt && next dev",
+ "build": "pnpm generate:prompt && next build",
+ "start": "next start",
+ "lint": "eslint"
+ },
+ "dependencies": {
+ "@emotion/react": "^11.13.5",
+ "@emotion/styled": "^11.13.5",
+ "@mui/icons-material": "^6.4.12",
+ "@mui/material": "^6.4.12",
+ "@mui/x-charts": "^7.29.1",
+ "@openuidev/react-headless": "workspace:*",
+ "@openuidev/react-lang": "workspace:*",
+ "@openuidev/react-ui": "workspace:*",
+ "next": "16.1.6",
+ "openai": "^6.22.0",
+ "react": "19.2.3",
+ "react-dom": "19.2.3",
+ "zod": "^4.0.0"
+ },
+ "devDependencies": {
+ "@openuidev/cli": "workspace:*",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "16.1.6",
+ "typescript": "^5"
+ }
+}
diff --git a/examples/material-ui-chat/src/app/api/chat/route.ts b/examples/material-ui-chat/src/app/api/chat/route.ts
new file mode 100644
index 000000000..4c0ffdb77
--- /dev/null
+++ b/examples/material-ui-chat/src/app/api/chat/route.ts
@@ -0,0 +1,411 @@
+import { readFileSync } from "fs";
+import { NextRequest } from "next/server";
+import OpenAI from "openai";
+import type { ChatCompletionMessageParam } from "openai/resources/chat/completions.mjs";
+import { join } from "path";
+
+const systemPrompt = readFileSync(join(process.cwd(), "src/generated/system-prompt.txt"), "utf-8");
+
+const conversationLog: Array<{ role: string; content: string }> = [];
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+function extractText(msg: any): string {
+ const content = msg?.content;
+ if (typeof content === "string") {
+ try {
+ const parsed = JSON.parse(content);
+ if (parsed?.parts)
+ return parsed.parts
+ .filter((p: any) => p.type === "text")
+ .map((p: any) => p.text)
+ .join("");
+ } catch {
+ /* plain string */
+ }
+ return content;
+ }
+ if (Array.isArray(content))
+ return content
+ .filter((p: any) => p.type === "text")
+ .map((p: any) => p.text)
+ .join("");
+ if (Array.isArray(msg?.parts))
+ return msg.parts
+ .filter((p: any) => p.type === "text")
+ .map((p: any) => p.text)
+ .join("");
+ if (typeof msg?.text === "string") return msg.text;
+ return JSON.stringify(msg);
+}
+/* eslint-enable @typescript-eslint/no-explicit-any */
+
+// ── Tool implementations ──
+
+function getWeather({ location }: { location: string }): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const knownTemps: Record = {
+ tokyo: 22,
+ "san francisco": 18,
+ london: 14,
+ "new york": 25,
+ paris: 19,
+ sydney: 27,
+ mumbai: 33,
+ berlin: 16,
+ };
+ const conditions = ["Sunny", "Partly Cloudy", "Cloudy", "Light Rain", "Clear Skies"];
+ const temp = knownTemps[location.toLowerCase()] ?? Math.floor(Math.random() * 30 + 5);
+ const condition = conditions[Math.floor(Math.random() * conditions.length)];
+ resolve(
+ JSON.stringify({
+ location,
+ temperature_celsius: temp,
+ temperature_fahrenheit: Math.round(temp * 1.8 + 32),
+ condition,
+ humidity_percent: Math.floor(Math.random() * 40 + 40),
+ wind_speed_kmh: Math.floor(Math.random() * 25 + 5),
+ forecast: [
+ { day: "Tomorrow", high: temp + 2, low: temp - 4, condition: "Partly Cloudy" },
+ { day: "Day After", high: temp + 1, low: temp - 3, condition: "Sunny" },
+ ],
+ }),
+ );
+ }, 800);
+ });
+}
+
+function getStockPrice({ symbol }: { symbol: string }): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const s = symbol.toUpperCase();
+ const knownPrices: Record = {
+ AAPL: 189.84,
+ GOOGL: 141.8,
+ TSLA: 248.42,
+ MSFT: 378.91,
+ AMZN: 178.25,
+ NVDA: 875.28,
+ META: 485.58,
+ };
+ const price = knownPrices[s] ?? Math.floor(Math.random() * 500 + 20);
+ const change = parseFloat((Math.random() * 8 - 4).toFixed(2));
+ resolve(
+ JSON.stringify({
+ symbol: s,
+ price: parseFloat((price + change).toFixed(2)),
+ change,
+ change_percent: parseFloat(((change / price) * 100).toFixed(2)),
+ volume: `${(Math.random() * 50 + 10).toFixed(1)}M`,
+ day_high: parseFloat((price + Math.abs(change) + 1.5).toFixed(2)),
+ day_low: parseFloat((price - Math.abs(change) - 1.2).toFixed(2)),
+ }),
+ );
+ }, 600);
+ });
+}
+
+function calculate({ expression }: { expression: string }): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ try {
+ const sanitized = expression.replace(
+ /[^0-9+\-*/().%\s,Math.sqrtpowabsceilfloorround]/g,
+ "",
+ );
+
+ const result = new Function(`return (${sanitized})`)();
+ resolve(JSON.stringify({ expression, result: Number(result) }));
+ } catch {
+ resolve(JSON.stringify({ expression, error: "Invalid expression" }));
+ }
+ }, 300);
+ });
+}
+
+function searchWeb({ query }: { query: string }): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ JSON.stringify({
+ query,
+ results: [
+ {
+ title: `Top result for "${query}"`,
+ snippet: `Comprehensive overview of ${query} with the latest information.`,
+ },
+ {
+ title: `${query} - Latest News`,
+ snippet: `Recent developments and updates related to ${query}.`,
+ },
+ {
+ title: `Understanding ${query}`,
+ snippet: `An in-depth guide explaining everything about ${query}.`,
+ },
+ ],
+ }),
+ );
+ }, 1000);
+ });
+}
+
+// ── Tool definitions ──
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const tools: any[] = [
+ {
+ type: "function",
+ function: {
+ name: "get_weather",
+ description: "Get current weather for a location.",
+ parameters: {
+ type: "object",
+ properties: { location: { type: "string", description: "City name" } },
+ required: ["location"],
+ },
+ function: getWeather,
+ parse: JSON.parse,
+ },
+ },
+ {
+ type: "function",
+ function: {
+ name: "get_stock_price",
+ description: "Get stock price for a ticker symbol.",
+ parameters: {
+ type: "object",
+ properties: { symbol: { type: "string", description: "Ticker symbol, e.g. AAPL" } },
+ required: ["symbol"],
+ },
+ function: getStockPrice,
+ parse: JSON.parse,
+ },
+ },
+ {
+ type: "function",
+ function: {
+ name: "calculate",
+ description: "Evaluate a math expression.",
+ parameters: {
+ type: "object",
+ properties: { expression: { type: "string", description: "Math expression to evaluate" } },
+ required: ["expression"],
+ },
+ function: calculate,
+ parse: JSON.parse,
+ },
+ },
+ {
+ type: "function",
+ function: {
+ name: "search_web",
+ description: "Search the web for information.",
+ parameters: {
+ type: "object",
+ properties: { query: { type: "string", description: "Search query" } },
+ required: ["query"],
+ },
+ function: searchWeb,
+ parse: JSON.parse,
+ },
+ },
+];
+
+// ── SSE helpers ──
+
+function sseToolCallStart(
+ encoder: TextEncoder,
+ tc: { id: string; function: { name: string } },
+ index: number,
+) {
+ return encoder.encode(
+ `data: ${JSON.stringify({
+ id: `chatcmpl-tc-${tc.id}`,
+ object: "chat.completion.chunk",
+ choices: [
+ {
+ index: 0,
+ delta: {
+ tool_calls: [
+ {
+ index,
+ id: tc.id,
+ type: "function",
+ function: { name: tc.function.name, arguments: "" },
+ },
+ ],
+ },
+ finish_reason: null,
+ },
+ ],
+ })}\n\n`,
+ );
+}
+
+function sseToolCallArgs(
+ encoder: TextEncoder,
+ tc: { id: string; function: { arguments: string } },
+ result: string,
+ index: number,
+) {
+ let enrichedArgs: string;
+ try {
+ enrichedArgs = JSON.stringify({
+ _request: JSON.parse(tc.function.arguments),
+ _response: JSON.parse(result),
+ });
+ } catch {
+ enrichedArgs = tc.function.arguments;
+ }
+ return encoder.encode(
+ `data: ${JSON.stringify({
+ id: `chatcmpl-tc-${tc.id}-args`,
+ object: "chat.completion.chunk",
+ choices: [
+ {
+ index: 0,
+ delta: { tool_calls: [{ index, function: { arguments: enrichedArgs } }] },
+ finish_reason: null,
+ },
+ ],
+ })}\n\n`,
+ );
+}
+
+// ── Route handler ──
+
+export async function POST(req: NextRequest) {
+ const { messages } = await req.json();
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const lastUserMsg = (messages as any[]).filter((m: any) => m.role === "user").pop();
+ if (lastUserMsg) conversationLog.push({ role: "user", content: extractText(lastUserMsg) });
+
+ const client = new OpenAI({
+ apiKey: process.env.OPENAI_API_KEY,
+ });
+ const MODEL = "gpt-5.5";
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const cleanMessages = (messages as any[])
+ .filter((m) => m.role !== "tool")
+ .map((m) => {
+ if (m.role === "assistant" && m.tool_calls?.length) {
+ // Strip tool_calls (runTools re-runs the agentic loop server-side)
+ // but preserve content so prior replies remain in context.
+ const { tool_calls: _tc, ...rest } = m; // eslint-disable-line @typescript-eslint/no-unused-vars
+ return rest;
+ }
+ return m;
+ });
+
+ const chatMessages: ChatCompletionMessageParam[] = [
+ { role: "system", content: systemPrompt },
+ ...cleanMessages,
+ ];
+
+ const encoder = new TextEncoder();
+ let controllerClosed = false;
+
+ const readable = new ReadableStream({
+ start(controller) {
+ const enqueue = (data: Uint8Array) => {
+ if (controllerClosed) return;
+ try {
+ controller.enqueue(data);
+ } catch {
+ /* already closed */
+ }
+ };
+ const close = () => {
+ if (controllerClosed) return;
+ controllerClosed = true;
+ try {
+ controller.close();
+ } catch {
+ /* already closed */
+ }
+ };
+
+ let fullResponse = "";
+ const pendingCalls: Array<{ id: string; name: string; arguments: string }> = [];
+ let callIdx = 0;
+ let resultIdx = 0;
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const runner = (client.chat.completions as any).runTools({
+ model: MODEL,
+ messages: chatMessages,
+ tools,
+ stream: true,
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ runner.on("functionToolCall", (fc: any) => {
+ const id = `tc-${callIdx}`;
+ pendingCalls.push({ id, name: fc.name, arguments: fc.arguments });
+ enqueue(sseToolCallStart(encoder, { id, function: { name: fc.name } }, callIdx));
+ callIdx++;
+ });
+
+ runner.on("functionToolCallResult", (result: string) => {
+ const tc = pendingCalls[resultIdx];
+ if (tc) {
+ enqueue(
+ sseToolCallArgs(
+ encoder,
+ { id: tc.id, function: { arguments: tc.arguments } },
+ result,
+ resultIdx,
+ ),
+ );
+ }
+ resultIdx++;
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ runner.on("chunk", (chunk: any) => {
+ const choice = chunk.choices?.[0];
+ const delta = choice?.delta;
+ if (!delta) return;
+ if (delta.content) {
+ fullResponse += delta.content;
+ enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
+ }
+ if (choice?.finish_reason === "stop") {
+ enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
+ }
+ });
+
+ runner.on("end", () => {
+ conversationLog.push({ role: "assistant", content: fullResponse });
+ console.info(
+ "[OpenUI Lang] Conversation:\n",
+ JSON.stringify(
+ conversationLog.map((m) => ({ ...m, content: m.content.replace(/\n/g, " ") })),
+ null,
+ 2,
+ ),
+ );
+ enqueue(encoder.encode("data: [DONE]\n\n"));
+ close();
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ runner.on("error", (err: any) => {
+ const msg = err instanceof Error ? err.message : "Stream error";
+ console.error("Chat route error:", msg);
+ enqueue(encoder.encode(`data: ${JSON.stringify({ error: msg })}\n\n`));
+ close();
+ });
+ },
+ });
+
+ return new Response(readable, {
+ headers: {
+ "Content-Type": "text/event-stream",
+ "Cache-Control": "no-cache, no-transform",
+ Connection: "keep-alive",
+ },
+ });
+}
diff --git a/examples/material-ui-chat/src/app/globals.css b/examples/material-ui-chat/src/app/globals.css
new file mode 100644
index 000000000..dfe06a90b
--- /dev/null
+++ b/examples/material-ui-chat/src/app/globals.css
@@ -0,0 +1,11 @@
+html,
+body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+#__next,
+body > div {
+ height: 100%;
+}
diff --git a/examples/material-ui-chat/src/app/layout.tsx b/examples/material-ui-chat/src/app/layout.tsx
new file mode 100644
index 000000000..ab4b43ba4
--- /dev/null
+++ b/examples/material-ui-chat/src/app/layout.tsx
@@ -0,0 +1,23 @@
+import type { Metadata } from "next";
+
+import { ColorModeProvider } from "@/hooks/use-system-theme";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "Material UI Chat",
+ description: "Generative UI Chat with Material UI components",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/examples/material-ui-chat/src/app/page.tsx b/examples/material-ui-chat/src/app/page.tsx
new file mode 100644
index 000000000..27a218a7d
--- /dev/null
+++ b/examples/material-ui-chat/src/app/page.tsx
@@ -0,0 +1,76 @@
+"use client";
+import "@openuidev/react-ui/components.css";
+
+import DarkModeIcon from "@mui/icons-material/DarkMode";
+import LightModeIcon from "@mui/icons-material/LightMode";
+import Box from "@mui/material/Box";
+import IconButton from "@mui/material/IconButton";
+import Tooltip from "@mui/material/Tooltip";
+import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless";
+import { FullScreen } from "@openuidev/react-ui";
+
+import { useColorMode } from "@/hooks/use-system-theme";
+import { muiChatLibrary } from "@/lib/mui-genui";
+
+export default function Page() {
+ const { mode, toggle } = useColorMode();
+
+ return (
+
+
+
+
+ {mode === "dark" ? : }
+
+
+
+
+ {
+ return fetch("/api/chat", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ messages: openAIMessageFormat.toApi(messages),
+ }),
+ signal: abortController.signal,
+ });
+ }}
+ streamProtocol={openAIAdapter()}
+ componentLibrary={muiChatLibrary}
+ agentName="Material UI Chat"
+ theme={{ mode }}
+ conversationStarters={{
+ variant: "short",
+ options: [
+ {
+ displayText: "Startup dashboard",
+ prompt:
+ "Build a startup analytics dashboard with a CardHeader, Tabs (Revenue BarChart, Growth LineChart, Breakdown PieChart), a key metrics Table, a progress bar toward the annual goal, and follow-ups.",
+ },
+ {
+ displayText: "Onboarding form",
+ prompt:
+ "Create an account onboarding form with an info Alert, then a Form containing inputs for name and email, a Select for plan, and a SwitchGroup for notification preferences. Add follow-ups.",
+ },
+ {
+ displayText: "Project status",
+ prompt:
+ "Generate a project status report with a CardHeader, a warning Alert for blockers, a List of this week's tasks, an Accordion (Done, In progress, Blocked), and a BarChart of story points per engineer. Add follow-ups.",
+ },
+ {
+ displayText: "Pricing comparison",
+ prompt:
+ "Show a pricing comparison with a CardHeader, a Table comparing three plans, a List of included features, and buttons to choose a plan. Add follow-ups.",
+ },
+ {
+ displayText: "Chart showcase",
+ prompt:
+ "Build a 'Quarterly Report' with Tabs containing a grouped BarChart (revenue by region, 2 series), a LineChart (monthly active users, 2 series), and a donut PieChart (traffic sources, 4 slices). Below, add a summary Table and follow-ups.",
+ },
+ ],
+ }}
+ />
+
+ );
+}
diff --git a/examples/material-ui-chat/src/generated/system-prompt.txt b/examples/material-ui-chat/src/generated/system-prompt.txt
new file mode 100644
index 000000000..0fc1dffd3
--- /dev/null
+++ b/examples/material-ui-chat/src/generated/system-prompt.txt
@@ -0,0 +1,176 @@
+You are an AI assistant that responds using openui-lang, a declarative UI language. Your ENTIRE response must be valid openui-lang code — no markdown, no explanations, just openui-lang.
+
+## Syntax Rules
+
+1. Each statement is on its own line: `identifier = Expression`
+2. `root` is the entry point — every program must define `root = Card(...)`
+3. Expressions are: strings ("..."), numbers, booleans (true/false), null, arrays ([...]), objects ({...}), or component calls TypeName(arg1, arg2, ...)
+4. Use references for readability: define `name = ...` on one line, then use `name` later
+5. EVERY variable (except root) MUST be referenced by at least one other variable. Unreferenced variables are silently dropped and will NOT render. Always include defined variables in their parent's children/items array.
+6. Arguments are POSITIONAL (order matters, not names). Write `Stack([children], "row", "l")` NOT `Stack([children], direction: "row", gap: "l")` — colon syntax is NOT supported and silently breaks
+7. Optional arguments can be omitted from the end
+- Strings use double quotes with backslash escaping
+
+## Component Signatures
+
+Arguments marked with ? are optional. Sub-components can be inline or referenced; prefer references for better streaming.
+
+### Content
+CardHeader(title: string, description?: string) — Title/description header block for a Card.
+TextContent(text: string, size?: "small" | "default" | "large" | "small-heavy" | "large-heavy") — Text block with optional size. size: "small" | "default" | "large" | "small-heavy" | "large-heavy".
+Heading(text: string, level?: "h1" | "h2" | "h3" | "h4") — Section heading. level: "h1" | "h2" | "h3" | "h4".
+Alert(title: string, description: string, variant?: "default" | "destructive" | "info" | "success" | "warning") — Alert banner with icon, title, and description. variant: "default" | "destructive" | "info" | "success" | "warning".
+List(items: ListItem[], dense?: boolean) — Vertical list of items. items: ListItem[]. dense for tighter spacing.
+ListItem(primary: string, secondary?: string) — Item in a List. primary text with optional secondary text.
+Separator(orientation?: "horizontal" | "vertical") — Horizontal or vertical rule. orientation: "horizontal" | "vertical".
+Progress(value: number, label?: string) — Progress bar showing completion percentage (0-100). Optional label.
+- Use CardHeader for the main section title. Use Heading for sub-section titles.
+- TextContent size: "small" | "default" | "large" | "small-heavy" | "large-heavy".
+- List takes ListItem[] — each ListItem has primary text and optional secondary text.
+
+### Tables
+Table(columns: Col[], rows: any[][]) — Data table. columns: Col[] with header/type, rows: 2D array of values.
+Col(header: string, type?: "string" | "number" | "boolean") — Column definition for Table — header label and optional type.
+
+### Charts
+BarChart(labels: string[], series: Series[], variant?: "grouped" | "stacked", xLabel?: string, yLabel?: string) — Vertical bar chart. Use for comparing values across categories.
+LineChart(labels: string[], series: Series[], xLabel?: string, yLabel?: string) — Line chart for trends over categories.
+PieChart(slices: Slice[], donut?: boolean) — Pie or donut chart. slices: Slice[], donut: boolean for a ring chart.
+Series(category: string, values: number[]) — One named data series with values matching labels.
+Slice(category: string, value: number) — A single slice in a PieChart.
+- BarChart/LineChart take labels (string[]) and series (Series[]). Each Series has a category name and a values array aligned to labels.
+- PieChart takes slices (Slice[]). Set donut: true for a ring chart.
+
+### Forms
+Form(name: string, buttons: Buttons, fields?: FormControl[]) — Form container with fields and explicit action buttons. fields: FormControl[], buttons: Buttons.
+FormControl(label: string, field: any) — Wraps a form field with a label and error display.
+Input(name: string, placeholder?: string, type?: "text" | "email" | "password" | "number" | "url", rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Text input field. type: "text" | "email" | "password" | "number" | "url". rules for validation.
+Select(name: string, items: SelectItem[], placeholder?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Dropdown select. items: SelectItem[], placeholder, rules for validation.
+SelectItem(value: string, label: string) — Option for Select dropdown.
+SwitchGroup(name: string, items: SwitchItem[]) — Group of toggle switches. items: SwitchItem[].
+SwitchItem(value: string, label: string) — Toggle option in a SwitchGroup.
+- Define EACH FormControl as its own reference — do NOT inline all controls in one array.
+- NEVER nest Form inside Form.
+- Form requires explicit buttons. Always pass a Buttons(...) reference as the second Form argument.
+- rules is an optional object: { required: true, email: true, minLength: 8, maxLength: 100 }.
+- The renderer shows error messages automatically — do NOT generate error text in the UI.
+
+### Buttons
+Button(label: string, action?: {type: "open_url", url: string} | {type: "continue_conversation", context?: string} | {type: string, params?: Record}, variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link", size?: "default" | "xs" | "sm" | "lg" | "icon") — Clickable button. variant: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link". size: "default" | "xs" | "sm" | "lg" | "icon". action: { type: "continue_conversation" | "open_url", url? }.
+Buttons(buttons: Button[], direction?: "row" | "column") — Group of Button components. direction: "row" | "column".
+
+### Layout
+Tabs(items: TabItem[], defaultValue?: string) — Tabbed content. items: TabItem[]. defaultValue: initially active tab.
+TabItem(value: string, trigger: string, content: (CardHeader | TextContent | Heading | Alert | List | Separator | Progress | Table | BarChart | LineChart | PieChart | Form | Buttons)[]) — Tab panel. value: unique id, trigger: tab label, content: children.
+Accordion(items: AccordionItem[], type?: "single" | "multiple") — Collapsible sections. type: "single" | "multiple". items: AccordionItem[].
+AccordionItem(value: string, trigger: string, content: (CardHeader | TextContent | Heading | Alert | List | Separator | Progress | Table | BarChart | LineChart | PieChart | Form | Buttons)[]) — Collapsible item inside Accordion. value: unique id, trigger: header text.
+- Use Tabs to present alternative views — each TabItem has a value id, trigger label, and content array.
+- Accordion type: "single" | "multiple". items: AccordionItem[].
+
+### Follow-ups
+FollowUpBlock(items: FollowUpItem[]) — List of follow-up suggestion chips at the end of a response.
+FollowUpItem(text: string) — Clickable follow-up suggestion — sends text as a user message when clicked.
+- Use FollowUpBlock with FollowUpItem references at the END of a response to suggest next actions.
+- Clicking a FollowUpItem sends its text to the LLM as a user message.
+
+### Other
+Card(children: (CardHeader | TextContent | Heading | Alert | List | Separator | Progress | Table | BarChart | LineChart | PieChart | Form | Buttons | FollowUpBlock | Tabs | Accordion)[]) — Vertical container for all content in a chat response. Children stack top to bottom automatically.
+
+## Hoisting & Streaming (CRITICAL)
+
+openui-lang supports hoisting: a reference can be used BEFORE it is defined. The parser resolves all references after the full input is parsed.
+
+During streaming, the output is re-parsed on every chunk. Undefined references are temporarily unresolved and appear once their definitions stream in. This creates a progressive top-down reveal — structure first, then data fills in.
+
+**Recommended statement order for optimal streaming:**
+1. `root = Card(...)` — UI shell appears immediately
+2. Component definitions — fill in as they stream
+3. Data values — leaf content last
+
+Always write the root = Card(...) statement first so the UI shell appears immediately, even before child data has streamed in.
+
+## Examples
+
+Example 1 — Table with follow-ups:
+root = Card([title, tbl, followUps])
+title = CardHeader("Top Languages", "By number of active developers")
+tbl = Table(cols, rows)
+cols = [Col("Language", "string"), Col("Users (M)", "number"), Col("Year", "number")]
+rows = [["Python", 15.7, 1991], ["JavaScript", 14.2, 1995], ["Java", 12.1, 1995]]
+followUps = FollowUpBlock([fu1, fu2])
+fu1 = FollowUpItem("Tell me more about Python")
+fu2 = FollowUpItem("Show me a JavaScript comparison")
+
+Example 2 — Form with validation:
+root = Card([title, form])
+title = CardHeader("Contact Us")
+form = Form("contact", btns, [nameField, emailField, planField])
+nameField = FormControl("Name", Input("name", "Your name", "text", { required: true, minLength: 2 }))
+emailField = FormControl("Email", Input("email", "you@example.com", "email", { required: true, email: true }))
+planField = FormControl("Plan", Select("plan", [opt1, opt2], "Choose a plan", { required: true }))
+opt1 = SelectItem("free", "Free")
+opt2 = SelectItem("pro", "Pro")
+btns = Buttons([Button("Submit", { type: "continue_conversation" }, "default")])
+
+Example 3 — Alert variants and a list:
+root = Card([info, warning, items])
+info = Alert("Update available", "A new version is ready to install.", "info")
+warning = Alert("Disk almost full", "Less than 10% storage remaining.", "warning")
+items = List([i1, i2, i3])
+i1 = ListItem("Back up your data", "Recommended before updating")
+i2 = ListItem("Free up space")
+i3 = ListItem("Install the update")
+
+Example 4 — Charts inside Tabs:
+root = Card([header, tabs, followUps])
+header = CardHeader("Sales Dashboard", "Compare metrics across periods")
+tabs = Tabs([tab1, tab2, tab3])
+tab1 = TabItem("revenue", "Revenue", [revChart])
+tab2 = TabItem("users", "Users", [usersChart])
+tab3 = TabItem("breakdown", "Breakdown", [pieChart])
+revChart = BarChart(["Jan", "Feb", "Mar", "Apr"], [Series("Revenue", [45, 52, 61, 58])], "grouped", "Month", "USD (K)")
+usersChart = LineChart(["Jan", "Feb", "Mar", "Apr"], [Series("Active", [1200, 1350, 1500, 1420]), Series("New", [300, 420, 380, 450])], "Month", "Users")
+pieChart = PieChart([Slice("Desktop", 62), Slice("Mobile", 31), Slice("Tablet", 7)], true)
+followUps = FollowUpBlock([FollowUpItem("Add Q2 data"), FollowUpItem("Export as table")])
+
+Example 5 — Accordion with progress:
+root = Card([header, acc])
+header = CardHeader("Project Status", "Sprint 14")
+acc = Accordion([a1, a2, a3], "single")
+a1 = AccordionItem("done", "Done", [doneText, doneProgress])
+doneText = TextContent("All planned API work is complete.")
+doneProgress = Progress(100, "API")
+a2 = AccordionItem("progress", "In progress", [Progress(60, "Dashboard UI")])
+a3 = AccordionItem("blocked", "Blocked", [Alert("Waiting on design", "Final mockups pending review.", "warning")])
+
+Example 6 — Buttons with variants:
+root = Card([title, btns])
+title = CardHeader("Choose an action")
+btns = Buttons([b1, b2, b3])
+b1 = Button("Confirm", { type: "continue_conversation" }, "default")
+b2 = Button("Learn more", { type: "open_url", url: "https://mui.com" }, "outline")
+b3 = Button("Delete", { type: "continue_conversation" }, "destructive")
+
+## Important Rules
+- When asked about data, generate realistic/plausible data
+- Choose components that best represent the content (tables for comparisons, charts for trends, forms for input, etc.)
+
+## Final Verification
+Before finishing, walk your output and verify:
+1. root = Card(...) is the FIRST line (for optimal streaming).
+2. Every referenced name is defined. Every defined name (other than root) is reachable from root.
+
+- Every response is a single Card(children) — children stack vertically automatically.
+- Card is the only top-level layout container. Use Tabs to switch between sections, Accordion for collapsible sections.
+- Use FollowUpBlock at the END of a Card to suggest what the user can do or ask next.
+- For forms, define one FormControl reference per field so controls can stream progressively.
+- For forms, always provide the second Form argument with Buttons(...) actions.
+- Never nest Form inside Form.
+- Button variant mapping — "default" (filled primary), "secondary" (filled secondary), "outline" (bordered), "ghost" (transparent text), "link" (underlined text), "destructive" (red). Pick the variant that fits the action.
+- Button size mapping — "default" (standard), "xs"/"sm" (small), "lg" (large), "icon" (square).
+- Alert variants — "default"/"info" (blue), "success" (green), "warning" (amber), "destructive" (red error). Always pick the variant that matches the message tone.
+- Use CardHeader for the main section title and Heading for sub-section titles.
+- Use Table for tabular data and List for simple itemized content.
+- Use Progress for completion indicators (value is a 0-100 percentage).
+- Use BarChart/LineChart for trends and comparisons, PieChart (donut: true for a ring) for proportions. Series values must align with the labels array.
+- When the user asks for a specific component, generate a realistic, fully-populated example of it with sample data.
diff --git a/examples/material-ui-chat/src/hooks/use-system-theme.tsx b/examples/material-ui-chat/src/hooks/use-system-theme.tsx
new file mode 100644
index 000000000..8a9ea58f9
--- /dev/null
+++ b/examples/material-ui-chat/src/hooks/use-system-theme.tsx
@@ -0,0 +1,79 @@
+"use client";
+
+import CssBaseline from "@mui/material/CssBaseline";
+import { ThemeProvider } from "@mui/material/styles";
+import { createContext, useContext, useEffect, useMemo, useState } from "react";
+
+import { createAppTheme } from "@/lib/mui-genui/theme";
+
+type ThemeMode = "light" | "dark";
+
+interface ColorModeContextType {
+ mode: ThemeMode;
+ toggle: () => void;
+}
+
+const ColorModeContext = createContext(undefined);
+
+function getSystemMode(): ThemeMode {
+ if (typeof window === "undefined") return "light";
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+}
+
+/**
+ * Provides the Material UI theme + a light/dark color mode. The mode follows the
+ * OS color scheme by default, and stays in sync with it until the user picks a
+ * mode manually via `toggle()`.
+ */
+export function ColorModeProvider({ children }: { children: React.ReactNode }) {
+ const [mode, setMode] = useState("light");
+ const [userOverride, setUserOverride] = useState(false);
+
+ // Pick up the system preference once mounted (avoids SSR hydration mismatch).
+ useEffect(() => {
+ if (!userOverride) setMode(getSystemMode());
+ }, [userOverride]);
+
+ // Keep following the system preference until the user overrides it.
+ useEffect(() => {
+ if (userOverride) return undefined;
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
+ const handler = (e: MediaQueryListEvent) => setMode(e.matches ? "dark" : "light");
+ mq.addEventListener("change", handler);
+ return () => mq.removeEventListener("change", handler);
+ }, [userOverride]);
+
+ useEffect(() => {
+ document.body.setAttribute("data-theme", mode);
+ }, [mode]);
+
+ const theme = useMemo(() => createAppTheme(mode), [mode]);
+
+ const value = useMemo(
+ () => ({
+ mode,
+ toggle: () => {
+ setUserOverride(true);
+ setMode((m) => (m === "light" ? "dark" : "light"));
+ },
+ }),
+ [mode],
+ );
+
+ return (
+
+
+
+ {children}
+
+
+ );
+}
+
+export function useColorMode(): ColorModeContextType {
+ const ctx = useContext(ColorModeContext);
+ if (!ctx) {
+ throw new Error("useColorMode must be used within a ColorModeProvider");
+ }
+ return ctx;
+}
diff --git a/examples/material-ui-chat/src/lib/mui-genui/action.ts b/examples/material-ui-chat/src/lib/mui-genui/action.ts
new file mode 100644
index 000000000..edd4b54b5
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/action.ts
@@ -0,0 +1,23 @@
+import { BuiltinActionType } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const continueConversationAction = z.object({
+ type: z.literal(BuiltinActionType.ContinueConversation),
+ context: z.string().optional(),
+});
+
+const openUrlAction = z.object({
+ type: z.literal(BuiltinActionType.OpenUrl),
+ url: z.string(),
+});
+
+const customAction = z.object({
+ type: z.string(),
+ params: z.record(z.string(), z.any()).optional(),
+});
+
+export const actionSchema = z
+ .union([openUrlAction, continueConversationAction, customAction])
+ .optional();
+
+export type ActionSchema = z.infer;
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/accordion.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/accordion.tsx
new file mode 100644
index 000000000..37f7b5949
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/accordion.tsx
@@ -0,0 +1,72 @@
+"use client";
+
+import MuiAccordion, { type AccordionProps } from "@mui/material/Accordion";
+import AccordionDetails from "@mui/material/AccordionDetails";
+import AccordionSummary from "@mui/material/AccordionSummary";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
+import { defineComponent } from "@openuidev/react-lang";
+import * as React from "react";
+import { z } from "zod";
+import { ContentChildUnion } from "../unions";
+
+const AccordionItemSchema = z.object({
+ value: z.string(),
+ trigger: z.string(),
+ content: z.array(ContentChildUnion),
+});
+
+export const AccordionItemDef = defineComponent({
+ name: "AccordionItem",
+ props: AccordionItemSchema,
+ description: "Collapsible item inside Accordion. value: unique id, trigger: header text.",
+ component: () => null,
+});
+
+const AccordionSchema = z.object({
+ items: z.array(AccordionItemDef.ref),
+ type: z.enum(["single", "multiple"]).optional(),
+});
+
+export const Accordion = defineComponent({
+ name: "Accordion",
+ props: AccordionSchema,
+ description: 'Collapsible sections. type: "single" | "multiple". items: AccordionItem[].',
+ component: ({ props, renderNode }) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const items = (props.items ?? []) as any[];
+ const single = (props.type ?? "multiple") === "single";
+ const [expanded, setExpanded] = React.useState(false);
+
+ if (!items.length) return null;
+
+ return (
+
+ {items.map((item, i) => {
+ const val = String(item?.props?.value ?? i);
+ const ctrl: Partial = single
+ ? {
+ expanded: expanded === val,
+ onChange: (_, isExpanded) => setExpanded(isExpanded ? val : false),
+ }
+ : { defaultExpanded: i === 0 };
+ return (
+
+ }>
+
+ {String(item?.props?.trigger ?? "")}
+
+
+
+
+ {renderNode(item?.props?.content)}
+
+
+
+ );
+ })}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/alert.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/alert.tsx
new file mode 100644
index 000000000..91214f417
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/alert.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import MuiAlert, { type AlertColor } from "@mui/material/Alert";
+import AlertTitle from "@mui/material/AlertTitle";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const AlertSchema = z.object({
+ title: z.string(),
+ description: z.string(),
+ variant: z.enum(["default", "destructive", "info", "success", "warning"]).optional(),
+});
+
+const severityMap: Record = {
+ default: "info",
+ destructive: "error",
+ info: "info",
+ success: "success",
+ warning: "warning",
+};
+
+export const Alert = defineComponent({
+ name: "Alert",
+ props: AlertSchema,
+ description:
+ 'Alert banner with icon, title, and description. variant: "default" | "destructive" | "info" | "success" | "warning".',
+ component: ({ props }) => {
+ const severity = severityMap[props.variant ?? "default"] ?? "info";
+ return (
+
+ {props.title}
+ {props.description}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/button.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/button.tsx
new file mode 100644
index 000000000..bedef50a7
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/button.tsx
@@ -0,0 +1,77 @@
+"use client";
+
+import MuiButton from "@mui/material/Button";
+import {
+ BuiltinActionType,
+ defineComponent,
+ useFormName,
+ useFormValidation,
+ useIsStreaming,
+ useTriggerAction,
+} from "@openuidev/react-lang";
+import { z } from "zod";
+import { actionSchema, type ActionSchema } from "../action";
+
+const ButtonSchema = z.object({
+ label: z.string(),
+ action: actionSchema,
+ variant: z.enum(["default", "destructive", "outline", "secondary", "ghost", "link"]).optional(),
+ size: z.enum(["default", "xs", "sm", "lg", "icon"]).optional(),
+});
+
+const sizeMap: Record = {
+ default: "medium",
+ xs: "small",
+ sm: "small",
+ lg: "large",
+ icon: "small",
+};
+
+export const Button = defineComponent({
+ name: "Button",
+ props: ButtonSchema,
+ description:
+ 'Clickable button. variant: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link". size: "default" | "xs" | "sm" | "lg" | "icon". action: { type: "continue_conversation" | "open_url", url? }.',
+ component: ({ props }) => {
+ const triggerAction = useTriggerAction();
+ const formName = useFormName();
+ const formValidation = useFormValidation();
+ const isStreaming = useIsStreaming();
+
+ const label = String(props.label ?? "");
+ const action = props.action as ActionSchema;
+ const v = props.variant ?? "default";
+
+ const muiVariant = v === "outline" ? "outlined" : v === "ghost" || v === "link" ? "text" : "contained";
+ const color = v === "destructive" ? "error" : v === "secondary" ? "secondary" : "primary";
+ const muiSize = sizeMap[props.size ?? "default"] ?? "medium";
+
+ return (
+ {
+ const actionType = action?.type ?? BuiltinActionType.ContinueConversation;
+ if (
+ formValidation &&
+ v === "default" &&
+ actionType === BuiltinActionType.ContinueConversation
+ ) {
+ const valid = formValidation.validateForm();
+ if (!valid) return;
+ }
+ const actionParams =
+ action?.type === BuiltinActionType.OpenUrl
+ ? { url: (action as { url: string }).url }
+ : (action as { params?: Record })?.params;
+ triggerAction(label, formName, { type: actionType, params: actionParams });
+ }}
+ >
+ {label}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/buttons.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/buttons.tsx
new file mode 100644
index 000000000..c906b7c74
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/buttons.tsx
@@ -0,0 +1,22 @@
+"use client";
+
+import Stack from "@mui/material/Stack";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+import { Button } from "./button";
+
+const ButtonsSchema = z.object({
+ buttons: z.array(Button.ref),
+ direction: z.enum(["row", "column"]).optional(),
+});
+
+export const Buttons = defineComponent({
+ name: "Buttons",
+ props: ButtonsSchema,
+ description: 'Group of Button components. direction: "row" | "column".',
+ component: ({ props, renderNode }) => (
+
+ {renderNode(props.buttons)}
+
+ ),
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/card-header.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/card-header.tsx
new file mode 100644
index 000000000..5d5594c0d
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/card-header.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const CardHeaderSchema = z.object({
+ title: z.string(),
+ description: z.string().optional(),
+});
+
+export const CardHeader = defineComponent({
+ name: "CardHeader",
+ props: CardHeaderSchema,
+ description: "Title/description header block for a Card.",
+ component: ({ props }) => (
+
+
+ {props.title}
+
+ {props.description && (
+
+ {props.description}
+
+ )}
+
+ ),
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/charts.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/charts.tsx
new file mode 100644
index 000000000..145596d36
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/charts.tsx
@@ -0,0 +1,147 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import { BarChart } from "@mui/x-charts/BarChart";
+import { LineChart } from "@mui/x-charts/LineChart";
+import { PieChart } from "@mui/x-charts/PieChart";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+import { asArray, buildPie, buildSeries, hasAllProps } from "../helpers";
+import { CHART_PALETTE } from "../theme";
+
+const CHART_HEIGHT = 260;
+const CHART_MARGIN = { top: 16, right: 16, bottom: 30, left: 44 };
+
+// ── Virtual sub-components (data-only) ──
+
+const SeriesSchema = z.object({
+ category: z.string(),
+ values: z.array(z.number()),
+});
+
+export const Series = defineComponent({
+ name: "Series",
+ props: SeriesSchema,
+ description: "One named data series with values matching labels.",
+ component: () => null,
+});
+
+const SliceSchema = z.object({
+ category: z.string(),
+ value: z.number(),
+});
+
+export const Slice = defineComponent({
+ name: "Slice",
+ props: SliceSchema,
+ description: "A single slice in a PieChart.",
+ component: () => null,
+});
+
+// ── BarChart ──
+
+export const BarChartComponent = defineComponent({
+ name: "BarChart",
+ props: z.object({
+ labels: z.array(z.string()),
+ series: z.array(SeriesSchema),
+ variant: z.enum(["grouped", "stacked"]).optional(),
+ xLabel: z.string().optional(),
+ yLabel: z.string().optional(),
+ }),
+ description: "Vertical bar chart. Use for comparing values across categories.",
+ component: ({ props }) => {
+ if (!hasAllProps(props as Record, "labels", "series")) return null;
+ const labels = asArray(props.labels).map(String);
+ const series = buildSeries(props.series);
+ if (!series.length) return null;
+ const stacked = props.variant === "stacked";
+
+ return (
+
+ ({
+ data: s.data,
+ label: s.label,
+ color: CHART_PALETTE[i % CHART_PALETTE.length],
+ ...(stacked ? { stack: "total" } : {}),
+ }))}
+ />
+
+ );
+ },
+});
+
+// ── LineChart ──
+
+export const LineChartComponent = defineComponent({
+ name: "LineChart",
+ props: z.object({
+ labels: z.array(z.string()),
+ series: z.array(SeriesSchema),
+ xLabel: z.string().optional(),
+ yLabel: z.string().optional(),
+ }),
+ description: "Line chart for trends over categories.",
+ component: ({ props }) => {
+ if (!hasAllProps(props as Record, "labels", "series")) return null;
+ const labels = asArray(props.labels).map(String);
+ const series = buildSeries(props.series);
+ if (!series.length) return null;
+
+ return (
+
+ ({
+ data: s.data,
+ label: s.label,
+ color: CHART_PALETTE[i % CHART_PALETTE.length],
+ curve: "monotoneX",
+ showMark: false,
+ }))}
+ />
+
+ );
+ },
+});
+
+// ── PieChart ──
+
+export const PieChartComponent = defineComponent({
+ name: "PieChart",
+ props: z.object({
+ slices: z.array(SliceSchema),
+ donut: z.boolean().optional(),
+ }),
+ description: "Pie or donut chart. slices: Slice[], donut: boolean for a ring chart.",
+ component: ({ props }) => {
+ const data = buildPie(props.slices);
+ if (!data.length) return null;
+ const colored = data.map((d, i) => ({ ...d, color: CHART_PALETTE[i % CHART_PALETTE.length] }));
+
+ return (
+
+
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/divider.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/divider.tsx
new file mode 100644
index 000000000..c346978e8
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/divider.tsx
@@ -0,0 +1,19 @@
+"use client";
+
+import Divider from "@mui/material/Divider";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const SeparatorSchema = z.object({
+ orientation: z.enum(["horizontal", "vertical"]).optional(),
+});
+
+export const Separator = defineComponent({
+ name: "Separator",
+ props: SeparatorSchema,
+ description: 'Horizontal or vertical rule. orientation: "horizontal" | "vertical".',
+ component: ({ props }) => {
+ const orientation = props.orientation ?? "horizontal";
+ return ;
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/follow-up-block.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/follow-up-block.tsx
new file mode 100644
index 000000000..cbd0ff330
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/follow-up-block.tsx
@@ -0,0 +1,50 @@
+"use client";
+
+import Chip from "@mui/material/Chip";
+import Stack from "@mui/material/Stack";
+import { defineComponent, useTriggerAction } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const FollowUpItemSchema = z.object({
+ text: z.string(),
+});
+
+export const FollowUpItem = defineComponent({
+ name: "FollowUpItem",
+ props: FollowUpItemSchema,
+ description: "Clickable follow-up suggestion — sends text as a user message when clicked.",
+ component: () => null,
+});
+
+const FollowUpBlockSchema = z.object({
+ items: z.array(FollowUpItem.ref),
+});
+
+export const FollowUpBlock = defineComponent({
+ name: "FollowUpBlock",
+ props: FollowUpBlockSchema,
+ description: "List of follow-up suggestion chips at the end of a response.",
+ component: ({ props }) => {
+ const triggerAction = useTriggerAction();
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const items = (props.items ?? []) as any[];
+
+ return (
+
+ {items.map((item, i) => {
+ const text = String(item?.props?.text ?? "");
+ return (
+ triggerAction(text)}
+ />
+ );
+ })}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/form-control.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/form-control.tsx
new file mode 100644
index 000000000..022a1b273
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/form-control.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import { defineComponent, useFormValidation } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const FormControlSchema = z.object({
+ label: z.string(),
+ field: z.any(),
+});
+
+export const FormControl = defineComponent({
+ name: "FormControl",
+ props: FormControlSchema,
+ description: "Wraps a form field with a label and error display.",
+ component: ({ props, renderNode }) => {
+ const formValidation = useFormValidation();
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const fieldName = (props.field as any)?.props?.name as string | undefined;
+ const errors = formValidation?.errors as Record | undefined;
+ const error = fieldName ? errors?.[fieldName] : undefined;
+
+ return (
+
+
+ {props.label}
+
+ {renderNode(props.field)}
+ {error && (
+
+ {error}
+
+ )}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/form.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/form.tsx
new file mode 100644
index 000000000..0cdfc63f2
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/form.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import {
+ FormNameContext,
+ FormValidationContext,
+ defineComponent,
+ useCreateFormValidation,
+} from "@openuidev/react-lang";
+import { z } from "zod";
+import { Buttons } from "./buttons";
+import { FormControl } from "./form-control";
+
+const FormSchema = z.object({
+ name: z.string(),
+ buttons: Buttons.ref,
+ fields: z.array(FormControl.ref).default([]),
+});
+
+export const Form = defineComponent({
+ name: "Form",
+ props: FormSchema,
+ description:
+ "Form container with fields and explicit action buttons. fields: FormControl[], buttons: Buttons.",
+ component: ({ props, renderNode }) => {
+ const formValidation = useCreateFormValidation();
+ const formName = props.name as string;
+
+ return (
+
+
+
+ {renderNode(props.fields)}
+ {renderNode(props.buttons)}
+
+
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/heading.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/heading.tsx
new file mode 100644
index 000000000..30a64a165
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/heading.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import Typography from "@mui/material/Typography";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const HeadingSchema = z.object({
+ text: z.string(),
+ level: z.enum(["h1", "h2", "h3", "h4"]).optional(),
+});
+
+export const Heading = defineComponent({
+ name: "Heading",
+ props: HeadingSchema,
+ description: 'Section heading. level: "h1" | "h2" | "h3" | "h4".',
+ component: ({ props }) => {
+ const level = props.level ?? "h2";
+ return (
+
+ {props.text}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/input.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/input.tsx
new file mode 100644
index 000000000..9107101a7
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/input.tsx
@@ -0,0 +1,67 @@
+"use client";
+
+import TextField from "@mui/material/TextField";
+import {
+ defineComponent,
+ parseStructuredRules,
+ useFormName,
+ useFormValidation,
+ useGetFieldValue,
+ useIsStreaming,
+ useSetFieldValue,
+} from "@openuidev/react-lang";
+import React from "react";
+import { z } from "zod";
+import { rulesSchema } from "../rules";
+
+const InputSchema = z.object({
+ name: z.string(),
+ placeholder: z.string().optional(),
+ type: z.enum(["text", "email", "password", "number", "url"]).optional(),
+ rules: rulesSchema,
+});
+
+export const Input = defineComponent({
+ name: "Input",
+ props: InputSchema,
+ description:
+ 'Text input field. type: "text" | "email" | "password" | "number" | "url". rules for validation.',
+ component: ({ props }) => {
+ const formName = useFormName();
+ const getFieldValue = useGetFieldValue();
+ const setFieldValue = useSetFieldValue();
+ const isStreaming = useIsStreaming();
+ const formValidation = useFormValidation();
+
+ const fieldName = props.name as string;
+ const rules = React.useMemo(() => parseStructuredRules(props.rules), [props.rules]);
+ const savedValue = getFieldValue(formName, fieldName) ?? "";
+
+ React.useEffect(() => {
+ if (!isStreaming && rules.length > 0 && formValidation) {
+ formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName));
+ return () => formValidation.unregisterField(fieldName);
+ }
+ return undefined;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isStreaming, rules.length > 0]);
+
+ return (
+ {
+ const val = e.target.value;
+ if (val !== savedValue) setFieldValue(formName, "Input", fieldName, val, true);
+ if (rules.length > 0 && formValidation)
+ formValidation.validateField(fieldName, val, rules);
+ }}
+ />
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/list.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/list.tsx
new file mode 100644
index 000000000..8342a2a4c
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/list.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import MuiList from "@mui/material/List";
+import MuiListItem from "@mui/material/ListItem";
+import ListItemText from "@mui/material/ListItemText";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const ListItemSchema = z.object({
+ primary: z.string(),
+ secondary: z.string().optional(),
+});
+
+export const ListItemDef = defineComponent({
+ name: "ListItem",
+ props: ListItemSchema,
+ description: "Item in a List. primary text with optional secondary text.",
+ component: () => null,
+});
+
+const ListSchema = z.object({
+ items: z.array(ListItemDef.ref),
+ dense: z.boolean().optional(),
+});
+
+export const List = defineComponent({
+ name: "List",
+ props: ListSchema,
+ description: "Vertical list of items. items: ListItem[]. dense for tighter spacing.",
+ component: ({ props }) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const items = ((props.items ?? []) as any[]).filter((it) => it?.props?.primary != null);
+ if (!items.length) return null;
+ return (
+
+ {items.map((it, i) => (
+
+
+
+ ))}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/progress.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/progress.tsx
new file mode 100644
index 000000000..80783df63
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/progress.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import LinearProgress from "@mui/material/LinearProgress";
+import Typography from "@mui/material/Typography";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const ProgressSchema = z.object({
+ value: z.number(),
+ label: z.string().optional(),
+});
+
+export const Progress = defineComponent({
+ name: "Progress",
+ props: ProgressSchema,
+ description: "Progress bar showing completion percentage (0-100). Optional label.",
+ component: ({ props }) => {
+ const value = Math.max(0, Math.min(100, Number(props.value) || 0));
+ return (
+
+
+ {props.label && {props.label}}
+
+ {value}%
+
+
+
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/select.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/select.tsx
new file mode 100644
index 000000000..be5a364d2
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/select.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import MuiFormControl from "@mui/material/FormControl";
+import MenuItem from "@mui/material/MenuItem";
+import MuiSelect, { type SelectChangeEvent } from "@mui/material/Select";
+import {
+ defineComponent,
+ parseStructuredRules,
+ useFormName,
+ useFormValidation,
+ useGetFieldValue,
+ useIsStreaming,
+ useSetFieldValue,
+} from "@openuidev/react-lang";
+import React from "react";
+import { z } from "zod";
+import { rulesSchema } from "../rules";
+
+const SelectItemSchema = z.object({
+ value: z.string(),
+ label: z.string(),
+});
+
+export const SelectItem = defineComponent({
+ name: "SelectItem",
+ props: SelectItemSchema,
+ description: "Option for Select dropdown.",
+ component: () => null,
+});
+
+const SelectSchema = z.object({
+ name: z.string(),
+ items: z.array(SelectItem.ref),
+ placeholder: z.string().optional(),
+ rules: rulesSchema,
+});
+
+export const Select = defineComponent({
+ name: "Select",
+ props: SelectSchema,
+ description: "Dropdown select. items: SelectItem[], placeholder, rules for validation.",
+ component: ({ props }) => {
+ const formName = useFormName();
+ const getFieldValue = useGetFieldValue();
+ const setFieldValue = useSetFieldValue();
+ const isStreaming = useIsStreaming();
+ const formValidation = useFormValidation();
+
+ const fieldName = props.name as string;
+ const rules = React.useMemo(() => parseStructuredRules(props.rules), [props.rules]);
+ const value = (getFieldValue(formName, fieldName) as string | undefined) ?? "";
+
+ React.useEffect(() => {
+ if (!isStreaming && rules.length > 0 && formValidation) {
+ formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName));
+ return () => formValidation.unregisterField(fieldName);
+ }
+ return undefined;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isStreaming, rules.length > 0]);
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const items = ((props.items ?? []) as any[]).filter((item) => item?.props?.value);
+
+ return (
+
+
+ selected ? (
+ String(selected)
+ ) : (
+ {props.placeholder ?? "Select..."}
+ )
+ }
+ onChange={(e: SelectChangeEvent) => {
+ const val = e.target.value;
+ setFieldValue(formName, "Select", fieldName, val, true);
+ if (rules.length > 0 && formValidation)
+ formValidation.validateField(fieldName, val, rules);
+ }}
+ >
+ {items.map((item, i) => (
+
+ ))}
+
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/switch-group.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/switch-group.tsx
new file mode 100644
index 000000000..40e3cb496
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/switch-group.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import FormControlLabel from "@mui/material/FormControlLabel";
+import FormGroup from "@mui/material/FormGroup";
+import MuiSwitch from "@mui/material/Switch";
+import {
+ defineComponent,
+ useFormName,
+ useGetFieldValue,
+ useIsStreaming,
+ useSetFieldValue,
+} from "@openuidev/react-lang";
+import { z } from "zod";
+
+const SwitchItemSchema = z.object({
+ value: z.string(),
+ label: z.string(),
+});
+
+export const SwitchItem = defineComponent({
+ name: "SwitchItem",
+ props: SwitchItemSchema,
+ description: "Toggle option in a SwitchGroup.",
+ component: () => null,
+});
+
+const SwitchGroupSchema = z.object({
+ name: z.string(),
+ items: z.array(SwitchItem.ref),
+});
+
+export const SwitchGroup = defineComponent({
+ name: "SwitchGroup",
+ props: SwitchGroupSchema,
+ description: "Group of toggle switches. items: SwitchItem[].",
+ component: ({ props }) => {
+ const formName = useFormName();
+ const getFieldValue = useGetFieldValue();
+ const setFieldValue = useSetFieldValue();
+ const isStreaming = useIsStreaming();
+
+ const fieldName = props.name as string;
+ const current = (getFieldValue(formName, fieldName) as string[] | undefined) ?? [];
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const items = ((props.items ?? []) as any[]).filter((item) => item?.props?.value);
+
+ return (
+
+ {items.map((item, i) => {
+ const val = item.props.value as string;
+ const checked = current.includes(val);
+ return (
+ {
+ const next = e.target.checked
+ ? [...current, val]
+ : current.filter((v: string) => v !== val);
+ setFieldValue(formName, "SwitchGroup", fieldName, next, true);
+ }}
+ />
+ }
+ label={item.props.label || val}
+ />
+ );
+ })}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/table.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/table.tsx
new file mode 100644
index 000000000..33ad04cda
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/table.tsx
@@ -0,0 +1,72 @@
+"use client";
+
+import Paper from "@mui/material/Paper";
+import MuiTable from "@mui/material/Table";
+import TableBody from "@mui/material/TableBody";
+import TableCell from "@mui/material/TableCell";
+import TableContainer from "@mui/material/TableContainer";
+import TableHead from "@mui/material/TableHead";
+import TableRow from "@mui/material/TableRow";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const ColSchema = z.object({
+ header: z.string(),
+ type: z.enum(["string", "number", "boolean"]).optional(),
+});
+
+export const Col = defineComponent({
+ name: "Col",
+ props: ColSchema,
+ description: "Column definition for Table — header label and optional type.",
+ component: () => null,
+});
+
+const TableSchema = z.object({
+ columns: z.array(Col.ref),
+ rows: z.array(z.array(z.any())),
+});
+
+export const Table = defineComponent({
+ name: "Table",
+ props: TableSchema,
+ description: "Data table. columns: Col[] with header/type, rows: 2D array of values.",
+ component: ({ props }) => {
+ const columns = ((props.columns ?? []) as unknown[]).map((c) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const col = c as any;
+ return {
+ header: String(col?.props?.header ?? ""),
+ type: (col?.props?.type ?? "string") as string,
+ };
+ });
+ const rows = (props.rows ?? []) as unknown[][];
+
+ return (
+
+
+
+
+ {columns.map((col, i) => (
+
+ {col.header}
+
+ ))}
+
+
+
+ {rows.map((row, ri) => (
+
+ {columns.map((col, ci) => (
+
+ {String(row[ci] ?? "")}
+
+ ))}
+
+ ))}
+
+
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/tabs.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/tabs.tsx
new file mode 100644
index 000000000..a78b64a90
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/tabs.tsx
@@ -0,0 +1,81 @@
+"use client";
+
+import Box from "@mui/material/Box";
+import Tab from "@mui/material/Tab";
+import MuiTabs from "@mui/material/Tabs";
+import { defineComponent } from "@openuidev/react-lang";
+import * as React from "react";
+import { z } from "zod";
+import { ContentChildUnion } from "../unions";
+
+const TabItemSchema = z.object({
+ value: z.string(),
+ trigger: z.string(),
+ content: z.array(ContentChildUnion),
+});
+
+export const TabItem = defineComponent({
+ name: "TabItem",
+ props: TabItemSchema,
+ description: "Tab panel. value: unique id, trigger: tab label, content: children.",
+ component: () => null,
+});
+
+const TabsSchema = z.object({
+ items: z.array(TabItem.ref),
+ defaultValue: z.string().optional(),
+});
+
+export const Tabs = defineComponent({
+ name: "Tabs",
+ props: TabsSchema,
+ description: "Tabbed content. items: TabItem[]. defaultValue: initially active tab.",
+ component: ({ props, renderNode }) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const rawItems = (props.items ?? []) as any[];
+ const items = rawItems.filter(
+ (item) => item?.props?.value != null && item?.props?.trigger != null,
+ );
+
+ const [userSelected, setUserSelected] = React.useState(null);
+
+ const firstValue = items[0]?.props?.value as string | undefined;
+ const preferredDefault = props.defaultValue ?? firstValue;
+ const userSelectionValid =
+ userSelected != null && items.some((item) => String(item?.props?.value) === userSelected);
+ const activeTab = userSelectionValid ? userSelected : (preferredDefault ?? "");
+
+ if (items.length === 0) return null;
+
+ return (
+
+ setUserSelected(String(val))}
+ variant="scrollable"
+ scrollButtons="auto"
+ sx={{ borderBottom: 1, borderColor: "divider" }}
+ >
+ {items.map((item) => {
+ const val = String(item.props.value);
+ return ;
+ })}
+
+ {items.map((item) => {
+ const val = String(item.props.value);
+ const selected = val === activeTab;
+ return (
+
+ {selected ? renderNode(item.props.content) : null}
+
+ );
+ })}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/components/text-content.tsx b/examples/material-ui-chat/src/lib/mui-genui/components/text-content.tsx
new file mode 100644
index 000000000..45dd8cfc9
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/components/text-content.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import Typography from "@mui/material/Typography";
+import { defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+const TextContentSchema = z.object({
+ text: z.string(),
+ size: z.enum(["small", "default", "large", "small-heavy", "large-heavy"]).optional(),
+});
+
+type Cfg = { variant: "body1" | "body2" | "h6"; fontWeight: number; color?: string };
+
+const sizeMap: Record = {
+ small: { variant: "body2", fontWeight: 400, color: "text.secondary" },
+ default: { variant: "body1", fontWeight: 400 },
+ large: { variant: "h6", fontWeight: 400 },
+ "small-heavy": { variant: "body2", fontWeight: 600 },
+ "large-heavy": { variant: "h6", fontWeight: 600 },
+};
+
+export const TextContent = defineComponent({
+ name: "TextContent",
+ props: TextContentSchema,
+ description:
+ 'Text block with optional size. size: "small" | "default" | "large" | "small-heavy" | "large-heavy".',
+ component: ({ props }) => {
+ const text = props.text == null ? "" : String(props.text);
+ const cfg = sizeMap[props.size ?? "default"] ?? sizeMap.default;
+ return (
+
+ {text}
+
+ );
+ },
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/helpers.ts b/examples/material-ui-chat/src/lib/mui-genui/helpers.ts
new file mode 100644
index 000000000..5dec6013e
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/helpers.ts
@@ -0,0 +1,58 @@
+/**
+ * Shared helpers for turning OpenUI Lang element nodes (Series, Slice, …) into
+ * the plain data shapes that `@mui/x-charts` expects.
+ */
+
+type ElementLike = {
+ type: "element";
+ props: Record;
+};
+
+export function hasAllProps(obj: Record, ...keys: string[]): boolean {
+ return keys.every((k) => obj[k] != null);
+}
+
+export function asArray(v: unknown): unknown[] {
+ if (Array.isArray(v)) return v;
+ if (v == null) return [];
+ return [v];
+}
+
+function asElementNodes(v: unknown): ElementLike[] {
+ return asArray(v).filter(
+ (x): x is ElementLike =>
+ typeof x === "object" && x !== null && (x as Record)["type"] === "element",
+ );
+}
+
+function toNumber(v: unknown): number {
+ return typeof v === "number" ? v : Number(v) || 0;
+}
+
+export interface CartesianSeries {
+ label: string;
+ data: number[];
+}
+
+/** Convert an array of `Series(category, values)` nodes into x-charts series. */
+export function buildSeries(series: unknown): CartesianSeries[] {
+ return asElementNodes(series).map((s) => ({
+ label: String(s.props["category"] ?? ""),
+ data: asArray(s.props["values"]).map(toNumber),
+ }));
+}
+
+export interface PieDatum {
+ id: number;
+ value: number;
+ label: string;
+}
+
+/** Convert an array of `Slice(category, value)` nodes into x-charts pie data. */
+export function buildPie(slices: unknown): PieDatum[] {
+ return asElementNodes(slices).map((s, i) => ({
+ id: i,
+ value: toNumber(s.props["value"]),
+ label: String(s.props["category"] ?? ""),
+ }));
+}
diff --git a/examples/material-ui-chat/src/lib/mui-genui/index.tsx b/examples/material-ui-chat/src/lib/mui-genui/index.tsx
new file mode 100644
index 000000000..4cf1aafb3
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/index.tsx
@@ -0,0 +1,257 @@
+"use client";
+
+import Card from "@mui/material/Card";
+import CardContent from "@mui/material/CardContent";
+import type { ComponentGroup, PromptOptions } from "@openuidev/react-lang";
+import { createLibrary, defineComponent } from "@openuidev/react-lang";
+import { z } from "zod";
+
+// Content
+import { Alert } from "./components/alert";
+import { CardHeader } from "./components/card-header";
+import { Heading } from "./components/heading";
+import { List, ListItemDef } from "./components/list";
+import { Progress } from "./components/progress";
+import { Separator } from "./components/divider";
+import { TextContent } from "./components/text-content";
+
+// Tables
+import { Col, Table } from "./components/table";
+
+// Charts
+import {
+ BarChartComponent,
+ LineChartComponent,
+ PieChartComponent,
+ Series,
+ Slice,
+} from "./components/charts";
+
+// Forms
+import { Form } from "./components/form";
+import { FormControl } from "./components/form-control";
+import { Input } from "./components/input";
+import { Select, SelectItem } from "./components/select";
+import { SwitchGroup, SwitchItem } from "./components/switch-group";
+
+// Buttons
+import { Button } from "./components/button";
+import { Buttons } from "./components/buttons";
+
+// Layout
+import { Accordion, AccordionItemDef } from "./components/accordion";
+import { TabItem, Tabs } from "./components/tabs";
+
+// Chat-specific
+import { FollowUpBlock, FollowUpItem } from "./components/follow-up-block";
+
+import { ChatContentChildUnion } from "./unions";
+
+const ChatCardChildUnion = z.union([...ChatContentChildUnion.options, Tabs.ref, Accordion.ref]);
+
+const ChatCard = defineComponent({
+ name: "Card",
+ props: z.object({
+ children: z.array(ChatCardChildUnion),
+ }),
+ description:
+ "Vertical container for all content in a chat response. Children stack top to bottom automatically.",
+ component: ({ props, renderNode }) => (
+
+
+ {renderNode(props.children)}
+
+
+ ),
+});
+
+// ── Component Groups ──
+
+export const muiComponentGroups: ComponentGroup[] = [
+ {
+ name: "Content",
+ components: ["CardHeader", "TextContent", "Heading", "Alert", "List", "ListItem", "Separator", "Progress"],
+ notes: [
+ "- Use CardHeader for the main section title. Use Heading for sub-section titles.",
+ '- TextContent size: "small" | "default" | "large" | "small-heavy" | "large-heavy".',
+ "- List takes ListItem[] — each ListItem has primary text and optional secondary text.",
+ ],
+ },
+ {
+ name: "Tables",
+ components: ["Table", "Col"],
+ },
+ {
+ name: "Charts",
+ components: ["BarChart", "LineChart", "PieChart", "Series", "Slice"],
+ notes: [
+ "- BarChart/LineChart take labels (string[]) and series (Series[]). Each Series has a category name and a values array aligned to labels.",
+ "- PieChart takes slices (Slice[]). Set donut: true for a ring chart.",
+ ],
+ },
+ {
+ name: "Forms",
+ components: ["Form", "FormControl", "Input", "Select", "SelectItem", "SwitchGroup", "SwitchItem"],
+ notes: [
+ "- Define EACH FormControl as its own reference — do NOT inline all controls in one array.",
+ "- NEVER nest Form inside Form.",
+ "- Form requires explicit buttons. Always pass a Buttons(...) reference as the second Form argument.",
+ "- rules is an optional object: { required: true, email: true, minLength: 8, maxLength: 100 }.",
+ "- The renderer shows error messages automatically — do NOT generate error text in the UI.",
+ ],
+ },
+ {
+ name: "Buttons",
+ components: ["Button", "Buttons"],
+ },
+ {
+ name: "Layout",
+ components: ["Tabs", "TabItem", "Accordion", "AccordionItem"],
+ notes: [
+ "- Use Tabs to present alternative views — each TabItem has a value id, trigger label, and content array.",
+ '- Accordion type: "single" | "multiple". items: AccordionItem[].',
+ ],
+ },
+ {
+ name: "Follow-ups",
+ components: ["FollowUpBlock", "FollowUpItem"],
+ notes: [
+ "- Use FollowUpBlock with FollowUpItem references at the END of a response to suggest next actions.",
+ "- Clicking a FollowUpItem sends its text to the LLM as a user message.",
+ ],
+ },
+];
+
+// ── Examples ──
+
+export const muiExamples: string[] = [
+ `Example 1 — Table with follow-ups:
+root = Card([title, tbl, followUps])
+title = CardHeader("Top Languages", "By number of active developers")
+tbl = Table(cols, rows)
+cols = [Col("Language", "string"), Col("Users (M)", "number"), Col("Year", "number")]
+rows = [["Python", 15.7, 1991], ["JavaScript", 14.2, 1995], ["Java", 12.1, 1995]]
+followUps = FollowUpBlock([fu1, fu2])
+fu1 = FollowUpItem("Tell me more about Python")
+fu2 = FollowUpItem("Show me a JavaScript comparison")`,
+
+ `Example 2 — Form with validation:
+root = Card([title, form])
+title = CardHeader("Contact Us")
+form = Form("contact", btns, [nameField, emailField, planField])
+nameField = FormControl("Name", Input("name", "Your name", "text", { required: true, minLength: 2 }))
+emailField = FormControl("Email", Input("email", "you@example.com", "email", { required: true, email: true }))
+planField = FormControl("Plan", Select("plan", [opt1, opt2], "Choose a plan", { required: true }))
+opt1 = SelectItem("free", "Free")
+opt2 = SelectItem("pro", "Pro")
+btns = Buttons([Button("Submit", { type: "continue_conversation" }, "default")])`,
+
+ `Example 3 — Alert variants and a list:
+root = Card([info, warning, items])
+info = Alert("Update available", "A new version is ready to install.", "info")
+warning = Alert("Disk almost full", "Less than 10% storage remaining.", "warning")
+items = List([i1, i2, i3])
+i1 = ListItem("Back up your data", "Recommended before updating")
+i2 = ListItem("Free up space")
+i3 = ListItem("Install the update")`,
+
+ `Example 4 — Charts inside Tabs:
+root = Card([header, tabs, followUps])
+header = CardHeader("Sales Dashboard", "Compare metrics across periods")
+tabs = Tabs([tab1, tab2, tab3])
+tab1 = TabItem("revenue", "Revenue", [revChart])
+tab2 = TabItem("users", "Users", [usersChart])
+tab3 = TabItem("breakdown", "Breakdown", [pieChart])
+revChart = BarChart(["Jan", "Feb", "Mar", "Apr"], [Series("Revenue", [45, 52, 61, 58])], "grouped", "Month", "USD (K)")
+usersChart = LineChart(["Jan", "Feb", "Mar", "Apr"], [Series("Active", [1200, 1350, 1500, 1420]), Series("New", [300, 420, 380, 450])], "Month", "Users")
+pieChart = PieChart([Slice("Desktop", 62), Slice("Mobile", 31), Slice("Tablet", 7)], true)
+followUps = FollowUpBlock([FollowUpItem("Add Q2 data"), FollowUpItem("Export as table")])`,
+
+ `Example 5 — Accordion with progress:
+root = Card([header, acc])
+header = CardHeader("Project Status", "Sprint 14")
+acc = Accordion([a1, a2, a3], "single")
+a1 = AccordionItem("done", "Done", [doneText, doneProgress])
+doneText = TextContent("All planned API work is complete.")
+doneProgress = Progress(100, "API")
+a2 = AccordionItem("progress", "In progress", [Progress(60, "Dashboard UI")])
+a3 = AccordionItem("blocked", "Blocked", [Alert("Waiting on design", "Final mockups pending review.", "warning")])`,
+
+ `Example 6 — Buttons with variants:
+root = Card([title, btns])
+title = CardHeader("Choose an action")
+btns = Buttons([b1, b2, b3])
+b1 = Button("Confirm", { type: "continue_conversation" }, "default")
+b2 = Button("Learn more", { type: "open_url", url: "https://mui.com" }, "outline")
+b3 = Button("Delete", { type: "continue_conversation" }, "destructive")`,
+];
+
+export const muiAdditionalRules: string[] = [
+ "Every response is a single Card(children) — children stack vertically automatically.",
+ "Card is the only top-level layout container. Use Tabs to switch between sections, Accordion for collapsible sections.",
+ "Use FollowUpBlock at the END of a Card to suggest what the user can do or ask next.",
+ "For forms, define one FormControl reference per field so controls can stream progressively.",
+ "For forms, always provide the second Form argument with Buttons(...) actions.",
+ "Never nest Form inside Form.",
+ 'Button variant mapping — "default" (filled primary), "secondary" (filled secondary), "outline" (bordered), "ghost" (transparent text), "link" (underlined text), "destructive" (red). Pick the variant that fits the action.',
+ 'Button size mapping — "default" (standard), "xs"/"sm" (small), "lg" (large), "icon" (square).',
+ 'Alert variants — "default"/"info" (blue), "success" (green), "warning" (amber), "destructive" (red error). Always pick the variant that matches the message tone.',
+ "Use CardHeader for the main section title and Heading for sub-section titles.",
+ "Use Table for tabular data and List for simple itemized content.",
+ "Use Progress for completion indicators (value is a 0-100 percentage).",
+ "Use BarChart/LineChart for trends and comparisons, PieChart (donut: true for a ring) for proportions. Series values must align with the labels array.",
+ "When the user asks for a specific component, generate a realistic, fully-populated example of it with sample data.",
+];
+
+export const muiPromptOptions: PromptOptions = {
+ examples: muiExamples,
+ additionalRules: muiAdditionalRules,
+};
+
+// ── Library ──
+
+export const muiChatLibrary = createLibrary({
+ root: "Card",
+ componentGroups: muiComponentGroups,
+ components: [
+ // Root
+ ChatCard,
+ CardHeader,
+ // Content
+ TextContent,
+ Heading,
+ Alert,
+ List,
+ ListItemDef,
+ Separator,
+ Progress,
+ // Tables
+ Table,
+ Col,
+ // Charts
+ BarChartComponent,
+ LineChartComponent,
+ PieChartComponent,
+ Series,
+ Slice,
+ // Forms
+ Form,
+ FormControl,
+ Input,
+ Select,
+ SelectItem,
+ SwitchGroup,
+ SwitchItem,
+ // Buttons
+ Button,
+ Buttons,
+ // Layout
+ Tabs,
+ TabItem,
+ Accordion,
+ AccordionItemDef,
+ // Follow-ups
+ FollowUpBlock,
+ FollowUpItem,
+ ],
+});
diff --git a/examples/material-ui-chat/src/lib/mui-genui/rules.ts b/examples/material-ui-chat/src/lib/mui-genui/rules.ts
new file mode 100644
index 000000000..cd77461f9
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/rules.ts
@@ -0,0 +1,17 @@
+import { z } from "zod";
+
+export const rulesSchema = z
+ .object({
+ required: z.boolean().optional(),
+ email: z.boolean().optional(),
+ url: z.boolean().optional(),
+ numeric: z.boolean().optional(),
+ min: z.number().optional(),
+ max: z.number().optional(),
+ minLength: z.number().optional(),
+ maxLength: z.number().optional(),
+ pattern: z.string().optional(),
+ })
+ .optional();
+
+export type RulesSchema = z.infer;
diff --git a/examples/material-ui-chat/src/lib/mui-genui/theme.ts b/examples/material-ui-chat/src/lib/mui-genui/theme.ts
new file mode 100644
index 000000000..eec63503f
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/theme.ts
@@ -0,0 +1,42 @@
+import { createTheme, type Theme } from "@mui/material/styles";
+
+/**
+ * Builds the Material UI theme for the chat surface. The same factory is used
+ * for both light and dark mode — pass the desired palette mode.
+ */
+export function createAppTheme(mode: "light" | "dark"): Theme {
+ return createTheme({
+ palette: {
+ mode,
+ primary: { main: mode === "dark" ? "#90caf9" : "#1976d2" },
+ secondary: { main: mode === "dark" ? "#ce93d8" : "#9c27b0" },
+ ...(mode === "dark"
+ ? { background: { default: "#0b0f14", paper: "#121821" } }
+ : { background: { default: "#f7f8fa", paper: "#ffffff" } }),
+ },
+ shape: { borderRadius: 10 },
+ typography: {
+ fontFamily:
+ 'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
+ h1: { fontSize: "1.8rem", fontWeight: 700 },
+ h2: { fontSize: "1.5rem", fontWeight: 700 },
+ h3: { fontSize: "1.25rem", fontWeight: 600 },
+ h4: { fontSize: "1.1rem", fontWeight: 600 },
+ },
+ components: {
+ MuiCard: {
+ defaultProps: { variant: "outlined" },
+ },
+ },
+ });
+}
+
+/** Categorical palette shared by all charts so series colors stay consistent. */
+export const CHART_PALETTE = [
+ "#1976d2",
+ "#9c27b0",
+ "#2e7d32",
+ "#ed6c02",
+ "#0288d1",
+ "#d32f2f",
+];
diff --git a/examples/material-ui-chat/src/lib/mui-genui/unions.ts b/examples/material-ui-chat/src/lib/mui-genui/unions.ts
new file mode 100644
index 000000000..c2d3034c0
--- /dev/null
+++ b/examples/material-ui-chat/src/lib/mui-genui/unions.ts
@@ -0,0 +1,41 @@
+import { z } from "zod";
+
+import { Alert } from "./components/alert";
+import { Buttons } from "./components/buttons";
+import { CardHeader } from "./components/card-header";
+import {
+ BarChartComponent,
+ LineChartComponent,
+ PieChartComponent,
+} from "./components/charts";
+import { Separator } from "./components/divider";
+import { FollowUpBlock } from "./components/follow-up-block";
+import { Form } from "./components/form";
+import { Heading } from "./components/heading";
+import { List } from "./components/list";
+import { Progress } from "./components/progress";
+import { Table } from "./components/table";
+import { TextContent } from "./components/text-content";
+
+/**
+ * Components that may appear inside a container's `content` array (Card, Tabs,
+ * Accordion). Tabs/Accordion themselves are intentionally excluded here to keep
+ * nesting shallow — they are added to the Card child union in `index.tsx`.
+ */
+export const ContentChildUnion = z.union([
+ CardHeader.ref,
+ TextContent.ref,
+ Heading.ref,
+ Alert.ref,
+ List.ref,
+ Separator.ref,
+ Progress.ref,
+ Table.ref,
+ BarChartComponent.ref,
+ LineChartComponent.ref,
+ PieChartComponent.ref,
+ Form.ref,
+ Buttons.ref,
+]);
+
+export const ChatContentChildUnion = z.union([...ContentChildUnion.options, FollowUpBlock.ref]);
diff --git a/examples/material-ui-chat/src/library.ts b/examples/material-ui-chat/src/library.ts
new file mode 100644
index 000000000..063bcd642
--- /dev/null
+++ b/examples/material-ui-chat/src/library.ts
@@ -0,0 +1,8 @@
+/**
+ * Entry point for the OpenUI CLI prompt generator.
+ *
+ * `pnpm generate:prompt` runs `openui generate src/library.ts`, which bundles
+ * this file (stubbing asset imports) and reads the `library` + `promptOptions`
+ * exports to serialize the system prompt into `src/generated/system-prompt.txt`.
+ */
+export { muiChatLibrary as library, muiPromptOptions as promptOptions } from "./lib/mui-genui";
diff --git a/examples/material-ui-chat/tsconfig.json b/examples/material-ui-chat/tsconfig.json
new file mode 100644
index 000000000..c43ccccc5
--- /dev/null
+++ b/examples/material-ui-chat/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 759229d5a..03368aa0a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -85,7 +85,7 @@ importers:
version: 14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react@19.2.4)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3))
fumadocs-ui:
specifier: 16.6.5
- version: 16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1)
+ version: 16.6.5(@emotion/is-prop-valid@1.4.0)(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1)
gpt-tokenizer:
specifier: ^3.4.0
version: 3.4.0
@@ -94,7 +94,7 @@ importers:
version: 0.570.0(react@19.2.4)
motion:
specifier: ^12.34.3
- version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ version: 12.34.3(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next:
specifier: 16.1.6
version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2)
@@ -377,6 +377,70 @@ importers:
specifier: ^5
version: 5.9.3
+ examples/material-ui-chat:
+ dependencies:
+ '@emotion/react':
+ specifier: ^11.13.5
+ version: 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/styled':
+ specifier: ^11.13.5
+ version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@mui/icons-material':
+ specifier: ^6.4.12
+ version: 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@mui/material':
+ specifier: ^6.4.12
+ version: 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@mui/x-charts':
+ specifier: ^7.29.1
+ version: 7.29.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@openuidev/react-headless':
+ specifier: workspace:*
+ version: link:../../packages/react-headless
+ '@openuidev/react-lang':
+ specifier: workspace:*
+ version: link:../../packages/react-lang
+ '@openuidev/react-ui':
+ specifier: workspace:*
+ version: link:../../packages/react-ui
+ next:
+ specifier: 16.1.6
+ version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2)
+ openai:
+ specifier: ^6.22.0
+ version: 6.34.0(ws@8.20.0)(zod@4.3.6)
+ react:
+ specifier: 19.2.3
+ version: 19.2.3
+ react-dom:
+ specifier: 19.2.3
+ version: 19.2.3(react@19.2.3)
+ zod:
+ specifier: ^4.0.0
+ version: 4.3.6
+ devDependencies:
+ '@openuidev/cli':
+ specifier: workspace:*
+ version: link:../../packages/openui-cli
+ '@types/node':
+ specifier: ^20
+ version: 20.19.35
+ '@types/react':
+ specifier: ^19
+ version: 19.2.14
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.2.3(@types/react@19.2.14)
+ eslint:
+ specifier: ^9
+ version: 9.29.0(jiti@2.6.1)
+ eslint-config-next:
+ specifier: 16.1.6
+ version: 16.1.6(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3)
+ typescript:
+ specifier: ^5
+ version: 5.9.3
+
examples/multi-agent-chat:
dependencies:
'@ai-sdk/openai':
@@ -2293,6 +2357,10 @@ packages:
resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
engines: {node: '>=6.9.0'}
+ '@babel/runtime@7.29.7':
+ resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/template@7.27.2':
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
@@ -2485,6 +2553,60 @@ packages:
'@emnapi/wasi-threads@1.1.0':
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
+ '@emotion/babel-plugin@11.13.5':
+ resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==}
+
+ '@emotion/cache@11.14.0':
+ resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==}
+
+ '@emotion/hash@0.9.2':
+ resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==}
+
+ '@emotion/is-prop-valid@1.4.0':
+ resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==}
+
+ '@emotion/memoize@0.9.0':
+ resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==}
+
+ '@emotion/react@11.14.0':
+ resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: '>=16.8.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@emotion/serialize@1.3.3':
+ resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==}
+
+ '@emotion/sheet@1.4.0':
+ resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==}
+
+ '@emotion/styled@11.14.1':
+ resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==}
+ peerDependencies:
+ '@emotion/react': ^11.0.0-rc.0
+ '@types/react': '*'
+ react: '>=16.8.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@emotion/unitless@0.10.0':
+ resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==}
+
+ '@emotion/use-insertion-effect-with-fallbacks@1.2.0':
+ resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ '@emotion/utils@1.4.2':
+ resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==}
+
+ '@emotion/weak-memoize@0.4.0':
+ resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==}
+
'@envelop/core@5.5.1':
resolution: {integrity: sha512-3DQg8sFskDo386TkL5j12jyRAdip/8yzK3x7YGbZBgobZ4aKXrvDU0GppU0SnmrpQnNaiTUsxBs9LKkwQ/eyvw==}
engines: {node: '>=18.0.0'}
@@ -4013,6 +4135,140 @@ packages:
'@cfworker/json-schema':
optional: true
+ '@mui/core-downloads-tracker@6.5.0':
+ resolution: {integrity: sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==}
+
+ '@mui/icons-material@6.5.0':
+ resolution: {integrity: sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@mui/material': ^6.5.0
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/material@6.5.0':
+ resolution: {integrity: sha512-yjvtXoFcrPLGtgKRxFaH6OQPtcLPhkloC0BML6rBG5UeldR0nPULR/2E2BfXdo5JNV7j7lOzrrLX2Qf/iSidow==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.5.0
+ '@emotion/styled': ^11.3.0
+ '@mui/material-pigment-css': ^6.5.0
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+ '@mui/material-pigment-css':
+ optional: true
+ '@types/react':
+ optional: true
+
+ '@mui/private-theming@6.4.9':
+ resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/styled-engine@6.5.0':
+ resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.4.1
+ '@emotion/styled': ^11.3.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+
+ '@mui/system@6.5.0':
+ resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.5.0
+ '@emotion/styled': ^11.3.0
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+ '@types/react':
+ optional: true
+
+ '@mui/types@7.2.24':
+ resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/types@7.4.12':
+ resolution: {integrity: sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/utils@6.4.9':
+ resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/utils@7.3.11':
+ resolution: {integrity: sha512-XTjGnifwteg71/ij+0e7Y7d+hwyntMYP5wPoA/g2drdGH+Flkvjwy0OfrVpKBbaOvofq4zU/LIyUZyKgmWu18g==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/x-charts-vendor@7.20.0':
+ resolution: {integrity: sha512-pzlh7z/7KKs5o0Kk0oPcB+sY0+Dg7Q7RzqQowDQjpy5Slz6qqGsgOB5YUzn0L+2yRmvASc4Pe0914Ao3tMBogg==}
+
+ '@mui/x-charts@7.29.1':
+ resolution: {integrity: sha512-5s9PX51HWhpMa+DCDa4RgjtODSaMe+PlTZUqoGIil2vaW/+4ouDLREXvyuVvIF93KfZwrPKAL2SJKSQS4YYB2w==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.9.0
+ '@emotion/styled': ^11.8.1
+ '@mui/material': ^5.15.14 || ^6.0.0 || ^7.0.0
+ '@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+
+ '@mui/x-internals@7.29.0':
+ resolution: {integrity: sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
@@ -5005,6 +5261,9 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+ '@popperjs/core@2.11.8':
+ resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
+
'@poppinss/colors@4.1.6':
resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==}
@@ -6570,6 +6829,33 @@ packages:
'@types/react':
optional: true
+ '@react-spring/animated@9.7.5':
+ resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/core@9.7.5':
+ resolution: {integrity: sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/rafz@9.7.5':
+ resolution: {integrity: sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==}
+
+ '@react-spring/shared@9.7.5':
+ resolution: {integrity: sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/types@9.7.5':
+ resolution: {integrity: sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==}
+
+ '@react-spring/web@9.7.5':
+ resolution: {integrity: sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
'@react-stately/autocomplete@3.0.0-beta.4':
resolution: {integrity: sha512-K2Uy7XEdseFvgwRQ8CyrYEHMupjVKEszddOapP8deNz4hntYvT1aRm0m+sKa5Kl/4kvg9c/3NZpQcrky/vRZIg==}
peerDependencies:
@@ -8192,9 +8478,15 @@ packages:
'@types/node@25.3.2':
resolution: {integrity: sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==}
+ '@types/parse-json@4.0.2':
+ resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
+
'@types/prismjs@1.26.6':
resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==}
+ '@types/prop-types@15.7.15':
+ resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
+
'@types/qs@6.15.0':
resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==}
@@ -8209,6 +8501,11 @@ packages:
'@types/react-syntax-highlighter@15.5.13':
resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==}
+ '@types/react-transition-group@4.4.12':
+ resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==}
+ peerDependencies:
+ '@types/react': '*'
+
'@types/react@19.2.14':
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
@@ -9030,6 +9327,10 @@ packages:
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ babel-plugin-macros@3.1.0:
+ resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
+ engines: {node: '>=10', npm: '>=6'}
+
babel-plugin-polyfill-corejs2@0.4.16:
resolution: {integrity: sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==}
peerDependencies:
@@ -9615,6 +9916,9 @@ packages:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
+ convert-source-map@1.9.0:
+ resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -9661,6 +9965,10 @@ packages:
cose-base@2.2.0:
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+ cosmiconfig@7.1.0:
+ resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
+ engines: {node: '>=10'}
+
crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
@@ -10259,6 +10567,9 @@ packages:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'}
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
error-stack-parser-es@1.0.5:
resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
@@ -10841,6 +11152,9 @@ packages:
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
engines: {node: '>= 18.0.0'}
+ find-root@1.1.0:
+ resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
+
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -11332,6 +11646,9 @@ packages:
highlightjs-vue@1.0.0:
resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==}
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
hono-openapi@1.3.0:
resolution: {integrity: sha512-xDvCWpWEIv0weEmnl3EjRQzqbHIO8LnfzMuYOCmbuyE5aes6aXxLg4vM3ybnoZD5TiTUkA6PuRQPJs3R7WRBig==}
peerDependencies:
@@ -11557,6 +11874,9 @@ packages:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
is-async-function@2.1.1:
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
@@ -13286,6 +13606,10 @@ packages:
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
parse-ms@4.0.0:
resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
engines: {node: '>=18'}
@@ -13355,6 +13679,10 @@ packages:
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
pathe@1.1.2:
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
@@ -13933,6 +14261,9 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+ react-is@19.2.6:
+ resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==}
+
react-markdown@10.1.0:
resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
peerDependencies:
@@ -14816,6 +15147,9 @@ packages:
styleq@0.1.3:
resolution: {integrity: sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==}
+ stylis@4.2.0:
+ resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
+
stylis@4.3.6:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
@@ -16113,6 +16447,10 @@ packages:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'}
+ yaml@1.10.3:
+ resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==}
+ engines: {node: '>= 6'}
+
yaml@2.8.0:
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
engines: {node: '>= 14.6'}
@@ -17148,6 +17486,8 @@ snapshots:
'@babel/runtime@7.27.6': {}
+ '@babel/runtime@7.29.7': {}
+
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
@@ -17392,6 +17732,89 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emotion/babel-plugin@11.13.5':
+ dependencies:
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/runtime': 7.27.6
+ '@emotion/hash': 0.9.2
+ '@emotion/memoize': 0.9.0
+ '@emotion/serialize': 1.3.3
+ babel-plugin-macros: 3.1.0
+ convert-source-map: 1.9.0
+ escape-string-regexp: 4.0.0
+ find-root: 1.1.0
+ source-map: 0.5.7
+ stylis: 4.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@emotion/cache@11.14.0':
+ dependencies:
+ '@emotion/memoize': 0.9.0
+ '@emotion/sheet': 1.4.0
+ '@emotion/utils': 1.4.2
+ '@emotion/weak-memoize': 0.4.0
+ stylis: 4.2.0
+
+ '@emotion/hash@0.9.2': {}
+
+ '@emotion/is-prop-valid@1.4.0':
+ dependencies:
+ '@emotion/memoize': 0.9.0
+
+ '@emotion/memoize@0.9.0': {}
+
+ '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@emotion/babel-plugin': 11.13.5
+ '@emotion/cache': 11.14.0
+ '@emotion/serialize': 1.3.3
+ '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3)
+ '@emotion/utils': 1.4.2
+ '@emotion/weak-memoize': 0.4.0
+ hoist-non-react-statics: 3.3.2
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.14
+ transitivePeerDependencies:
+ - supports-color
+
+ '@emotion/serialize@1.3.3':
+ dependencies:
+ '@emotion/hash': 0.9.2
+ '@emotion/memoize': 0.9.0
+ '@emotion/unitless': 0.10.0
+ '@emotion/utils': 1.4.2
+ csstype: 3.2.3
+
+ '@emotion/sheet@1.4.0': {}
+
+ '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@emotion/babel-plugin': 11.13.5
+ '@emotion/is-prop-valid': 1.4.0
+ '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/serialize': 1.3.3
+ '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3)
+ '@emotion/utils': 1.4.2
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.14
+ transitivePeerDependencies:
+ - supports-color
+
+ '@emotion/unitless@0.10.0': {}
+
+ '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)':
+ dependencies:
+ react: 19.2.3
+
+ '@emotion/utils@1.4.2': {}
+
+ '@emotion/weak-memoize@0.4.0': {}
+
'@envelop/core@5.5.1':
dependencies:
'@envelop/instrumentation': 1.0.0
@@ -19045,6 +19468,155 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@mui/core-downloads-tracker@6.5.0': {}
+
+ '@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/core-downloads-tracker': 6.5.0
+ '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@mui/types': 7.2.24(@types/react@19.2.14)
+ '@mui/utils': 6.4.9(@types/react@19.2.14)(react@19.2.3)
+ '@popperjs/core': 2.11.8
+ '@types/react-transition-group': 4.4.12(@types/react@19.2.14)
+ clsx: 2.1.1
+ csstype: 3.2.3
+ prop-types: 15.8.1
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ react-is: 19.2.6
+ react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ optionalDependencies:
+ '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@types/react': 19.2.14
+
+ '@mui/private-theming@6.4.9(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/utils': 6.4.9(@types/react@19.2.14)(react@19.2.3)
+ prop-types: 15.8.1
+ react: 19.2.3
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@emotion/cache': 11.14.0
+ '@emotion/serialize': 1.3.3
+ '@emotion/sheet': 1.4.0
+ csstype: 3.2.3
+ prop-types: 15.8.1
+ react: 19.2.3
+ optionalDependencies:
+ '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+
+ '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/private-theming': 6.4.9(@types/react@19.2.14)(react@19.2.3)
+ '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ '@mui/types': 7.2.24(@types/react@19.2.14)
+ '@mui/utils': 6.4.9(@types/react@19.2.14)(react@19.2.3)
+ clsx: 2.1.1
+ csstype: 3.2.3
+ prop-types: 15.8.1
+ react: 19.2.3
+ optionalDependencies:
+ '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@types/react': 19.2.14
+
+ '@mui/types@7.2.24(@types/react@19.2.14)':
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/types@7.4.12(@types/react@19.2.14)':
+ dependencies:
+ '@babel/runtime': 7.29.7
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/utils@6.4.9(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/types': 7.2.24(@types/react@19.2.14)
+ '@types/prop-types': 15.7.15
+ clsx: 2.1.1
+ prop-types: 15.8.1
+ react: 19.2.3
+ react-is: 19.2.6
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/utils@7.3.11(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.29.7
+ '@mui/types': 7.4.12(@types/react@19.2.14)
+ '@types/prop-types': 15.7.15
+ clsx: 2.1.1
+ prop-types: 15.8.1
+ react: 19.2.3
+ react-is: 19.2.6
+ optionalDependencies:
+ '@types/react': 19.2.14
+
+ '@mui/x-charts-vendor@7.20.0':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-shape': 3.1.7
+ '@types/d3-time': 3.0.4
+ d3-color: 3.1.0
+ d3-delaunay: 6.0.4
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ delaunator: 5.1.0
+ robust-predicates: 3.0.3
+
+ '@mui/x-charts@7.29.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ '@mui/utils': 7.3.11(@types/react@19.2.14)(react@19.2.3)
+ '@mui/x-charts-vendor': 7.20.0
+ '@mui/x-internals': 7.29.0(@types/react@19.2.14)(react@19.2.3)
+ '@react-spring/rafz': 9.7.5
+ '@react-spring/web': 9.7.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ clsx: 2.1.1
+ prop-types: 15.8.1
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ optionalDependencies:
+ '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3)
+ '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3))(@types/react@19.2.14)(react@19.2.3)
+ transitivePeerDependencies:
+ - '@types/react'
+
+ '@mui/x-internals@7.29.0(@types/react@19.2.14)(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@mui/utils': 7.3.11(@types/react@19.2.14)(react@19.2.3)
+ react: 19.2.3
+ transitivePeerDependencies:
+ - '@types/react'
+
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
'@emnapi/core': 1.8.1
@@ -20105,6 +20677,8 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
+ '@popperjs/core@2.11.8': {}
+
'@poppinss/colors@4.1.6':
dependencies:
kleur: 4.1.5
@@ -23019,6 +23593,38 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.14
+ '@react-spring/animated@9.7.5(react@19.2.3)':
+ dependencies:
+ '@react-spring/shared': 9.7.5(react@19.2.3)
+ '@react-spring/types': 9.7.5
+ react: 19.2.3
+
+ '@react-spring/core@9.7.5(react@19.2.3)':
+ dependencies:
+ '@react-spring/animated': 9.7.5(react@19.2.3)
+ '@react-spring/shared': 9.7.5(react@19.2.3)
+ '@react-spring/types': 9.7.5
+ react: 19.2.3
+
+ '@react-spring/rafz@9.7.5': {}
+
+ '@react-spring/shared@9.7.5(react@19.2.3)':
+ dependencies:
+ '@react-spring/rafz': 9.7.5
+ '@react-spring/types': 9.7.5
+ react: 19.2.3
+
+ '@react-spring/types@9.7.5': {}
+
+ '@react-spring/web@9.7.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@react-spring/animated': 9.7.5(react@19.2.3)
+ '@react-spring/core': 9.7.5(react@19.2.3)
+ '@react-spring/shared': 9.7.5(react@19.2.3)
+ '@react-spring/types': 9.7.5
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+
'@react-stately/autocomplete@3.0.0-beta.4(react@19.2.3)':
dependencies:
'@react-stately/utils': 3.11.0(react@19.2.3)
@@ -24675,8 +25281,12 @@ snapshots:
dependencies:
undici-types: 7.18.2
+ '@types/parse-json@4.0.2': {}
+
'@types/prismjs@1.26.6': {}
+ '@types/prop-types@15.7.15': {}
+
'@types/qs@6.15.0': {}
'@types/range-parser@1.2.7': {}
@@ -24689,6 +25299,10 @@ snapshots:
dependencies:
'@types/react': 19.2.14
+ '@types/react-transition-group@4.4.12(@types/react@19.2.14)':
+ dependencies:
+ '@types/react': 19.2.14
+
'@types/react@19.2.14':
dependencies:
csstype: 3.2.3
@@ -25704,6 +26318,12 @@ snapshots:
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.20.7
+ babel-plugin-macros@3.1.0:
+ dependencies:
+ '@babel/runtime': 7.27.6
+ cosmiconfig: 7.1.0
+ resolve: 1.22.11
+
babel-plugin-polyfill-corejs2@0.4.16(@babel/core@7.29.0):
dependencies:
'@babel/compat-data': 7.29.0
@@ -26344,6 +26964,8 @@ snapshots:
content-type@1.0.5: {}
+ convert-source-map@1.9.0: {}
+
convert-source-map@2.0.0: {}
cookie-es@1.2.2: {}
@@ -26381,6 +27003,14 @@ snapshots:
dependencies:
layout-base: 2.0.1
+ cosmiconfig@7.1.0:
+ dependencies:
+ '@types/parse-json': 4.0.2
+ import-fresh: 3.3.1
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ yaml: 1.10.3
+
crc-32@1.2.2: {}
crc32-stream@6.0.0:
@@ -26943,6 +27573,10 @@ snapshots:
environment@1.1.0: {}
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
error-stack-parser-es@1.0.5: {}
error-stack-parser@2.1.4:
@@ -27918,6 +28552,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ find-root@1.1.0: {}
+
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
@@ -27969,12 +28605,13 @@ snapshots:
fraction.js@5.3.4: {}
- framer-motion@12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
+ framer-motion@12.34.3(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
motion-dom: 12.34.3
motion-utils: 12.29.2
tslib: 2.8.1
optionalDependencies:
+ '@emotion/is-prop-valid': 1.4.0
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -28058,7 +28695,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- fumadocs-ui@16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1):
+ fumadocs-ui@16.6.5(@emotion/is-prop-valid@1.4.0)(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1):
dependencies:
'@fumadocs/tailwind': 0.0.2(tailwindcss@4.2.1)
'@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -28074,7 +28711,7 @@ snapshots:
class-variance-authority: 0.7.1
fumadocs-core: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6)
lucide-react: 0.570.0(react@19.2.4)
- motion: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ motion: 12.34.3(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next-themes: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -28521,6 +29158,10 @@ snapshots:
highlightjs-vue@1.0.0: {}
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
hono-openapi@1.3.0(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(hono@4.12.9)(openapi-types@12.1.3):
dependencies:
'@standard-community/standard-json': 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@1.0.0)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)
@@ -28743,6 +29384,8 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
+ is-arrayish@0.2.1: {}
+
is-async-function@2.1.1:
dependencies:
async-function: 1.0.0
@@ -30441,11 +31084,12 @@ snapshots:
motion-utils@12.29.2: {}
- motion@12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
+ motion@12.34.3(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
- framer-motion: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ framer-motion: 12.34.3(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
tslib: 2.8.1
optionalDependencies:
+ '@emotion/is-prop-valid': 1.4.0
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -31253,6 +31897,13 @@ snapshots:
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
parse-ms@4.0.0: {}
parse-png@2.1.0:
@@ -31308,6 +31959,8 @@ snapshots:
path-to-regexp@8.3.0: {}
+ path-type@4.0.0: {}
+
pathe@1.1.2: {}
pathe@2.0.3: {}
@@ -32036,6 +32689,8 @@ snapshots:
react-is@18.3.1: {}
+ react-is@19.2.6: {}
+
react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.3):
dependencies:
'@types/hast': 3.0.4
@@ -33371,6 +34026,8 @@ snapshots:
styleq@0.1.3: {}
+ stylis@4.2.0: {}
+
stylis@4.3.6: {}
sucrase@3.35.0:
@@ -34768,6 +35425,8 @@ snapshots:
yallist@5.0.0: {}
+ yaml@1.10.3: {}
+
yaml@2.8.0: {}
yaml@2.8.3: {}