From cae4c7bdeb9532583611f4d2f71117afa4b35a71 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Sun, 22 Mar 2026 20:11:19 +0530 Subject: [PATCH 01/14] Initial working setup for heroui with just buttons components --- examples/heroui-chat/.gitignore | 41 + examples/heroui-chat/README.md | 27 + examples/heroui-chat/eslint.config.mjs | 18 + examples/heroui-chat/next.config.ts | 8 + examples/heroui-chat/package.json | 38 + examples/heroui-chat/postcss.config.mjs | 7 + .../heroui-chat/src/app/api/chat/route.ts | 303 +++ examples/heroui-chat/src/app/globals.css | 2 + examples/heroui-chat/src/app/layout.tsx | 22 + examples/heroui-chat/src/app/page.tsx | 48 + .../src/generated/system-prompt.txt | 54 + .../src/lib/heroui-genui/action.ts | 18 + .../lib/heroui-genui/components/button.tsx | 68 + .../lib/heroui-genui/components/buttons.tsx | 33 + .../components/markdown-renderer.tsx | 32 + .../heroui-genui/components/text-content.tsx | 30 + .../src/lib/heroui-genui/index.tsx | 59 + .../src/lib/heroui-genui/unions.ts | 11 + examples/heroui-chat/src/library.ts | 1 + examples/heroui-chat/tsconfig.json | 34 + pnpm-lock.yaml | 2149 ++++++++++++++++- 21 files changed, 2916 insertions(+), 87 deletions(-) create mode 100644 examples/heroui-chat/.gitignore create mode 100644 examples/heroui-chat/README.md create mode 100644 examples/heroui-chat/eslint.config.mjs create mode 100644 examples/heroui-chat/next.config.ts create mode 100644 examples/heroui-chat/package.json create mode 100644 examples/heroui-chat/postcss.config.mjs create mode 100644 examples/heroui-chat/src/app/api/chat/route.ts create mode 100644 examples/heroui-chat/src/app/globals.css create mode 100644 examples/heroui-chat/src/app/layout.tsx create mode 100644 examples/heroui-chat/src/app/page.tsx create mode 100644 examples/heroui-chat/src/generated/system-prompt.txt create mode 100644 examples/heroui-chat/src/lib/heroui-genui/action.ts create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/button.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/index.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/unions.ts create mode 100644 examples/heroui-chat/src/library.ts create mode 100644 examples/heroui-chat/tsconfig.json diff --git a/examples/heroui-chat/.gitignore b/examples/heroui-chat/.gitignore new file mode 100644 index 000000000..5ef6a5207 --- /dev/null +++ b/examples/heroui-chat/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/heroui-chat/README.md b/examples/heroui-chat/README.md new file mode 100644 index 000000000..03a161d8b --- /dev/null +++ b/examples/heroui-chat/README.md @@ -0,0 +1,27 @@ +This is an [OpenUI](https://openui.com) Agent Chat project bootstrapped with [`openui-cli`](https://openui.com/docs/chat/quick-start). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `src/app/api/route.ts` and improving your agent +by adding system prompts or tools. + +## Learn More + +To learn more about OpenUI, take a look at the following resources: + +- [OpenUI Documentation](https://openui.com/docs) - learn about OpenUI features and API. +- [OpenUI GitHub repository](https://github.com/thesysdev/openui) - your feedback and contributions are welcome! diff --git a/examples/heroui-chat/eslint.config.mjs b/examples/heroui-chat/eslint.config.mjs new file mode 100644 index 000000000..05e726d1b --- /dev/null +++ b/examples/heroui-chat/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/examples/heroui-chat/next.config.ts b/examples/heroui-chat/next.config.ts new file mode 100644 index 000000000..69512fdcd --- /dev/null +++ b/examples/heroui-chat/next.config.ts @@ -0,0 +1,8 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + turbopack: {}, + transpilePackages: ["@openuidev/react-ui", "@openuidev/react-headless", "@openuidev/react-lang"], +}; + +export default nextConfig; diff --git a/examples/heroui-chat/package.json b/examples/heroui-chat/package.json new file mode 100644 index 000000000..8b47fc8ad --- /dev/null +++ b/examples/heroui-chat/package.json @@ -0,0 +1,38 @@ +{ + "name": "openui-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": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@heroui/react": "^3.0.1", + "@heroui/styles": "^3.0.1", + "@openuidev/react-headless": "workspace:*", + "@openuidev/react-lang": "workspace:*", + "@openuidev/react-ui": "workspace:*", + "lucide-react": "^0.575.0", + "next": "16.1.6", + "openai": "^6.22.0", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "zod": "^4.0.0" + }, + "devDependencies": { + "@openuidev/cli": "workspace:*", + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.1.6", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/examples/heroui-chat/postcss.config.mjs b/examples/heroui-chat/postcss.config.mjs new file mode 100644 index 000000000..61e36849c --- /dev/null +++ b/examples/heroui-chat/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/examples/heroui-chat/src/app/api/chat/route.ts b/examples/heroui-chat/src/app/api/chat/route.ts new file mode 100644 index 000000000..0ebcc126b --- /dev/null +++ b/examples/heroui-chat/src/app/api/chat/route.ts @@ -0,0 +1,303 @@ +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"); + +// ── 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(); + + const client = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, + }); + const MODEL = "gpt-5.4"; + + // 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 */ } + }; + + 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) { + 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", () => { + 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/heroui-chat/src/app/globals.css b/examples/heroui-chat/src/app/globals.css new file mode 100644 index 000000000..ed35a3374 --- /dev/null +++ b/examples/heroui-chat/src/app/globals.css @@ -0,0 +1,2 @@ +@import "tailwindcss"; +@import "@heroui/styles"; diff --git a/examples/heroui-chat/src/app/layout.tsx b/examples/heroui-chat/src/app/layout.tsx new file mode 100644 index 000000000..ee2918ae5 --- /dev/null +++ b/examples/heroui-chat/src/app/layout.tsx @@ -0,0 +1,22 @@ +import { ThemeProvider } from "@openuidev/react-ui"; +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "OpenUI Chat", + description: "Generative UI Chat with OpenAI SDK", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/examples/heroui-chat/src/app/page.tsx b/examples/heroui-chat/src/app/page.tsx new file mode 100644 index 000000000..18e6fdec2 --- /dev/null +++ b/examples/heroui-chat/src/app/page.tsx @@ -0,0 +1,48 @@ +"use client"; +import "@openuidev/react-ui/components.css"; + +import { herouiChatLibrary } from "@/lib/heroui-genui"; +import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless"; +import { FullScreen } from "@openuidev/react-ui"; + +export default function Page() { + return ( +
+ { + return fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + messages: openAIMessageFormat.toApi(messages), + }), + signal: abortController.signal, + }); + }} + streamProtocol={openAIAdapter()} + componentLibrary={herouiChatLibrary} + agentName="OpenUI Chat" + theme={{ mode: "light" }} + conversationStarters={{ + variant: "short", + options: [ + { + displayText: "Weather in Tokyo", + prompt: "What's the weather like in Tokyo right now?", + }, + { displayText: "AAPL stock price", prompt: "What's the current Apple stock price?" }, + { + displayText: "Contact form", + prompt: "Build me a contact form with name, email, topic, and message fields.", + }, + { + displayText: "Data table", + prompt: + "Show me a table of the top 5 programming languages by popularity with year created.", + }, + ], + }} + /> +
+ ); +} diff --git a/examples/heroui-chat/src/generated/system-prompt.txt b/examples/heroui-chat/src/generated/system-prompt.txt new file mode 100644 index 000000000..cc0d22f12 --- /dev/null +++ b/examples/heroui-chat/src/generated/system-prompt.txt @@ -0,0 +1,54 @@ + +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), 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) +7. Optional arguments can be omitted from the end +8. No operators, no logic, no variables — only declarations +9. 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. +The `action` prop type accepts: ContinueConversation (sends message to LLM), OpenUrl (navigates to URL), or Custom (app-defined). + +### Content +TextContent(text: string, size?: "small" | "default" | "large" | "small-heavy" | "large-heavy") — Plain text block. size: "small" | "default" | "large" | "small-heavy" | "large-heavy". +MarkDownRenderer(textMarkdown: string, variant?: "clear" | "card" | "sunk") — Renders markdown text with optional container variant + +### Buttons +Button(label: string, action?: {type: "open_url", url: string} | {type: "continue_conversation", context?: string}, variant?: "primary" | "secondary" | "tertiary" | "outline" | "ghost" | "danger", size?: "sm" | "md" | "lg", fullWidth?: boolean) — Clickable button +Buttons(buttons: Button[]) — Group of Button components rendered together with consistent spacing. + +### Ungrouped +Card(children: (TextContent | MarkDownRenderer | Buttons)[]) — 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. +## Important Rules +- ALWAYS start with root = Card(...) +- Write statements in TOP-DOWN order: root → components → data (leverages hoisting for progressive streaming) +- Each statement on its own line +- No trailing text or explanations — output ONLY openui-lang code +- 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.) +- NEVER define a variable without referencing it from the tree. Every variable must be reachable from root, otherwise it will not render. + +- Every response is a single Card(children) — children stack vertically automatically. +- Card is the only layout container. diff --git a/examples/heroui-chat/src/lib/heroui-genui/action.ts b/examples/heroui-chat/src/lib/heroui-genui/action.ts new file mode 100644 index 000000000..250370acc --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/action.ts @@ -0,0 +1,18 @@ +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(), +}); + +export const actionSchema = z + .union([openUrlAction, continueConversationAction]) + .optional(); + +export type ActionSchema = z.infer; diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx new file mode 100644 index 000000000..f5d089b35 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx @@ -0,0 +1,68 @@ +"use client"; + +import { Button as HeroUIButton } from "@heroui/react"; +import { + BuiltinActionType, + type ComponentRenderProps, + 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(["primary", "secondary", "tertiary", "outline", "ghost", "danger"]).optional(), + size: z.enum(["sm", "md", "lg"]).optional(), + fullWidth: z.boolean().optional(), +}); + +function ButtonRenderer({ props }: ComponentRenderProps>) { + const triggerAction = useTriggerAction(); + const formName = useFormName(); + const formValidation = useFormValidation(); + const isStreaming = useIsStreaming(); + + const label = String(props.label ?? ""); + const action = props.action as ActionSchema; + const variant = props.variant ?? "primary"; + const size = props.size ?? "md"; + + return ( + { + const actionType = action?.type ?? BuiltinActionType.ContinueConversation; + if ( + formValidation && + variant === "primary" && + 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} + + ); +} + +export const Button = defineComponent({ + name: "Button", + props: ButtonSchema, + description: "Clickable button", + component: ButtonRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx new file mode 100644 index 000000000..24db5c757 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { ButtonGroup } from "@heroui/react"; +import { defineComponent } from "@openuidev/react-lang"; +import { z } from "zod"; +import { Button } from "./button"; + +const ButtonsSchema = z.object({ + buttons: z.array(Button.ref), + grouped: z.boolean().optional(), +}); + +export const Buttons = defineComponent({ + name: "Buttons", + props: ButtonsSchema, + description: + 'Row of Button components. grouped: true joins buttons into a connected group with a separator between them.', + component: ({ props, renderNode }) => { + if (props.grouped) { + return ( + + + {renderNode(props.buttons)} + + ); + } + return ( +
+ {renderNode(props.buttons)} +
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx new file mode 100644 index 000000000..2422205ca --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { defineComponent } from "@openuidev/react-lang"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import { z } from "zod"; + +const MarkDownRendererSchema = z.object({ + textMarkdown: z.string(), + variant: z.enum(["clear", "card", "sunk"]).optional(), +}); + +export const MarkDownRenderer = defineComponent({ + name: "MarkDownRenderer", + props: MarkDownRendererSchema, + description: "Renders markdown text with optional container variant", + component: ({ props }) => { + const text = props.textMarkdown == null ? "" : String(props.textMarkdown); + const variant = (props.variant as string) ?? "clear"; + const variantClass = + variant === "card" + ? "bg-content2 rounded-lg p-4" + : variant === "sunk" + ? "bg-content3 rounded-lg p-4" + : ""; + return ( +
+ {text} +
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx new file mode 100644 index 000000000..f9bfe613d --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx @@ -0,0 +1,30 @@ +"use client"; + +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(), +}); + +const sizeClasses: Record = { + small: "text-sm text-muted", + default: "text-base", + large: "text-lg", + "small-heavy": "text-sm font-semibold", + "large-heavy": "text-lg font-semibold", +}; + +export const TextContent = defineComponent({ + name: "TextContent", + props: TextContentSchema, + description: + 'Plain text block. size: "small" | "default" | "large" | "small-heavy" | "large-heavy".', + component: ({ props }) => { + const text = props.text == null ? "" : String(props.text); + const size = (props.size as string) ?? "default"; + const cls = sizeClasses[size] ?? sizeClasses["default"]; + return

{text}

; + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/index.tsx b/examples/heroui-chat/src/lib/heroui-genui/index.tsx new file mode 100644 index 000000000..a2359bcd2 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/index.tsx @@ -0,0 +1,59 @@ +"use client"; + +import { Card } from "@heroui/react"; +import type { ComponentGroup, PromptOptions } from "@openuidev/react-lang"; +import { createLibrary, defineComponent } from "@openuidev/react-lang"; +import { z } from "zod"; + +import { Button } from "./components/button"; +import { Buttons } from "./components/buttons"; +import { MarkDownRenderer } from "./components/markdown-renderer"; +import { TextContent } from "./components/text-content"; +import { ChatContentChildUnion } from "./unions"; + +const ChatCard = defineComponent({ + name: "Card", + props: z.object({ + children: z.array(ChatContentChildUnion), + }), + 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 herouiComponentGroups: ComponentGroup[] = [ + { + name: "Content", + components: ["TextContent", "MarkDownRenderer"], + }, + { + name: "Buttons", + components: ["Button", "Buttons"], + }, +]; + +// ── Prompt Options ── + +export const herouiExamples: string[] = []; + +export const herouiAdditionalRules: string[] = [ + "Every response is a single Card(children) — children stack vertically automatically.", + "Card is the only layout container.", +]; + +export const herouiPromptOptions: PromptOptions = { + examples: herouiExamples, + additionalRules: herouiAdditionalRules, +}; + +// ── Library ── + +export const herouiChatLibrary = createLibrary({ + root: "Card", + componentGroups: herouiComponentGroups, + components: [ChatCard, TextContent, MarkDownRenderer, Button, Buttons], +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/unions.ts b/examples/heroui-chat/src/lib/heroui-genui/unions.ts new file mode 100644 index 000000000..e150e8cc9 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/unions.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +import { Buttons } from "./components/buttons"; +import { MarkDownRenderer } from "./components/markdown-renderer"; +import { TextContent } from "./components/text-content"; + +export const ChatContentChildUnion = z.union([ + TextContent.ref, + MarkDownRenderer.ref, + Buttons.ref, +]); diff --git a/examples/heroui-chat/src/library.ts b/examples/heroui-chat/src/library.ts new file mode 100644 index 000000000..bac573a7f --- /dev/null +++ b/examples/heroui-chat/src/library.ts @@ -0,0 +1 @@ +export { herouiChatLibrary as library, herouiPromptOptions as promptOptions } from "@/lib/heroui-genui"; diff --git a/examples/heroui-chat/tsconfig.json b/examples/heroui-chat/tsconfig.json new file mode 100644 index 000000000..cf9c65d3e --- /dev/null +++ b/examples/heroui-chat/tsconfig.json @@ -0,0 +1,34 @@ +{ + "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 3a4984b0e..0aa68ec76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,7 +64,7 @@ importers: version: 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) fumadocs-mdx: specifier: 14.2.8 - 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)(sass@1.89.2)) + 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.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)) 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) @@ -130,6 +130,76 @@ importers: specifier: ^4.1.18 version: 4.2.1 + examples/heroui-chat: + dependencies: + '@heroui/react': + specifier: ^3.0.1 + version: 3.0.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.2.1) + '@heroui/styles': + specifier: ^3.0.1 + version: 3.0.1(tailwind-merge@3.4.0)(tailwindcss@4.2.1) + '@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 + lucide-react: + specifier: ^0.575.0 + version: 0.575.0(react@19.2.3) + 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.22.0(ws@8.18.2)(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) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.14)(react@19.2.3) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + zod: + specifier: ^4.0.0 + version: 4.3.6 + devDependencies: + '@openuidev/cli': + specifier: workspace:* + version: link:../../packages/openui-cli + '@tailwindcss/postcss': + specifier: ^4 + version: 4.2.1 + '@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) + tailwindcss: + specifier: ^4 + version: 4.2.1 + typescript: + specifier: ^5 + version: 5.9.3 + examples/openui-chat: dependencies: '@openuidev/react-headless': @@ -2152,9 +2222,24 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + '@formatjs/fast-memoize@3.1.0': resolution: {integrity: sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg==} + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} + + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + '@formatjs/intl-localematcher@0.8.1': resolution: {integrity: sha512-xwEuwQFdtSq1UKtQnyTZWC+eHdv7Uygoa+H2k/9uzBVQjDyp9r20LNDNKedWXll7FssT3GRHvqsdJGYSUWqYFA==} @@ -2166,6 +2251,18 @@ packages: tailwindcss: optional: true + '@heroui/react@3.0.1': + resolution: {integrity: sha512-Dx5oku2LPgK1+Uy1KIyVcfqgYTkVCXEII9OuwoKIJe7GW9Lf1xdHpSTwsdDZoNrUl8IvkDjav1ud92OGY/maRA==} + peerDependencies: + react: '>=19.0.0' + react-dom: '>=19.0.0' + tailwindcss: '>=4.0.0' + + '@heroui/styles@3.0.1': + resolution: {integrity: sha512-ZGXz6nse8713f3uZMNapa8STkiyERgE7P5qOJlWTGnO8qHC+OHCB9TRJOdz1JR+daAhxu68pBO1qdWlzvud1cw==} + peerDependencies: + tailwindcss: '>=4.0.0' + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -2457,6 +2554,18 @@ packages: '@types/node': optional: true + '@internationalized/date@3.12.0': + resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==} + + '@internationalized/message@3.1.8': + resolution: {integrity: sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA==} + + '@internationalized/number@3.6.5': + resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} + + '@internationalized/string@3.2.7': + resolution: {integrity: sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -3863,6 +3972,297 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@react-aria/autocomplete@3.0.0-rc.6': + resolution: {integrity: sha512-uymUNJ8NW+dX7lmgkHE+SklAbxwktycAJcI5lBBw6KPZyc0EdMHC+/Fc5CUz3enIAhNwd2oxxogcSHknquMzQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/breadcrumbs@3.5.32': + resolution: {integrity: sha512-S61vh5DJ2PXiXUwD7gk+pvS/b4VPrc3ZJOUZ0yVRLHkVESr5LhIZH+SAVgZkm1lzKyMRG+BH+fiRH/DZRSs7SA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/button@3.14.5': + resolution: {integrity: sha512-ZuLx+wQj9VQhH9BYe7t0JowmKnns2XrFHFNvIVBb5RwxL+CIycIOL7brhWKg2rGdxvlOom7jhVbcjSmtAaSyaQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/calendar@3.9.5': + resolution: {integrity: sha512-k0kvceYdZZu+DoeqephtlmIvh1CxqdFyoN52iqVzTz9O0pe5Xfhq7zxPGbeCp4pC61xzp8Lu/6uFA/YNfQQNag==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/checkbox@3.16.5': + resolution: {integrity: sha512-ZhUT7ELuD52hb+Zpzw0ElLQiVOd5sKYahrh+PK3vq13Wk5TedBscALpjuXetI4pwFfdmAM1Lhgcsrd8+6AmyvA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/collections@3.0.3': + resolution: {integrity: sha512-lbC5DEbHeVFvVr4ke9y8D9Nynnr8G8UjVEBoFGRylpAaScU7SX1TN84QI+EjMbsdZ0/5P2H7gUTS+MYd+6U3Rg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/color@3.1.5': + resolution: {integrity: sha512-eysWdBRzE8WDhBzh1nfjyUgzseMokXGHjIoJo880T7IPJ8tTavfQni49pU1B2qWrNOWPyrwx4Bd9pzHyboxJSA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/combobox@3.15.0': + resolution: {integrity: sha512-qSjQTFwKl3x1jCP2NRSJ6doZqAp6c2GTfoiFwWjaWg1IewwLsglaW6NnzqRDFiqFbDGgXPn4MqtC1VYEJ3NEjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/datepicker@3.16.1': + resolution: {integrity: sha512-6BltCVWt09yefTkGjb2gViGCwoddx9HKJiZbY9u6Es/Q+VhwNJQRtczbnZ3K32p262hIknukNf/5nZaCOI1AKA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/dialog@3.5.34': + resolution: {integrity: sha512-/x53Q5ynpW5Kv9637WYu7SrDfj3woSp6jJRj8l6teGnWW/iNZWYJETgzHfbxx+HPKYATCZesRoIeO2LnYIXyEA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/disclosure@3.1.3': + resolution: {integrity: sha512-S3k7Wqrj+x0sWcP88Z1stSr5TIZmKEmx2rU7RB1O1/jPpbw5mgKnjtiriOlTh+kwdK11FkeqgxyHzAcBAR+FMQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/dnd@3.11.6': + resolution: {integrity: sha512-4YLHUeYJleF+moAYaYt8UZqujudPvpoaHR+QMkWIFzhfridVUhCr6ZjGWrzpSZY3r68k46TG7YCsi4IEiNnysw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/focus@3.21.5': + resolution: {integrity: sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/form@3.1.5': + resolution: {integrity: sha512-BWlONgHn8hmaMkcS6AgMSLQeNqVBwqPNLhdqjDO/PCfzvV7O8NZw/dFeIzJwfG4aBfSpbHHRdXGdfrk3d8dylQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/grid@3.14.8': + resolution: {integrity: sha512-X6rRFKDu/Kh6Sv8FBap3vjcb+z4jXkSOwkYnexIJp5kMTo5/Dqo55cCBio5B70Tanfv32Ev/6SpzYG7ryxnM9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/gridlist@3.14.4': + resolution: {integrity: sha512-C/SbwC0qagZatoBrCjx8iZUex9apaJ8o8iRJ9eVHz0cpj7mXg6HuuotYGmDy9q67A2hve4I693RM1Cuwqwm+PQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/i18n@3.12.16': + resolution: {integrity: sha512-Km2CAz6MFQOUEaattaW+2jBdWOHUF8WX7VQoNbjlqElCP58nSaqi9yxTWUDRhAcn8/xFUnkFh4MFweNgtrHuEA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/interactions@3.27.1': + resolution: {integrity: sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/label@3.7.25': + resolution: {integrity: sha512-oNK3Pqj4LDPwEbQaoM/uCip4QvQmmwGOh08VeW+vzSi6TAwf+KoWTyH/tiAeB0CHWNDK0k3e1iTygTAt4wzBmg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/landmark@3.0.10': + resolution: {integrity: sha512-GpNjJaI8/a6WxYDZgzTCLYSzPM6xp2pxCIQ4udiGbTCtxx13Trmm0cPABvPtzELidgolCf05em9Phr+3G0eE8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/link@3.8.9': + resolution: {integrity: sha512-UaAFBfs84/Qq6TxlMWkREqqNY6SFLukot+z2Aa1kC+VyStv1kWG6sE5QLjm4SBn1Q3CGRsefhB/5+taaIbB4Pw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/listbox@3.15.3': + resolution: {integrity: sha512-C6YgiyrHS5sbS5UBdxGMhEs+EKJYotJgGVtl9l0ySXpBUXERiHJWLOyV7a8PwkUOmepbB4FaLD7Y9EUzGkrGlw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/live-announcer@3.4.4': + resolution: {integrity: sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA==} + + '@react-aria/menu@3.21.0': + resolution: {integrity: sha512-CKTVZ4izSE1eKIti6TbTtzJAUo+WT8O4JC0XZCYDBpa0f++lD19Kz9aY+iY1buv5xGI20gAfpO474E9oEd4aQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/meter@3.4.30': + resolution: {integrity: sha512-ZmANKW7s/Z4QGylHi46nhwtQ47T1bfMsU9MysBu7ViXXNJ03F4b6JXCJlKL5o2goQ3NbfZ68GeWamIT0BWSgtw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/numberfield@3.12.5': + resolution: {integrity: sha512-Fi41IUWXEHLFIeJ/LHuZ9Azs8J/P563fZi37GSBkIq5P1pNt1rPgJJng5CNn4KsHxwqadTRUlbbZwbZraWDtRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/overlays@3.31.2': + resolution: {integrity: sha512-78HYI08r6LvcfD34gyv19ArRIjy1qxOKuXl/jYnjLDyQzD4pVb634IQWcm0zt10RdKgyuH6HTqvuDOgZTLet7Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/progress@3.4.30': + resolution: {integrity: sha512-S6OWVGgluSWYSd/A6O8CVjz83eeMUfkuWSra0ewAV9bmxZ7TP9pUmD3bGdqHZEl97nt5vHGjZ3eq/x8eCmzKhA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/radio@3.12.5': + resolution: {integrity: sha512-8CCJKJzfozEiWBPO9QAATG1rBGJEJ+xoqvHf9LKU2sPFGsA2/SRnLs6LB9fCG5R3spvaK1xz0any1fjWPl7x8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/searchfield@3.8.12': + resolution: {integrity: sha512-kYlUHD/+mWzNroHoR8ojUxYBoMviRZn134WaKPFjfNUGZDOEuh4XzOoj+cjdJfe6N3mwTaYu6rJQtunSHIAfhA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/select@3.17.3': + resolution: {integrity: sha512-u0UFWw0S7q9oiSbjetDpRoLLIcC+L89uYlm+YfCrdT8ntbQgABNiJRxdVvxnhR0fR6MC9ASTTvuQnNHNn52+1A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/selection@3.27.2': + resolution: {integrity: sha512-GbUSSLX/ciXix95KW1g+SLM9np7iXpIZrFDSXkC6oNx1uhy18eAcuTkeZE25+SY5USVUmEzjI3m/3JoSUcebbg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/separator@3.4.16': + resolution: {integrity: sha512-RCUtQhDGnPxKzyG8KM79yOB0fSiEf8r/rxShidOVnGLiBW2KFmBa22/Gfc4jnqg/keN3dxvkSGoqmeXgctyp6g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/slider@3.8.5': + resolution: {integrity: sha512-gqkJxznk141mE0JamXF5CXml9PDbPkBz8dyKlihtWHWX4yhEbVYdC9J0otE7iCR3zx69Bm7WHoTGL0BsdpKzVA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/spinbutton@3.7.2': + resolution: {integrity: sha512-adjE1wNCWlugvAtVXlXWPtIG9JWurEgYVn1Eeyh19x038+oXGvOsOAoKCXM+SnGleTWQ9J7pEZITFoEI3cVfAw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/ssr@3.9.10': + resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/switch@3.7.11': + resolution: {integrity: sha512-dYVX71HiepBsKyeMaQgHbhqI+MQ3MVoTd5EnTbUjefIBnmQZavYj1/e4NUiUI4Ix+/C0HxL8ibDAv4NlSW3eLQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/table@3.17.11': + resolution: {integrity: sha512-GkYmWPiW3OM+FUZxdS33teHXHXde7TjHuYgDDaG9phvg6cQTQjGilJozrzA3OfftTOq5VB8XcKTIQW3c0tpYsQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/tabs@3.11.1': + resolution: {integrity: sha512-3Ppz7yaEDW9L7p9PE9yNOl5caLwNnnLQqI+MX/dwbWlw9HluHS7uIjb21oswNl6UbSxAWyENOka45+KN4Fkh7A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/tag@3.8.1': + resolution: {integrity: sha512-VonpO++F8afXGDWc9VUxAc2wefyJpp1n9OGpbnB7zmqWiuPwO/RixjUdcH7iJkiC4vADwx9uLnhyD6kcwGV2ig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/textfield@3.18.5': + resolution: {integrity: sha512-ttwVSuwoV3RPaG2k2QzEXKeQNQ3mbdl/2yy6I4Tjrn1ZNkYHfVyJJ26AjenfSmj1kkTQoSAfZ8p+7rZp4n0xoQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/toast@3.0.11': + resolution: {integrity: sha512-2DjZjBAvm8/CWbnZ6s7LjkYCkULKtjMve6GvhPTq98AthuEDLEiBvM1wa3xdecCRhZyRT1g6DXqVca0EfZ9fJA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/toggle@3.12.5': + resolution: {integrity: sha512-XXVFLzcV8fr9mz7y/wfxEAhWvaBZ9jSfhCMuxH2bsivO7nTcMJ1jb4g2xJNwZgne17bMWNc7mKvW5dbsdlI6BA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/toolbar@3.0.0-beta.24': + resolution: {integrity: sha512-B2Rmpko7Ghi2RbNfsGdbR7I+RQBDhPGVE4bU3/EwHz+P/vNe5LyGPTeSwqaOMsQTF9lKNCkY8424dVTCr6RUMg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/tooltip@3.9.2': + resolution: {integrity: sha512-VrgkPwHiEnAnBhoQ4W7kfry/RfVuRWrUPaJSp0+wKM6u0gg2tmn7OFRDXTxBAm/omQUguIdIjRWg7sf3zHH82A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/tree@3.1.7': + resolution: {integrity: sha512-C54yH5NmsOFa2Q+cg6B1BPr5KUlU9vLIoBnVrgrH237FRSXQPIbcM4VpmITAHq1VR7w6ayyS1hgTwFxo67ykWQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/utils@3.33.1': + resolution: {integrity: sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/virtualizer@4.1.13': + resolution: {integrity: sha512-d5KS+p8GXGNRbGPRE/N6jtth3et3KssQIz52h2+CAoAh7C3vvR64kkTaGdeywClvM+fSo8FxJuBrdfQvqC2ktQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/visually-hidden@3.8.31': + resolution: {integrity: sha512-RTOHHa4n56a9A3criThqFHBifvZoV71+MCkSuNP2cKO662SUWjqKkd0tJt/mBRMEJPkys8K7Eirp6T8Wt5FFRA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@react-email/body@0.0.11': resolution: {integrity: sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==} peerDependencies: @@ -4081,89 +4481,384 @@ packages: '@types/react': optional: true - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} - engines: {node: '>=14.0.0'} + '@react-stately/autocomplete@3.0.0-beta.4': + resolution: {integrity: sha512-K2Uy7XEdseFvgwRQ8CyrYEHMupjVKEszddOapP8deNz4hntYvT1aRm0m+sKa5Kl/4kvg9c/3NZpQcrky/vRZIg==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-android-arm-eabi@4.43.0': - resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} - cpu: [arm] - os: [android] + '@react-stately/calendar@3.9.3': + resolution: {integrity: sha512-uw7fCZXoypSBBUsVkbNvJMQWTihZReRbyLIGG3o/ZM630N3OCZhb/h4Uxke4pNu7n527H0V1bAnZgAldIzOYqg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-android-arm64@4.43.0': - resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} - cpu: [arm64] - os: [android] + '@react-stately/checkbox@3.7.5': + resolution: {integrity: sha512-K5R5ted7AxLB3sDkuVAazUdyRMraFT1imVqij2GuAiOUFvsZvbuocnDuFkBVKojyV3GpqLBvViV8IaCMc4hNIw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-darwin-arm64@4.43.0': - resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} - cpu: [arm64] - os: [darwin] + '@react-stately/collections@3.12.10': + resolution: {integrity: sha512-wmF9VxJDyBujBuQ76vXj2g/+bnnj8fx5DdXgRmyfkkYhPB46+g2qnjbVGEvipo7bJuGxDftCUC4SN7l7xqUWfg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-darwin-x64@4.43.0': - resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} - cpu: [x64] - os: [darwin] + '@react-stately/color@3.9.5': + resolution: {integrity: sha512-8pZxzXWDRuglzDwyTG7mLw2LQMCHIVNbVc9YmbsxbOjAL+lOqszo60KzyaFKVxeDQczSvrNTHcQZqlbNIC0eyQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-freebsd-arm64@4.43.0': - resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} - cpu: [arm64] - os: [freebsd] + '@react-stately/combobox@3.13.0': + resolution: {integrity: sha512-dX9g/cK1hjLRjcbWVF6keHxTQDGhKGB2QAgPhWcBmOK3qJv+2dQqsJ6YCGWn/Y2N2acoEseLrAA7+Qe4HWV9cg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-freebsd-x64@4.43.0': - resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} - cpu: [x64] - os: [freebsd] + '@react-stately/data@3.15.2': + resolution: {integrity: sha512-BsmeeGgFwOGwo0g9Waprdyt+846n3KhKggZfpEnp5+sC4dE4uW1VIYpdyupMfr3bQcmX123q6TegfNP3eszrUA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-arm-gnueabihf@4.43.0': - resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} - cpu: [arm] - os: [linux] + '@react-stately/datepicker@3.16.1': + resolution: {integrity: sha512-BtAMDvxd1OZxkxjqq5tN5TYmp6Hm8+o3+IDA4qmem2/pfQfVbOZeWS2WitcPBImj4n4T+W1A5+PI7mT/6DUBVg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-arm-musleabihf@4.43.0': - resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} - cpu: [arm] - os: [linux] + '@react-stately/disclosure@3.0.11': + resolution: {integrity: sha512-/KjB/0HkxGWbhFAPztCP411LUKZCx9k8cKukrlGqrUWyvrcXlmza90j0g/CuxACBoV+DJP9V+4q+8ide0x750A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-arm64-gnu@4.43.0': - resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} - cpu: [arm64] - os: [linux] + '@react-stately/dnd@3.7.4': + resolution: {integrity: sha512-YD0TVR5JkvTqskc1ouBpVKs6t/QS4RYCIyu8Ug8RgO122iIizuf2pfKnRLjYMdu5lXzBXGaIgd49dvnLzEXHIw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-arm64-musl@4.43.0': - resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} - cpu: [arm64] - os: [linux] + '@react-stately/flags@3.1.2': + resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} - '@rollup/rollup-linux-loongarch64-gnu@4.43.0': - resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} - cpu: [loong64] - os: [linux] + '@react-stately/form@3.2.4': + resolution: {integrity: sha512-qNBzun8SbLdgahryhKLqL1eqP+MXY6as82sVXYOOvUYLzgU5uuN8mObxYlxJgMI5akSdQJQV3RzyfVobPRE7Kw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': - resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} - cpu: [ppc64] - os: [linux] + '@react-stately/grid@3.11.9': + resolution: {integrity: sha512-qQY6F+27iZRn30dt0ZOrSetUmbmNJ0pLe9Weuqw3+XDVSuWT+2O/rO1UUYeK+mO0Acjzdv+IWiYbu9RKf2wS9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-riscv64-gnu@4.43.0': - resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} - cpu: [riscv64] - os: [linux] + '@react-stately/layout@4.6.0': + resolution: {integrity: sha512-kBenEsP03nh5rKgfqlVMPcoKTJv0v92CTvrAb5gYY8t9g8LOwzdL89Yannq7f5xv8LFck/MmRQlotpMt2InETg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-riscv64-musl@4.43.0': - resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} - cpu: [riscv64] - os: [linux] + '@react-stately/list@3.13.4': + resolution: {integrity: sha512-HHYSjA9VG7FPSAtpXAjQyM/V7qFHWGg88WmMrDt5QDlTBexwPuH0oFLnW0qaVZpAIxuWIsutZfxRAnme/NhhAA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@rollup/rollup-linux-s390x-gnu@4.43.0': - resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} - cpu: [s390x] - os: [linux] + '@react-stately/menu@3.9.11': + resolution: {integrity: sha512-vYkpO9uV2OUecsIkrOc+Urdl/s1xw/ibNH/UXsp4PtjMnS6mK9q2kXZTM3WvMAKoh12iveUO+YkYCZQshmFLHQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/numberfield@3.11.0': + resolution: {integrity: sha512-rxfC047vL0LP4tanjinfjKAriAvdVL57Um5RUL5nHML8IOWCB3TBxegQkJ6to6goScC/oZhd0/Y2LSaiRuKbNw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/overlays@3.6.23': + resolution: {integrity: sha512-RzWxots9A6gAzQMP4s8hOAHV7SbJRTFSlQbb6ly1nkWQXacOSZSFNGsKOaS0eIatfNPlNnW4NIkgtGws5UYzfw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/radio@3.11.5': + resolution: {integrity: sha512-QxA779S4ea5icQ0ja7CeiNzY1cj7c9G9TN0m7maAIGiTSinZl2Ia8naZJ0XcbRRp+LBll7RFEdekne15TjvS/w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/searchfield@3.5.19': + resolution: {integrity: sha512-URllgjbtTQEaOCfddbHpJSPKOzG3pE3ajQHJ7Df8qCoHTjKfL6hnm/vp7X5sxPaZaN7VLZ5kAQxTE8hpo6s0+A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/select@3.9.2': + resolution: {integrity: sha512-oWn0bijuusp8YI7FRM/wgtPVqiIrgU/ZUfLKe/qJUmT8D+JFaMAJnyrAzKpx98TrgamgtXynF78ccpopPhgrKQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/selection@3.20.9': + resolution: {integrity: sha512-RhxRR5Wovg9EVi3pq7gBPK2BoKmP59tOXDMh2r1PbnGevg/7TNdR67DCEblcmXwHuBNS46ELfKdd0XGHqmS8nQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/slider@3.7.5': + resolution: {integrity: sha512-OrQMNR5xamLYH52TXtvTgyw3EMwv+JI+1istQgEj1CHBjC9eZZqn5iNCN20tzm+uDPTH0EIGULFjjPIumqYUQg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/table@3.15.4': + resolution: {integrity: sha512-fGaNyw3wv7JgRCNzgyDzpaaTFuSy5f4Qekch4UheMXDJX7dOeaMhUXeOfvnXCVg+BGM4ey/D82RvDOGvPy1Nww==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/tabs@3.8.9': + resolution: {integrity: sha512-AQ4Xrn6YzIolaVShCV9cnwOjBKPAOGP/PTp7wpSEtQbQ0HZzUDG2RG/M4baMeUB2jZ33b7ifXyPcK78o0uOftg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/toast@3.1.3': + resolution: {integrity: sha512-mT9QJKmD523lqFpOp0VWZ6QHZENFK7HrodnNJDVc7g616s5GNmemdlkITV43fSY3tHeThCVvPu+Uzh7RvQ9mpQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/toggle@3.9.5': + resolution: {integrity: sha512-PVzXc788q3jH98Kvw1LYDL+wpVC14dCEKjOku8cSaqhEof6AJGaLR9yq+EF1yYSL2dxI6z8ghc0OozY8WrcFcA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/tooltip@3.5.11': + resolution: {integrity: sha512-o8PnFXbvDCuVZ4Ht9ahfS6KHwIZjXopvoQ2vUPxv920irdgWEeC+4omgDOnJ/xFvcpmmJAmSsrQsTQrTguDUQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/tree@3.9.6': + resolution: {integrity: sha512-JCuhGyX2A+PAMsx2pRSwArfqNFZJ9JSPkDaOQJS8MFPAsBe5HemvXsdmv9aBIMzlbCYcVq6EsrFnzbVVTBt/6w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/utils@3.11.0': + resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/virtualizer@4.4.6': + resolution: {integrity: sha512-9SfXgLFB61/8SXNLfg5ARx9jAK4m03Aw6/Cg8mdZN24SYarL4TKNRpfw8K/HHVU/bi6WHSJypk6Z/z19o/ztrg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/autocomplete@3.0.0-alpha.38': + resolution: {integrity: sha512-0XrlVC8drzcrCNzybbkZdLcTofXEzBsHuaFevt5awW1J0xBJ+SMLIQMDeUYrvKjjwXUBlCtjJJpOvitGt4Z+KA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/breadcrumbs@3.7.19': + resolution: {integrity: sha512-AnkyYYmzaM2QFi/N0P/kQLM8tHOyFi7p397B/jEMucXDfwMw5Ny1ObCXeIEqbh8KrIa2Xp8SxmQlCV+8FPs4LA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/button@3.15.1': + resolution: {integrity: sha512-M1HtsKreJkigCnqceuIT22hDJBSStbPimnpmQmsl7SNyqCFY3+DHS7y/Sl3GvqCkzxF7j9UTL0dG38lGQ3K4xQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/calendar@3.8.3': + resolution: {integrity: sha512-fpH6WNXotzH0TlKHXXxtjeLZ7ko0sbyHmwDAwmDFyP7T0Iwn1YQZ+lhceLifvynlxuOgX6oBItyUKmkHQ0FouQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/checkbox@3.10.4': + resolution: {integrity: sha512-tYCG0Pd1usEz5hjvBEYcqcA0youx930Rss1QBIse9TgMekA1c2WmPDNupYV8phpO8Zuej3DL1WfBeXcgavK8aw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/color@3.1.4': + resolution: {integrity: sha512-s+Xj4pvNBlJPpQ1Gr7bO1j4/tuwMUfdS9xIVFuiW5RvDsSybKTUJ/gqPzTxms94VDCRhLFocVn2STNdD2Erf6A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/combobox@3.14.0': + resolution: {integrity: sha512-zmSSS7BcCOD8rGT8eGbVy7UlL5qq1vm88fFn4WgFe+lfK33ne+E7yTzTxcPY2TCGSo5fY6xMj3OG79FfVNGbSg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/datepicker@3.13.5': + resolution: {integrity: sha512-j28Vz+xvbb4bj7+9Xbpc4WTvSitlBvt7YEaEGM/8ZQ5g4Jr85H2KwkmDwjzmMN2r6VMQMMYq9JEcemq5wWpfUQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/dialog@3.5.24': + resolution: {integrity: sha512-NFurEP/zV0dA/41422lV1t+0oh6f/13n+VmLHZG8R13m1J3ql/kAXZ49zBSqkqANBO1ojyugWebk99IiR4pYOw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/form@3.7.18': + resolution: {integrity: sha512-0sBJW0+I9nJcF4SmKrYFEWAlehiebSTy7xqriqAXtqfTEdvzAYLGaAK2/7gx+wlNZeDTdW43CDRJ4XAhyhBqnw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/grid@3.3.8': + resolution: {integrity: sha512-zJvXH8gc1e1VH2H3LRnHH/W2HIkLkZMH3Cu5pLcj0vDuLBSWpcr3Ikh3jZ+VUOZF0G1Jt1lO8pKIaqFzDLNmLQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/link@3.6.7': + resolution: {integrity: sha512-1apXCFJgMC1uydc2KNENrps1qR642FqDpwlNWe254UTpRZn/hEZhA6ImVr8WhomfLJu672WyWA0rUOv4HT+/pQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/listbox@3.7.6': + resolution: {integrity: sha512-335NYElKEByXMalAmeRPyulKIDd2cjOCQhLwvv2BtxO5zaJfZnBbhZs+XPd9zwU6YomyOxODKSHrwbNDx+Jf3w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/menu@3.10.7': + resolution: {integrity: sha512-+p7ixZdvPDJZhisqdtWiiuJ9pteNfK5i19NB6wzAw5XkljbEzodNhwLv6rI96DY5XpbFso2kcjw7IWi+rAAGGQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/meter@3.4.15': + resolution: {integrity: sha512-9WjNphhLLM+TA4Ev1y2MkpugJ5JjTXseHh7ZWWx2veq5DrXMZYclkRpfUrUdLVKvaBIPQCgpQIj0TcQi+quR9A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/numberfield@3.8.18': + resolution: {integrity: sha512-nLzk7YAG9yAUtSv+9R8LgCHsu8hJq8/A+m1KsKxvc8WmNJjIujSFgWvT21MWBiUgPBzJKGzAqpMDDa087mltJQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/overlays@3.9.4': + resolution: {integrity: sha512-7Z9HaebMFyYBqtv3XVNHEmVkm7AiYviV7gv0c98elEN2Co+eQcKFGvwBM9Gy/lV57zlTqFX1EX/SAqkMEbCLOA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/progress@3.5.18': + resolution: {integrity: sha512-mKeQn+KrHr1y0/k7KtrbeDGDaERH6i4f6yBwj/ZtYDCTNKMO3tPHJY6nzF0w/KKZLplIO+BjUbHXc2RVm8ovwQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/radio@3.9.4': + resolution: {integrity: sha512-TkMRY3sA1PcFZhhclu4IUzUTIir6MzNJj8h6WT8vO6Nug2kXJ72qigugVFBWJSE472mltduOErEAo0rtAYWbQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/searchfield@3.6.8': + resolution: {integrity: sha512-M2p7OVdMTMDmlBcHd4N2uCBwg3uJSNM4lmEyf09YD44N5wDAI0yogk52QBwsnhpe+i2s65UwCYgunB+QltRX8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/select@3.12.2': + resolution: {integrity: sha512-AseOjfr3qM1W1qIWcbAe6NFpwZluVeQX/dmu9BYxjcnVvtoBLPMbE5zX/BPbv+N5eFYjoMyj7Ug9dqnI+LrlGw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/shared@3.33.1': + resolution: {integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/slider@3.8.4': + resolution: {integrity: sha512-C+xFVvfKREai9S/ekBDCVaGPOQYkNUAsQhjQnNsUAATaox4I6IYLmcIgLmljpMQWqAe+gZiWsIwacRYMez2Tew==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/switch@3.5.17': + resolution: {integrity: sha512-2GTPJvBCYI8YZ3oerHtXg+qikabIXCMJ6C2wcIJ5Xn0k9XOovowghfJi10OPB2GGyOiLBU74CczP5nx8adG90Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/table@3.13.6': + resolution: {integrity: sha512-eluL+iFfnVmFm7OSZrrFG9AUjw+tcv898zbv+NsZACa8oXG1v9AimhZfd+Mo8q/5+sX/9hguWNXFkSvmTjuVPQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/tabs@3.3.22': + resolution: {integrity: sha512-HGwLD9dA3k3AGfRKGFBhNgxU9/LyRmxN0kxVj1ghA4L9S/qTOzS6GhrGNkGzsGxyVLV4JN8MLxjWN2o9QHnLEg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/textfield@3.12.8': + resolution: {integrity: sha512-wt6FcuE5AyntxsnPika/h3nf/DPmeAVbI018L9o6h+B/IL4sMWWdx663wx2KOOeHH8ejKGZQNPLhUKs4s1mVQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/tooltip@3.5.2': + resolution: {integrity: sha512-FvSuZ2WP08NEWefrpCdBYpEEZh/5TvqvGjq0wqGzWg2OPwpc14HjD8aE7I3MOuylXkD4MSlMjl7J4DlvlcCs3Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.43.0': + resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.43.0': + resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.43.0': + resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.43.0': + resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.43.0': + resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.43.0': + resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.43.0': + resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.43.0': + resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.43.0': + resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.43.0': + resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.43.0': + resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': + resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.43.0': + resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.43.0': + resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.43.0': + resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} + cpu: [s390x] + os: [linux] '@rollup/rollup-linux-x64-gnu@4.43.0': resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} @@ -6931,6 +7626,9 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} + invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -8428,6 +9126,18 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-aria-components@1.16.0: + resolution: {integrity: sha512-MjHbTLpMFzzD2Tv5KbeXoZwPczuUWZcRavVvQQlNHRtXHH38D+sToMEYpNeir7Wh3K/XWtzeX3EujfJW6QNkrw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + react-aria@3.47.0: + resolution: {integrity: sha512-nvahimIqdByl/PXk/xPkG30LPRzcin+/Uk0uFfwbbKRRFC9aa22a6BRULZLqVHwa9GaNyKe6CDUxO1Dde4v0kA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-confetti@6.4.0: resolution: {integrity: sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==} engines: {node: '>=16'} @@ -8567,6 +9277,11 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-stately@3.45.0: + resolution: {integrity: sha512-G3bYr0BIiookpt4H05VeZUuVS/FslQAj2TeT8vDfCiL314Y+LtPXIPe/a3eamCA0wljy7z1EDYKV50Qbz7pcJg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -9166,9 +9881,22 @@ packages: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + tailwind-variants@3.2.2: + resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==} + engines: {node: '>=16.x', pnpm: '>=7.x'} + peerDependencies: + tailwind-merge: '>=3.0.0' + tailwindcss: '*' + peerDependenciesMeta: + tailwind-merge: + optional: true + tailwindcss@3.4.17: resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} engines: {node: '>=14.0.0'} @@ -9321,6 +10049,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + tween-functions@1.2.0: resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==} @@ -11325,10 +12056,36 @@ snapshots: '@floating-ui/utils@0.2.9': {} + '@formatjs/ecma402-abstract@2.3.6': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.2 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + '@formatjs/fast-memoize@3.1.0': dependencies: tslib: 2.8.1 + '@formatjs/icu-messageformat-parser@2.11.4': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.16': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + '@formatjs/intl-localematcher@0.8.1': dependencies: '@formatjs/fast-memoize': 3.1.0 @@ -11340,6 +12097,35 @@ snapshots: optionalDependencies: tailwindcss: 4.2.1 + '@heroui/react@3.0.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.2.1)': + dependencies: + '@heroui/styles': 3.0.1(tailwind-merge@3.4.0)(tailwindcss@4.2.1) + '@radix-ui/react-avatar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/color': 3.1.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + input-otp: 1.4.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-aria-components: 1.16.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + tailwind-merge: 3.4.0 + tailwind-variants: 3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.2.1) + tailwindcss: 4.2.1 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + '@heroui/styles@3.0.1(tailwind-merge@3.4.0)(tailwindcss@4.2.1)': + dependencies: + tailwind-variants: 3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.2.1) + tailwindcss: 4.2.1 + tw-animate-css: 1.4.0 + transitivePeerDependencies: + - tailwind-merge + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -11569,6 +12355,23 @@ snapshots: optionalDependencies: '@types/node': 22.15.32 + '@internationalized/date@3.12.0': + dependencies: + '@swc/helpers': 0.5.15 + + '@internationalized/message@3.1.8': + dependencies: + '@swc/helpers': 0.5.15 + intl-messageformat: 10.7.18 + + '@internationalized/number@3.6.5': + dependencies: + '@swc/helpers': 0.5.15 + + '@internationalized/string@3.2.7': + dependencies: + '@swc/helpers': 0.5.15 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -13626,25 +14429,660 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/react-visually-hidden@1.2.3(@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)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@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) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@radix-ui/rect@1.1.1': {} + + '@react-aria/autocomplete@3.0.0-rc.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/combobox': 3.15.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/listbox': 3.15.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/searchfield': 3.8.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/autocomplete': 3.0.0-beta.4(react@19.2.3) + '@react-stately/combobox': 3.13.0(react@19.2.3) + '@react-types/autocomplete': 3.0.0-alpha.38(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/breadcrumbs@3.5.32(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/link': 3.8.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/breadcrumbs': 3.7.19(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/button@3.14.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/toolbar': 3.0.0-beta.24(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/toggle': 3.9.5(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/calendar@3.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/calendar': 3.9.3(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/calendar': 3.8.3(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/checkbox@3.16.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/form': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/toggle': 3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/checkbox': 3.7.5(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/toggle': 3.9.5(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/collections@3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + + '@react-aria/color@3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/numberfield': 3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/slider': 3.8.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/spinbutton': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/visually-hidden': 3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/color': 3.9.5(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-types/color': 3.1.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/combobox@3.15.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/listbox': 3.15.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/menu': 3.21.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/combobox': 3.13.0(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/combobox': 3.14.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/datepicker@3.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@internationalized/number': 3.6.5 + '@internationalized/string': 3.2.7 + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/form': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/spinbutton': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/datepicker': 3.16.1(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/calendar': 3.8.3(react@19.2.3) + '@react-types/datepicker': 3.13.5(react@19.2.3) + '@react-types/dialog': 3.5.24(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/dialog@3.5.34(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/dialog': 3.5.24(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/disclosure@3.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/disclosure': 3.0.11(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/dnd@3.11.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@internationalized/string': 3.2.7 + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/dnd': 3.7.4(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/focus@3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/form@3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/grid@3.14.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/grid': 3.11.9(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/gridlist@3.14.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/grid': 3.14.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-stately/tree': 3.9.6(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/i18n@3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@internationalized/message': 3.1.8 + '@internationalized/number': 3.6.5 + '@internationalized/string': 3.2.7 + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/interactions@3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/label@3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/landmark@3.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + + '@react-aria/link@3.8.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/link': 3.6.7(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/listbox@3.15.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-types/listbox': 3.7.6(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/live-announcer@3.4.4': + dependencies: + '@swc/helpers': 0.5.15 + + '@react-aria/menu@3.21.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/menu': 3.9.11(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/tree': 3.9.6(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/menu': 3.10.7(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/meter@3.4.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/progress': 3.4.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/meter': 3.4.15(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/numberfield@3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/spinbutton': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/numberfield': 3.11.0(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/numberfield': 3.8.18(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/overlays@3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/visually-hidden': 3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/overlays': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/progress@3.4.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/progress': 3.5.18(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/radio@3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/form': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/radio': 3.11.5(react@19.2.3) + '@react-types/radio': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/searchfield@3.8.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/searchfield': 3.5.19(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/searchfield': 3.6.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/select@3.17.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/form': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/listbox': 3.15.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/menu': 3.21.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/visually-hidden': 3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/select': 3.9.2(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/select': 3.12.2(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/selection@3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/separator@3.4.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/slider@3.8.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/slider': 3.7.5(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/slider': 3.8.4(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/spinbutton@3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/ssr@3.9.10(react@19.2.3)': + dependencies: + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-aria/switch@3.7.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/toggle': 3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/toggle': 3.9.5(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/switch': 3.5.17(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/table@3.17.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/grid': 3.14.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/visually-hidden': 3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-stately/table': 3.15.4(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/table': 3.13.6(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/tabs@3.11.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/tabs': 3.8.9(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/tabs': 3.3.22(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/tag@3.8.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/gridlist': 3.14.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/textfield@3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/form': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/textfield': 3.12.8(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/toast@3.0.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/landmark': 3.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/toast': 3.1.3(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/toggle@3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/toggle': 3.9.5(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/toolbar@3.0.0-beta.24(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/tooltip@3.9.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/tooltip': 3.5.11(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/tooltip': 3.5.2(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/tree@3.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/gridlist': 3.14.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/tree': 3.9.6(react@19.2.3) + '@react-types/button': 3.15.1(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/utils@3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-visually-hidden@1.2.3(@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)': + '@react-aria/virtualizer@4.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@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) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/virtualizer': 4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - '@radix-ui/rect@1.1.1': {} + '@react-aria/visually-hidden@3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) '@react-email/body@0.0.11(react@19.2.3)': dependencies: @@ -14037,6 +15475,416 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@react-stately/autocomplete@3.0.0-beta.4(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/calendar@3.9.3(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/calendar': 3.8.3(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/checkbox@3.7.5(react@19.2.3)': + dependencies: + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/collections@3.12.10(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/color@3.9.5(react@19.2.3)': + dependencies: + '@internationalized/number': 3.6.5 + '@internationalized/string': 3.2.7 + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/numberfield': 3.11.0(react@19.2.3) + '@react-stately/slider': 3.7.5(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/color': 3.1.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/combobox@3.13.0(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/combobox': 3.14.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/data@3.15.2(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/datepicker@3.16.1(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@internationalized/number': 3.6.5 + '@internationalized/string': 3.2.7 + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/datepicker': 3.13.5(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/disclosure@3.0.11(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/dnd@3.7.4(react@19.2.3)': + dependencies: + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/flags@3.1.2': + dependencies: + '@swc/helpers': 0.5.15 + + '@react-stately/form@3.2.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/grid@3.11.9(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/layout@4.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/table': 3.15.4(react@19.2.3) + '@react-stately/virtualizer': 4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/table': 3.13.6(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-stately/list@3.13.4(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/menu@3.9.11(react@19.2.3)': + dependencies: + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-types/menu': 3.10.7(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/numberfield@3.11.0(react@19.2.3)': + dependencies: + '@internationalized/number': 3.6.5 + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/numberfield': 3.8.18(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/overlays@3.6.23(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/overlays': 3.9.4(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/radio@3.11.5(react@19.2.3)': + dependencies: + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/radio': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/searchfield@3.5.19(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/searchfield': 3.6.8(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/select@3.9.2(react@19.2.3)': + dependencies: + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/select': 3.12.2(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/selection@3.20.9(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/slider@3.7.5(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/slider': 3.8.4(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/table@3.15.4(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-stately/grid': 3.11.9(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/table': 3.13.6(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/tabs@3.8.9(react@19.2.3)': + dependencies: + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/tabs': 3.3.22(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/toast@3.1.3(react@19.2.3)': + dependencies: + '@swc/helpers': 0.5.15 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + + '@react-stately/toggle@3.9.5(react@19.2.3)': + dependencies: + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/checkbox': 3.10.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/tooltip@3.5.11(react@19.2.3)': + dependencies: + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-types/tooltip': 3.5.2(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/tree@3.9.6(react@19.2.3)': + dependencies: + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/utils@3.11.0(react@19.2.3)': + dependencies: + '@swc/helpers': 0.5.15 + react: 19.2.3 + + '@react-stately/virtualizer@4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-types/autocomplete@3.0.0-alpha.38(react@19.2.3)': + dependencies: + '@react-types/combobox': 3.14.0(react@19.2.3) + '@react-types/searchfield': 3.6.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/breadcrumbs@3.7.19(react@19.2.3)': + dependencies: + '@react-types/link': 3.6.7(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/button@3.15.1(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/calendar@3.8.3(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/checkbox@3.10.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/color@3.1.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/slider': 3.8.4(react@19.2.3) + react: 19.2.3 + + '@react-types/combobox@3.14.0(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/datepicker@3.13.5(react@19.2.3)': + dependencies: + '@internationalized/date': 3.12.0 + '@react-types/calendar': 3.8.3(react@19.2.3) + '@react-types/overlays': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/dialog@3.5.24(react@19.2.3)': + dependencies: + '@react-types/overlays': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/form@3.7.18(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/grid@3.3.8(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/link@3.6.7(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/listbox@3.7.6(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/menu@3.10.7(react@19.2.3)': + dependencies: + '@react-types/overlays': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/meter@3.4.15(react@19.2.3)': + dependencies: + '@react-types/progress': 3.5.18(react@19.2.3) + react: 19.2.3 + + '@react-types/numberfield@3.8.18(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/overlays@3.9.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/progress@3.5.18(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/radio@3.9.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/searchfield@3.6.8(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/textfield': 3.12.8(react@19.2.3) + react: 19.2.3 + + '@react-types/select@3.12.2(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/shared@3.33.1(react@19.2.3)': + dependencies: + react: 19.2.3 + + '@react-types/slider@3.8.4(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/switch@3.5.17(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/table@3.13.6(react@19.2.3)': + dependencies: + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/tabs@3.3.22(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/textfield@3.12.8(react@19.2.3)': + dependencies: + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + + '@react-types/tooltip@3.5.2(react@19.2.3)': + dependencies: + '@react-types/overlays': 3.9.4(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + '@rollup/pluginutils@5.2.0(rollup@4.43.0)': dependencies: '@types/estree': 1.0.8 @@ -15451,7 +17299,7 @@ snapshots: browserslist@4.25.0: dependencies: - caniuse-lite: 1.0.30001723 + caniuse-lite: 1.0.30001778 electron-to-chromium: 1.5.170 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.0) @@ -15848,8 +17696,7 @@ snapshots: decimal.js-light@2.5.1: {} - decimal.js@10.6.0: - optional: true + decimal.js@10.6.0: {} decode-named-character-reference@1.2.0: dependencies: @@ -16269,7 +18116,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16291,7 +18138,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -16816,7 +18663,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@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)(sass@1.89.2)): + fumadocs-mdx@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.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 @@ -17308,6 +19155,13 @@ snapshots: internmap@2.0.3: {} + intl-messageformat@10.7.18: + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + tslib: 2.8.1 + invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -19393,6 +21247,87 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-aria-components@1.16.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@internationalized/date': 3.12.0 + '@internationalized/string': 3.2.7 + '@react-aria/autocomplete': 3.0.0-rc.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/collections': 3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/dnd': 3.11.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/live-announcer': 3.4.4 + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/toolbar': 3.0.0-beta.24(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/virtualizer': 4.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/autocomplete': 3.0.0-beta.4(react@19.2.3) + '@react-stately/layout': 4.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/table': 3.15.4(react@19.2.3) + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-stately/virtualizer': 4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/form': 3.7.18(react@19.2.3) + '@react-types/grid': 3.3.8(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + '@react-types/table': 3.13.6(react@19.2.3) + '@swc/helpers': 0.5.15 + client-only: 0.0.1 + react: 19.2.3 + react-aria: 3.47.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + react-stately: 3.45.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + + react-aria@3.47.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@internationalized/string': 3.2.7 + '@react-aria/breadcrumbs': 3.5.32(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/button': 3.14.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/calendar': 3.9.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/checkbox': 3.16.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/color': 3.1.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/combobox': 3.15.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/datepicker': 3.16.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/dialog': 3.5.34(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/disclosure': 3.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/dnd': 3.11.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/focus': 3.21.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/gridlist': 3.14.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/i18n': 3.12.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/label': 3.7.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/landmark': 3.0.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/link': 3.8.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/listbox': 3.15.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/menu': 3.21.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/meter': 3.4.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/numberfield': 3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/overlays': 3.31.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/progress': 3.4.30(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/radio': 3.12.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/searchfield': 3.8.12(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/select': 3.17.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/selection': 3.27.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/separator': 3.4.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/slider': 3.8.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/switch': 3.7.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/table': 3.17.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/tabs': 3.11.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/tag': 3.8.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/textfield': 3.18.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/toast': 3.0.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/tooltip': 3.9.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/tree': 3.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.33.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/visually-hidden': 3.8.31(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-confetti@6.4.0(react@19.2.4): dependencies: react: 19.2.4 @@ -19665,6 +21600,36 @@ snapshots: react-dom: 19.2.4(react@19.2.4) react-transition-group: 4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react-stately@3.45.0(react@19.2.3): + dependencies: + '@react-stately/calendar': 3.9.3(react@19.2.3) + '@react-stately/checkbox': 3.7.5(react@19.2.3) + '@react-stately/collections': 3.12.10(react@19.2.3) + '@react-stately/color': 3.9.5(react@19.2.3) + '@react-stately/combobox': 3.13.0(react@19.2.3) + '@react-stately/data': 3.15.2(react@19.2.3) + '@react-stately/datepicker': 3.16.1(react@19.2.3) + '@react-stately/disclosure': 3.0.11(react@19.2.3) + '@react-stately/dnd': 3.7.4(react@19.2.3) + '@react-stately/form': 3.2.4(react@19.2.3) + '@react-stately/list': 3.13.4(react@19.2.3) + '@react-stately/menu': 3.9.11(react@19.2.3) + '@react-stately/numberfield': 3.11.0(react@19.2.3) + '@react-stately/overlays': 3.6.23(react@19.2.3) + '@react-stately/radio': 3.11.5(react@19.2.3) + '@react-stately/searchfield': 3.5.19(react@19.2.3) + '@react-stately/select': 3.9.2(react@19.2.3) + '@react-stately/selection': 3.20.9(react@19.2.3) + '@react-stately/slider': 3.7.5(react@19.2.3) + '@react-stately/table': 3.15.4(react@19.2.3) + '@react-stately/tabs': 3.8.9(react@19.2.3) + '@react-stately/toast': 3.1.3(react@19.2.3) + '@react-stately/toggle': 3.9.5(react@19.2.3) + '@react-stately/tooltip': 3.5.11(react@19.2.3) + '@react-stately/tree': 3.9.6(react@19.2.3) + '@react-types/shared': 3.33.1(react@19.2.3) + react: 19.2.3 + react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.3): dependencies: get-nonce: 1.0.1 @@ -20502,8 +22467,16 @@ snapshots: dependencies: '@pkgr/core': 0.2.9 + tailwind-merge@3.4.0: {} + tailwind-merge@3.5.0: {} + tailwind-variants@3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.2.1): + dependencies: + tailwindcss: 4.2.1 + optionalDependencies: + tailwind-merge: 3.4.0 + tailwindcss@3.4.17: dependencies: '@alloc/quick-lru': 5.2.0 @@ -20665,6 +22638,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tw-animate-css@1.4.0: {} + tween-functions@1.2.0: {} type-check@0.4.0: From 8a03e96753c0e33b2278e8d1a221c9ab78cda743 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Sun, 22 Mar 2026 21:57:41 +0530 Subject: [PATCH 02/14] Add all display components --- .../src/generated/system-prompt.txt | 88 +++++++++++++- .../lib/heroui-genui/components/accordion.tsx | 100 ++++++++++++++++ .../lib/heroui-genui/components/button.tsx | 10 +- .../lib/heroui-genui/components/buttons.tsx | 31 ++++- .../lib/heroui-genui/components/callout.tsx | 36 ++++++ .../lib/heroui-genui/components/follow-up.tsx | 74 ++++++++++++ .../components/markdown-renderer.tsx | 2 +- .../lib/heroui-genui/components/separator.tsx | 19 +++ .../src/lib/heroui-genui/components/table.tsx | 88 ++++++++++++++ .../src/lib/heroui-genui/components/tabs.tsx | 96 +++++++++++++++ .../src/lib/heroui-genui/components/tag.tsx | 54 +++++++++ .../heroui-genui/components/text-content.tsx | 7 +- .../src/lib/heroui-genui/index.tsx | 111 +++++++++++++++++- .../src/lib/heroui-genui/unions.ts | 13 +- 14 files changed, 705 insertions(+), 24 deletions(-) create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/callout.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/follow-up.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/separator.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/table.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx diff --git a/examples/heroui-chat/src/generated/system-prompt.txt b/examples/heroui-chat/src/generated/system-prompt.txt index cc0d22f12..5a13de5d7 100644 --- a/examples/heroui-chat/src/generated/system-prompt.txt +++ b/examples/heroui-chat/src/generated/system-prompt.txt @@ -19,15 +19,33 @@ Arguments marked with ? are optional. Sub-components can be inline or referenced The `action` prop type accepts: ContinueConversation (sends message to LLM), OpenUrl (navigates to URL), or Custom (app-defined). ### Content -TextContent(text: string, size?: "small" | "default" | "large" | "small-heavy" | "large-heavy") — Plain text block. size: "small" | "default" | "large" | "small-heavy" | "large-heavy". +TextContent(text: string, size?: "small" | "default" | "large" | "small-heavy" | "large-heavy") — Plain text block MarkDownRenderer(textMarkdown: string, variant?: "clear" | "card" | "sunk") — Renders markdown text with optional container variant +Callout(status: "default" | "accent" | "warning" | "danger" | "success", title: string, description: string) — Callout banner with status, title, and description +Separator(orientation?: "horizontal" | "vertical", decorative?: boolean) — Visual divider between content sections ### Buttons Button(label: string, action?: {type: "open_url", url: string} | {type: "continue_conversation", context?: string}, variant?: "primary" | "secondary" | "tertiary" | "outline" | "ghost" | "danger", size?: "sm" | "md" | "lg", fullWidth?: boolean) — Clickable button -Buttons(buttons: Button[]) — Group of Button components rendered together with consistent spacing. +Buttons(buttons: Button[], grouped?: boolean) — Row of Button components + +### Data +Col(label: string, type?: "string" | "number" | "action") — Column definition +Table(columns: Col[], rows: (string | number | boolean)[][]) — Data table +Tag(text: string, icon?: string, size?: "sm" | "md" | "lg", variant?: "neutral" | "info" | "success" | "warning" | "danger") — Styled tag/badge +TagBlock(tags: string[]) — Group of plain string tags + +### Layout +TabItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons)[]) — Single tab within a Tabs container +Tabs(items: TabItem[]) — Tabbed container +AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons)[]) — Single section within an Accordion +Accordion(items: AccordionItem[]) — Collapsible sections + +### Chat +FollowUpItem(text: string) — Clickable follow-up suggestion — when clicked, sends text as user message +FollowUpBlock(items: FollowUpItem[]) — List of clickable follow-up suggestions placed at the end of a response ### Ungrouped -Card(children: (TextContent | MarkDownRenderer | Buttons)[]) — Vertical container for all content in a chat response. Children stack top to bottom automatically. +Card(children: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons | FollowUpBlock | Tabs | Accordion)[]) — Vertical container for all content in a chat response. Children stack top to bottom automatically. ## Hoisting & Streaming (CRITICAL) @@ -41,6 +59,66 @@ During streaming, the output is re-parsed on every chunk. Undefined references a 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 — Simple text response: +root = Card([text]) +text = TextContent("Here is the information you requested.", "default") + +Example 2 — Markdown with formatting: +root = Card([md]) +md = MarkDownRenderer("## Summary\n\nHere are the key points:\n\n- **First** item\n- **Second** item\n- [Learn more](https://example.com)") + +Example 3 — Text with action buttons: +root = Card([text, btns]) +text = TextContent("Select one of the actions below to continue.") +btns = Buttons([b1, b2]) +b1 = Button("Continue", { type: "continue_conversation" }, "primary") +b2 = Button("Cancel", { type: "continue_conversation" }, "outline") + +Example 4 — Card header with callout: +root = Card([hdr, callout]) +hdr = CardHeader("Important Update", "Please read carefully") +callout = Callout("accent", "New feature", "We've added **dark mode** support.") + +Example 5 — Data table: +root = Card([title, tbl]) +title = TextContent("Sales Report", "large-heavy") +tbl = Table([c1, c2, c3], [["Widget A", 150, true], ["Widget B", 230, false]]) +c1 = Col("Product") +c2 = Col("Units", "number") +c3 = Col("In Stock") + +Example 6 — Tabs: +root = Card([tabs]) +tabs = Tabs([t1, t2]) +t1 = TabItem("overview", "Overview", [md1]) +t2 = TabItem("details", "Details", [md2]) +md1 = MarkDownRenderer("## Overview\nHigh-level summary.") +md2 = MarkDownRenderer("## Details\nDetailed breakdown.") + +Example 7 — Accordion: +root = Card([acc]) +acc = Accordion([a1, a2]) +a1 = AccordionItem("faq1", "What is OpenUI?", [text1]) +a2 = AccordionItem("faq2", "How does it work?", [text2]) +text1 = TextContent("OpenUI is a generative UI framework.") +text2 = TextContent("It streams structured component trees from an LLM.") + +Example 8 — Follow-up suggestions: +root = Card([text, followups]) +text = TextContent("Here's what I found. Want to explore more?") +followups = FollowUpBlock([f1, f2, f3]) +f1 = FollowUpItem("Tell me more about pricing") +f2 = FollowUpItem("Show me alternatives") +f3 = FollowUpItem("Compare features") + +Example 9 — Tags: +root = Card([title, tags]) +title = TextContent("Categories", "large-heavy") +tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"]) + ## Important Rules - ALWAYS start with root = Card(...) - Write statements in TOP-DOWN order: root → components → data (leverages hoisting for progressive streaming) @@ -52,3 +130,7 @@ Always write the root = Card(...) statement first so the UI shell appears immedi - Every response is a single Card(children) — children stack vertically automatically. - Card is the only layout container. +- Use TextContent for titles and short plain text. Use MarkDownRenderer for formatted content with links, bold, lists, tables, code. +- Callout description supports markdown. +- Table columns are defined with Col, rows are a 2D array of primitives. +- FollowUpBlock items become new user messages on click. diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx new file mode 100644 index 000000000..126047218 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { Accordion as HeroUIAccordion } from "@heroui/react"; +import { type ComponentRenderProps, defineComponent } from "@openuidev/react-lang"; +import { useEffect, useRef, useState } 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 AccordionItem = defineComponent({ + name: "AccordionItem", + props: AccordionItemSchema, + description: "Single section within an Accordion", + component: () => null, +}); + +const AccordionSchema = z.object({ + items: z.array(AccordionItem.ref), +}); + +function AccordionRenderer({ + props, + renderNode, +}: ComponentRenderProps>) { + const items = props.items ?? []; + const [expandedKeys, setExpandedKeys] = useState(new Set()); + const userHasInteracted = useRef(false); + const prevContentSizes = useRef>({}); + + useEffect(() => { + const first = items[0]; + if (items.length && expandedKeys.size === 0 && first) { + setExpandedKeys(new Set([first.props.value])); + } + }, [items.length, expandedKeys.size]); + + useEffect(() => { + if (userHasInteracted.current) return; + + let candidate: string | null = null; + const nextSizes: Record = {}; + + for (const item of items) { + const size = JSON.stringify(item.props.content).length; + const prevSize = prevContentSizes.current[item.props.value] ?? 0; + nextSizes[item.props.value] = size; + if (size > prevSize) { + candidate = item.props.value; + } + } + + prevContentSizes.current = nextSizes; + + if (candidate && !expandedKeys.has(candidate)) { + setExpandedKeys(new Set([candidate])); + } + }); + + if (!items.length) return null; + + return ( + { + userHasInteracted.current = true; + setExpandedKeys(keys); + }} + > + {items.map((item) => ( + + + + {item.props.trigger} + + + + + +
{renderNode(item.props.content)}
+
+
+
+ ))} +
+ ); +} + +export const Accordion = defineComponent({ + name: "Accordion", + props: AccordionSchema, + description: "Collapsible sections", + component: AccordionRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx index f5d089b35..987045c1b 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/button.tsx @@ -11,7 +11,7 @@ import { useTriggerAction, } from "@openuidev/react-lang"; import { z } from "zod"; -import { actionSchema, type ActionSchema } from "../action"; +import { actionSchema } from "../action"; const ButtonSchema = z.object({ label: z.string(), @@ -28,7 +28,7 @@ function ButtonRenderer({ props }: ComponentRenderProps })?.params; + ? { url: action.url } + : action?.type === BuiltinActionType.ContinueConversation + ? { context: action.context } + : undefined; triggerAction(label, formName, { type: actionType, params: actionParams }); }} > diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx index 24db5c757..ab266b658 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/buttons.tsx @@ -2,9 +2,20 @@ import { ButtonGroup } from "@heroui/react"; import { defineComponent } from "@openuidev/react-lang"; +import { type ReactNode } from "react"; import { z } from "zod"; import { Button } from "./button"; +function GroupSlot({ + __button_group_child, + children, +}: { + __button_group_child?: boolean; + children: ReactNode; +}) { + return <>{children}; +} + const ButtonsSchema = z.object({ buttons: z.array(Button.ref), grouped: z.boolean().optional(), @@ -13,20 +24,30 @@ const ButtonsSchema = z.object({ export const Buttons = defineComponent({ name: "Buttons", props: ButtonsSchema, - description: - 'Row of Button components. grouped: true joins buttons into a connected group with a separator between them.', + description: "Row of Button components", component: ({ props, renderNode }) => { + const buttons = props.buttons ?? []; if (props.grouped) { return ( - - {renderNode(props.buttons)} + {buttons.flatMap((btn, i) => { + const els = []; + if (i > 0) { + els.push( + + + , + ); + } + els.push({renderNode(btn)}); + return els; + })} ); } return (
- {renderNode(props.buttons)} + {renderNode(buttons)}
); }, diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/callout.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/callout.tsx new file mode 100644 index 000000000..d220d38d1 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/callout.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { Alert } from "@heroui/react"; +import { defineComponent } from "@openuidev/react-lang"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import { z } from "zod"; + +const CalloutSchema = z.object({ + status: z.enum(["default", "accent", "warning", "danger", "success"]), + title: z.string(), + description: z.string(), +}); + +export const Callout = defineComponent({ + name: "Callout", + props: CalloutSchema, + description: "Callout banner with status, title, and description", + component: ({ props }) => { + return ( + + + + {props.title} + +
+ + {String(props.description ?? "")} + +
+
+
+
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/follow-up.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/follow-up.tsx new file mode 100644 index 000000000..32cba02fc --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/follow-up.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { Button, ButtonGroup } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + useTriggerAction, +} from "@openuidev/react-lang"; +import { CornerDownRight } from "lucide-react"; +import { z } from "zod"; + +export const FollowUpItem = defineComponent({ + name: "FollowUpItem", + props: z.object({ + text: z.string(), + }), + description: "Clickable follow-up suggestion — when clicked, sends text as user message", + component: () => null, +}); + +const FollowUpBlockSchema = z.object({ + items: z.array(FollowUpItem.ref), +}); + +function FollowUpBlockRenderer({ + props, +}: ComponentRenderProps>) { + const triggerAction = useTriggerAction(); + const items = props.items ?? []; + + if (items.length === 0) { + return null; + } + + return ( +
+

+ Follow Ups +

+ + {items.flatMap((item, i) => { + const text = String(item?.props?.text ?? ""); + const button = ( + + ); + if (i === 0) return [button]; + return [ + , + button, + ]; + })} + +
+ ); +} + +export const FollowUpBlock = defineComponent({ + name: "FollowUpBlock", + props: FollowUpBlockSchema, + description: "List of clickable follow-up suggestions placed at the end of a response", + component: FollowUpBlockRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx index 2422205ca..6a6218fdd 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/markdown-renderer.tsx @@ -16,7 +16,7 @@ export const MarkDownRenderer = defineComponent({ description: "Renders markdown text with optional container variant", component: ({ props }) => { const text = props.textMarkdown == null ? "" : String(props.textMarkdown); - const variant = (props.variant as string) ?? "clear"; + const variant = props.variant ?? "clear"; const variantClass = variant === "card" ? "bg-content2 rounded-lg p-4" diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/separator.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/separator.tsx new file mode 100644 index 000000000..32fdab9b7 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/separator.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { Separator as HeroUISeparator } from "@heroui/react"; +import { defineComponent } from "@openuidev/react-lang"; +import { z } from "zod"; + +const SeparatorSchema = z.object({ + orientation: z.enum(["horizontal", "vertical"]).optional(), + decorative: z.boolean().optional(), +}); + +export const Separator = defineComponent({ + name: "Separator", + props: SeparatorSchema, + description: "Visual divider between content sections", + component: ({ props }) => ( + + ), +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/table.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/table.tsx new file mode 100644 index 000000000..81657725a --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/table.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { Table as HeroUITable } from "@heroui/react"; +import { defineComponent } from "@openuidev/react-lang"; +import type { ReactNode } from "react"; +import { z } from "zod"; + +const ColSchema = z.object({ + label: z.string(), + type: z.enum(["string", "number", "action"]).optional(), +}); + +export const Col = defineComponent({ + name: "Col", + props: ColSchema, + description: "Column definition", + component: () => null, +}); + +/** + * HeroUI / React Aria Table requires each body row to have exactly as many cells as + * columns. While streaming, rows often arrive short (or over-long); normalize so the + * collection never throws. Also avoids passing arbitrary objects to renderNode. + */ +function normalizeRowCells(row: unknown, columnCount: number): unknown[] { + const raw = Array.isArray(row) ? row : row == null ? [] : [row]; + if (raw.length >= columnCount) { + return raw.slice(0, columnCount); + } + return [...raw, ...Array(columnCount - raw.length).fill("")]; +} + +function renderPrimitiveCell(cell: unknown, renderNode: (value: unknown) => ReactNode): ReactNode { + if (cell == null) return ""; + if (typeof cell === "string" || typeof cell === "number" || typeof cell === "boolean") { + return String(cell); + } + if (typeof cell === "object" && (cell as { type?: string }).type === "element") { + return renderNode(cell); + } + return ""; +} + +export const Table = defineComponent({ + name: "Table", + props: z.object({ + columns: z.array(Col.ref), + rows: z.array(z.array(z.union([z.string(), z.number(), z.boolean()]))), + }), + description: "Data table", + component: ({ props, renderNode }) => { + const columns = props.columns ?? []; + const rows = props.rows ?? []; + const columnCount = columns.length; + + if (!columnCount) return null; + + return ( + + + + + {columns.map((c, i) => ( + + {c.props.label} + + ))} + + + {rows.map((row, ri) => { + const cells = normalizeRowCells(row, columnCount); + return ( + + {cells.map((cell, ci) => ( + + {renderPrimitiveCell(cell, renderNode)} + + ))} + + ); + })} + + + + + ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx new file mode 100644 index 000000000..56c17ea69 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Tabs as HeroUITabs } from "@heroui/react"; +import { type ComponentRenderProps, defineComponent } from "@openuidev/react-lang"; +import { useEffect, useRef, useState } 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: "Single tab within a Tabs container", + component: () => null, +}); + +const TabsSchema = z.object({ + items: z.array(TabItem.ref), +}); + +function TabsRenderer({ props, renderNode }: ComponentRenderProps>) { + const items = props.items ?? []; + const [activeTab, setActiveTab] = useState(""); + const userHasInteracted = useRef(false); + const prevContentSizes = useRef>({}); + + useEffect(() => { + const first = items[0]; + if (items.length && !activeTab && first) { + setActiveTab(first.props.value); + } + }, [items.length, activeTab]); + + useEffect(() => { + if (userHasInteracted.current) return; + + let candidate: string | null = null; + const nextSizes: Record = {}; + + for (const item of items) { + const size = JSON.stringify(item.props.content).length; + const prevSize = prevContentSizes.current[item.props.value] ?? 0; + nextSizes[item.props.value] = size; + if (size > prevSize) { + candidate = item.props.value; + } + } + + prevContentSizes.current = nextSizes; + + if (candidate && candidate !== activeTab) { + setActiveTab(candidate); + } + }); + + if (!items.length) return null; + + return ( + { + userHasInteracted.current = true; + setActiveTab(String(key)); + }} + > + + + {items.map((item) => ( + + {item.props.trigger} + + + ))} + + + {items.map((item) => ( + +
{renderNode(item.props.content)}
+
+ ))} +
+ ); +} + +export const Tabs = defineComponent({ + name: "Tabs", + props: TabsSchema, + description: "Tabbed container", + component: TabsRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx new file mode 100644 index 000000000..baba52e7e --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { Chip } from "@heroui/react"; +import { defineComponent } from "@openuidev/react-lang"; +import { z } from "zod"; + +const variantToColor = { + neutral: "default", + info: "accent", + success: "success", + warning: "warning", + danger: "danger", +} satisfies Record; + +const TagSchema = z.object({ + text: z.string(), + icon: z.string().optional(), + size: z.enum(["sm", "md", "lg"]).optional(), + variant: z.enum(["neutral", "info", "success", "warning", "danger"]).optional(), +}); + +export const Tag = defineComponent({ + name: "Tag", + props: TagSchema, + description: "Styled tag/badge", + component: ({ props }) => { + const color = variantToColor[props.variant ?? "neutral"]; + return ( + + {props.text} + + ); + }, +}); + +const TagBlockSchema = z.object({ + tags: z.array(z.string()), +}); + +export const TagBlock = defineComponent({ + name: "TagBlock", + props: TagBlockSchema, + description: "Group of plain string tags", + component: ({ props }) => { + const tags = props.tags ?? []; + return ( +
+ {tags.map((tag, i) => ( + {tag} + ))} +
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx index f9bfe613d..2723f77cd 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx @@ -19,12 +19,11 @@ const sizeClasses: Record = { export const TextContent = defineComponent({ name: "TextContent", props: TextContentSchema, - description: - 'Plain text block. size: "small" | "default" | "large" | "small-heavy" | "large-heavy".', + description: "Plain text block", component: ({ props }) => { const text = props.text == null ? "" : String(props.text); - const size = (props.size as string) ?? "default"; - const cls = sizeClasses[size] ?? sizeClasses["default"]; + const size = props.size ?? "default"; + const cls = sizeClasses[size]; return

{text}

; }, }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/index.tsx b/examples/heroui-chat/src/lib/heroui-genui/index.tsx index a2359bcd2..8526603e3 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/index.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/index.tsx @@ -1,25 +1,33 @@ "use client"; -import { Card } from "@heroui/react"; import type { ComponentGroup, PromptOptions } from "@openuidev/react-lang"; import { createLibrary, defineComponent } from "@openuidev/react-lang"; import { z } from "zod"; +import { Accordion, AccordionItem } from "./components/accordion"; import { Button } from "./components/button"; import { Buttons } from "./components/buttons"; +import { Callout } from "./components/callout"; +import { FollowUpBlock, FollowUpItem } from "./components/follow-up"; import { MarkDownRenderer } from "./components/markdown-renderer"; +import { Separator } from "./components/separator"; +import { Col, Table } from "./components/table"; +import { Tag, TagBlock } from "./components/tag"; +import { TabItem, Tabs } from "./components/tabs"; import { TextContent } from "./components/text-content"; 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(ChatContentChildUnion), + 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)} +
{renderNode(props.children)}
), }); @@ -28,21 +36,94 @@ const ChatCard = defineComponent({ export const herouiComponentGroups: ComponentGroup[] = [ { name: "Content", - components: ["TextContent", "MarkDownRenderer"], + components: ["TextContent", "MarkDownRenderer", "Callout", "Separator"], }, { name: "Buttons", components: ["Button", "Buttons"], }, + { + name: "Data", + components: ["Col", "Table", "Tag", "TagBlock"], + }, + { + name: "Layout", + components: ["TabItem", "Tabs", "AccordionItem", "Accordion"], + }, + { + name: "Chat", + components: ["FollowUpItem", "FollowUpBlock"], + }, ]; // ── Prompt Options ── -export const herouiExamples: string[] = []; +export const herouiExamples: string[] = [ + `Example 1 — Simple text response: +root = Card([text]) +text = TextContent("Here is the information you requested.", "default")`, + + `Example 2 — Markdown with formatting: +root = Card([md]) +md = MarkDownRenderer("## Summary\\n\\nHere are the key points:\\n\\n- **First** item\\n- **Second** item\\n- [Learn more](https://example.com)")`, + + `Example 3 — Text with action buttons: +root = Card([text, btns]) +text = TextContent("Select one of the actions below to continue.") +btns = Buttons([b1, b2]) +b1 = Button("Continue", { type: "continue_conversation" }, "primary") +b2 = Button("Cancel", { type: "continue_conversation" }, "outline")`, + + `Example 4 — Card header with callout: +root = Card([hdr, callout]) +hdr = CardHeader("Important Update", "Please read carefully") +callout = Callout("accent", "New feature", "We've added **dark mode** support.")`, + + `Example 5 — Data table: +root = Card([title, tbl]) +title = TextContent("Sales Report", "large-heavy") +tbl = Table([c1, c2, c3], [["Widget A", 150, true], ["Widget B", 230, false]]) +c1 = Col("Product") +c2 = Col("Units", "number") +c3 = Col("In Stock")`, + + `Example 6 — Tabs: +root = Card([tabs]) +tabs = Tabs([t1, t2]) +t1 = TabItem("overview", "Overview", [md1]) +t2 = TabItem("details", "Details", [md2]) +md1 = MarkDownRenderer("## Overview\\nHigh-level summary.") +md2 = MarkDownRenderer("## Details\\nDetailed breakdown.")`, + + `Example 7 — Accordion: +root = Card([acc]) +acc = Accordion([a1, a2]) +a1 = AccordionItem("faq1", "What is OpenUI?", [text1]) +a2 = AccordionItem("faq2", "How does it work?", [text2]) +text1 = TextContent("OpenUI is a generative UI framework.") +text2 = TextContent("It streams structured component trees from an LLM.")`, + + `Example 8 — Follow-up suggestions: +root = Card([text, followups]) +text = TextContent("Here's what I found. Want to explore more?") +followups = FollowUpBlock([f1, f2, f3]) +f1 = FollowUpItem("Tell me more about pricing") +f2 = FollowUpItem("Show me alternatives") +f3 = FollowUpItem("Compare features")`, + + `Example 9 — Tags: +root = Card([title, tags]) +title = TextContent("Categories", "large-heavy") +tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"])`, +]; export const herouiAdditionalRules: string[] = [ "Every response is a single Card(children) — children stack vertically automatically.", "Card is the only layout container.", + "Use TextContent for titles and short plain text. Use MarkDownRenderer for formatted content with links, bold, lists, tables, code.", + "Callout description supports markdown.", + "Table columns are defined with Col, rows are a 2D array of primitives.", + "FollowUpBlock items become new user messages on click.", ]; export const herouiPromptOptions: PromptOptions = { @@ -55,5 +136,23 @@ export const herouiPromptOptions: PromptOptions = { export const herouiChatLibrary = createLibrary({ root: "Card", componentGroups: herouiComponentGroups, - components: [ChatCard, TextContent, MarkDownRenderer, Button, Buttons], + components: [ + ChatCard, + TextContent, + MarkDownRenderer, + Callout, + Separator, + Button, + Buttons, + Col, + Table, + Tag, + TagBlock, + TabItem, + Tabs, + AccordionItem, + Accordion, + FollowUpItem, + FollowUpBlock, + ], }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/unions.ts b/examples/heroui-chat/src/lib/heroui-genui/unions.ts index e150e8cc9..ba25f6fd0 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/unions.ts +++ b/examples/heroui-chat/src/lib/heroui-genui/unions.ts @@ -1,11 +1,22 @@ import { z } from "zod"; import { Buttons } from "./components/buttons"; +import { Callout } from "./components/callout"; +import { FollowUpBlock } from "./components/follow-up"; import { MarkDownRenderer } from "./components/markdown-renderer"; +import { Separator } from "./components/separator"; +import { Table } from "./components/table"; +import { TagBlock } from "./components/tag"; import { TextContent } from "./components/text-content"; -export const ChatContentChildUnion = z.union([ +export const ContentChildUnion = z.union([ TextContent.ref, MarkDownRenderer.ref, + Callout.ref, + Separator.ref, + Table.ref, + TagBlock.ref, Buttons.ref, ]); + +export const ChatContentChildUnion = z.union([...ContentChildUnion.options, FollowUpBlock.ref]); From 2bc584c77ff2f8775910d84313ae2045b233e506 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Mon, 23 Mar 2026 00:18:57 +0530 Subject: [PATCH 03/14] Add support for forms --- .../src/generated/system-prompt.txt | 32 ++++- .../lib/heroui-genui/components/accordion.tsx | 2 + .../components/checkbox-group.tsx | 127 ++++++++++++++++++ .../heroui-genui/components/form-control.tsx | 71 ++++++++++ .../src/lib/heroui-genui/components/form.tsx | 42 ++++++ .../src/lib/heroui-genui/components/input.tsx | 83 ++++++++++++ .../heroui-genui/components/number-field.tsx | 96 +++++++++++++ .../heroui-genui/components/radio-group.tsx | 105 +++++++++++++++ .../lib/heroui-genui/components/select.tsx | 99 ++++++++++++++ .../lib/heroui-genui/components/slider.tsx | 91 +++++++++++++ .../heroui-genui/components/switch-group.tsx | 93 +++++++++++++ .../src/lib/heroui-genui/components/tabs.tsx | 2 + .../lib/heroui-genui/components/textarea.tsx | 82 +++++++++++ .../src/lib/heroui-genui/index.tsx | 69 +++++++++- .../heroui-chat/src/lib/heroui-genui/rules.ts | 17 +++ 15 files changed, 1009 insertions(+), 2 deletions(-) create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/checkbox-group.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/form.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/input.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/number-field.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/radio-group.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/select.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/slider.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/textarea.tsx create mode 100644 examples/heroui-chat/src/lib/heroui-genui/rules.ts diff --git a/examples/heroui-chat/src/generated/system-prompt.txt b/examples/heroui-chat/src/generated/system-prompt.txt index 5a13de5d7..011602762 100644 --- a/examples/heroui-chat/src/generated/system-prompt.txt +++ b/examples/heroui-chat/src/generated/system-prompt.txt @@ -40,12 +40,26 @@ Tabs(items: TabItem[]) — Tabbed container AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons)[]) — Single section within an Accordion Accordion(items: AccordionItem[]) — Collapsible sections +### Forms +Form(name: string, buttons: Buttons, fields) — Form container with fields and explicit action buttons +FormControl(label: string, input: Input | TextArea | Select | Slider | CheckBoxGroup | RadioGroup | NumberField, hint?: string) — Field with label, input component, and optional hint text +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}) — Single-line text input +TextArea(name: string, placeholder?: string, rows?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Multiline text input +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 +SelectItem(value: string, label: string) — Option for Select +NumberField(name: string, label?: string, min?: number, max?: number, step?: number, defaultValue?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Numeric input with increment/decrement buttons +Slider(name: string, variant: "continuous" | "discrete", min: number, max: number, step?: number, defaultValue?: number[], label?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Numeric slider input +CheckBoxGroup(name: string, items: CheckBoxItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Checkbox group +CheckBoxItem(label: string, description: string, name: string, defaultChecked?: boolean) — Option for CheckBoxGroup +RadioGroup(name: string, items: RadioItem[], defaultValue?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}) — Radio button group +RadioItem(label: string, description: string, value: string) — Option for RadioGroup + ### Chat FollowUpItem(text: string) — Clickable follow-up suggestion — when clicked, sends text as user message FollowUpBlock(items: FollowUpItem[]) — List of clickable follow-up suggestions placed at the end of a response ### Ungrouped -Card(children: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons | FollowUpBlock | Tabs | Accordion)[]) — Vertical container for all content in a chat response. Children stack top to bottom automatically. +Card(children: (TextContent | MarkDownRenderer | Callout | Separator | Table | TagBlock | Buttons | FollowUpBlock | Tabs | Accordion | Form)[]) — Vertical container for all content in a chat response. Children stack top to bottom automatically. ## Hoisting & Streaming (CRITICAL) @@ -119,6 +133,19 @@ root = Card([title, tags]) title = TextContent("Categories", "large-heavy") tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"]) +Example 10 — Form with inputs: +root = Card([title, form]) +title = TextContent("Contact Us", "large-heavy") +form = Form("contact", btns, [fc1, fc2, fc3]) +fc1 = FormControl("Name", input1) +fc2 = FormControl("Email", input2, "We'll never share your email.") +fc3 = FormControl("Message", ta1) +input1 = Input("name", "Your name", "text", { required: true }) +input2 = Input("email", "you@example.com", "email", { required: true, email: true }) +ta1 = TextArea("message", "How can we help?", 4) +btns = Buttons([b1]) +b1 = Button("Submit", { type: "continue_conversation" }, "primary") + ## Important Rules - ALWAYS start with root = Card(...) - Write statements in TOP-DOWN order: root → components → data (leverages hoisting for progressive streaming) @@ -134,3 +161,6 @@ tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"]) - Callout description supports markdown. - Table columns are defined with Col, rows are a 2D array of primitives. - FollowUpBlock items become new user messages on click. +- For Form fields, define EACH FormControl as its own reference — do NOT inline all controls in one array. This allows progressive field-by-field streaming. +- NEVER nest Form inside Form — each Form should be a standalone container. +- Form requires explicit buttons. Always pass a Buttons(...) reference as the buttons argument. diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx index 126047218..fbdb709f0 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/accordion.tsx @@ -36,6 +36,7 @@ function AccordionRenderer({ useEffect(() => { const first = items[0]; if (items.length && expandedKeys.size === 0 && first) { + // eslint-disable-next-line react-hooks/set-state-in-effect setExpandedKeys(new Set([first.props.value])); } }, [items.length, expandedKeys.size]); @@ -58,6 +59,7 @@ function AccordionRenderer({ prevContentSizes.current = nextSizes; if (candidate && !expandedKeys.has(candidate)) { + // eslint-disable-next-line react-hooks/set-state-in-effect setExpandedKeys(new Set([candidate])); } }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/checkbox-group.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/checkbox-group.tsx new file mode 100644 index 000000000..1e1cfae3f --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/checkbox-group.tsx @@ -0,0 +1,127 @@ +"use client"; + +import { + Checkbox, + CheckboxGroup as HeroUICheckboxGroup, + Description, + Label, +} from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useCallback, useEffect, useMemo } from "react"; +import { z } from "zod"; +import { rulesSchema } from "../rules"; + +const CheckBoxItemSchema = z.object({ + label: z.string(), + description: z.string(), + name: z.string(), + defaultChecked: z.boolean().optional(), +}); + +export const CheckBoxItem = defineComponent({ + name: "CheckBoxItem", + props: CheckBoxItemSchema, + description: "Option for CheckBoxGroup", + component: () => null, +}); + +const CheckBoxGroupSchema = z.object({ + name: z.string(), + items: z.array(CheckBoxItem.ref), + rules: rulesSchema, +}); + +function CheckBoxGroupRenderer({ + props, +}: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => parseStructuredRules(props.rules), [props.rules]); + const items = props.items ?? []; + + const getAggregate = useCallback((): Record => { + const stored = getFieldValue(formName, fieldName) as Record | undefined; + const result: Record = {}; + for (const item of items) { + result[item.props.name] = stored?.[item.props.name] ?? item.props.defaultChecked ?? false; + } + return result; + }, [formName, fieldName, items, getFieldValue]); + + useEffect(() => { + if (!isStreaming && items.length > 0 && getFieldValue(formName, fieldName) == null) { + const initial: Record = {}; + for (const item of items) { + initial[item.props.name] = item.props.defaultChecked ?? false; + } + setFieldValue(formName, "CheckBoxGroup", fieldName, initial, false); + } + }, [isStreaming]); + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName)); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0]); + + if (!items.length) return null; + + const aggregate = getAggregate(); + const selectedValues = items + .filter((item) => aggregate[item.props.name]) + .map((item) => item.props.name); + + return ( + { + const newAggregate: Record = {}; + for (const item of items) { + newAggregate[item.props.name] = newValues.includes(item.props.name); + } + setFieldValue(formName, "CheckBoxGroup", fieldName, newAggregate, true); + if (rules.length > 0) { + formValidation?.validateField(fieldName, newAggregate, rules); + } + }} + isDisabled={isStreaming} + > + {items.map((item, i) => ( + + + + + + + {item.props.description && ( + {item.props.description} + )} + + + ))} + + ); +} + +export const CheckBoxGroup = defineComponent({ + name: "CheckBoxGroup", + props: CheckBoxGroupSchema, + description: "Checkbox group", + component: CheckBoxGroupRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx new file mode 100644 index 000000000..a7728d073 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { Description, FieldError, Label } from "@heroui/react"; +import { defineComponent, useFormValidation } from "@openuidev/react-lang"; +import { z } from "zod"; + +import { CheckBoxGroup } from "./checkbox-group"; +import { Input } from "./input"; +import { NumberField } from "./number-field"; +import { RadioGroup } from "./radio-group"; +import { Select } from "./select"; +import { Slider } from "./slider"; +// import { SwitchGroup } from "./switch-group"; // disabled: scroll/layout issue on long forms +import { TextArea } from "./textarea"; + +const FormControlSchema = z.object({ + label: z.string(), + input: z.union([ + Input.ref, + TextArea.ref, + Select.ref, + Slider.ref, + CheckBoxGroup.ref, + RadioGroup.ref, + // SwitchGroup.ref, // disabled: scroll/layout issue on long forms + NumberField.ref, + ]), + hint: z.string().optional(), +}); + +/** Extract field name from input element (Input, Select, etc. all have name in props) */ +function getFieldNameFromInput(input: unknown): string | undefined { + const obj = input as { type?: string; props?: { name?: string } } | undefined; + return obj?.type === "element" ? obj.props?.name : undefined; +} + +export const FormControl = defineComponent({ + name: "FormControl", + props: FormControlSchema, + description: "Field with label, input component, and optional hint text", + component: ({ props, renderNode }) => { + const formValidation = useFormValidation(); + const inputObj = props.input as { + type?: string; + props?: { name?: string; rules?: { required?: boolean } }; + }; + const fieldName = getFieldNameFromInput(props.input); + const error = fieldName ? formValidation?.errors[fieldName] : undefined; + const isRequired = + inputObj?.type === "element" && inputObj.props?.rules?.required === true; + + return ( +
+ + {renderNode(props.input)} + {error ? ( +

+ {error} +

+ ) : props.hint ? ( + + {props.hint} + + ) : null} +
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx new file mode 100644 index 000000000..7a029ae94 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx @@ -0,0 +1,42 @@ +"use client"; + +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", + component: ({ props, renderNode }) => { + const formValidation = useCreateFormValidation(); + const formName = props.name; + + return ( + + +
+ {renderNode(props.fields)} + {renderNode(props.buttons)} +
+
+
+ ); + }, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx new file mode 100644 index 000000000..af9191819 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx @@ -0,0 +1,83 @@ +"use client"; + +import { Input as HeroUIInput } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseRules, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useEffect, useMemo } 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, +}); + +function InputRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => { + const r = props.rules; + if (Array.isArray(r)) return parseRules(r); + return parseStructuredRules(r); + }, [props.rules]); + const savedValue = getFieldValue(formName, fieldName) ?? ""; + const error = formValidation?.errors[fieldName]; + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => + getFieldValue(formName, fieldName), + ); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0, formValidation, fieldName, formName]); + + return ( + { + setFieldValue(formName, "Input", fieldName, e.target.value, true); + }} + onFocus={() => formValidation?.clearFieldError(fieldName)} + onBlur={() => { + if (rules.length > 0) { + formValidation?.validateField( + fieldName, + getFieldValue(formName, fieldName), + rules, + ); + } + }} + disabled={isStreaming} + /> + ); +} + +export const Input = defineComponent({ + name: "Input", + props: InputSchema, + description: "Single-line text input", + component: InputRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/number-field.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/number-field.tsx new file mode 100644 index 000000000..f080944e4 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/number-field.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Label, NumberField as HeroUINumberField } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseRules, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetDefaultValue, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useEffect, useMemo } from "react"; +import { z } from "zod"; +import { rulesSchema } from "../rules"; + +const NumberFieldSchema = z.object({ + name: z.string(), + label: z.string().optional(), + min: z.number().optional(), + max: z.number().optional(), + step: z.number().optional(), + defaultValue: z.number().optional(), + rules: rulesSchema, +}); + +function NumberFieldRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => { + const r = props.rules; + if (Array.isArray(r)) return parseRules(r); + return parseStructuredRules(r); + }, [props.rules]); + const existingValue = getFieldValue(formName, fieldName); + const error = formValidation?.errors[fieldName]; + + useSetDefaultValue({ + formName, + componentType: "NumberField", + name: fieldName, + existingValue, + defaultValue: props.defaultValue, + }); + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => + getFieldValue(formName, fieldName), + ); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0, formValidation, fieldName, formName]); + + const value = existingValue ?? props.defaultValue; + + return ( + { + setFieldValue(formName, "NumberField", fieldName, val, true); + if (rules.length > 0) { + formValidation?.validateField(fieldName, val, rules); + } + }} + > + + + + + + + + ); +} + +export const NumberField = defineComponent({ + name: "NumberField", + props: NumberFieldSchema, + description: "Numeric input with increment/decrement buttons", + component: NumberFieldRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/radio-group.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/radio-group.tsx new file mode 100644 index 000000000..bc6252123 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/radio-group.tsx @@ -0,0 +1,105 @@ +"use client"; + +import { + Description, + Label, + Radio, + RadioGroup as HeroUIRadioGroup, +} from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useEffect, useMemo } from "react"; +import { z } from "zod"; +import { rulesSchema } from "../rules"; + +const RadioItemSchema = z.object({ + label: z.string(), + description: z.string(), + value: z.string(), +}); + +export const RadioItem = defineComponent({ + name: "RadioItem", + props: RadioItemSchema, + description: "Option for RadioGroup", + component: () => null, +}); + +const RadioGroupSchema = z.object({ + name: z.string(), + items: z.array(RadioItem.ref), + defaultValue: z.string().optional(), + rules: rulesSchema, +}); + +function RadioGroupRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => parseStructuredRules(props.rules), [props.rules]); + const items = props.items ?? []; + const value = (getFieldValue(formName, fieldName) ?? props.defaultValue) as string | undefined; + + useEffect(() => { + if (!isStreaming && props.defaultValue != null && getFieldValue(formName, fieldName) == null) { + setFieldValue(formName, "RadioGroup", fieldName, props.defaultValue, false); + } + }, [isStreaming]); + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName)); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0]); + + if (!items.length) return null; + + return ( + { + setFieldValue(formName, "RadioGroup", fieldName, val, true); + if (rules.length > 0) { + formValidation?.validateField(fieldName, val, rules); + } + }} + isDisabled={isStreaming} + > + {items.map((item, i) => ( + + + + + + + {item.props.description && ( + {item.props.description} + )} + + + ))} + + ); +} + +export const RadioGroup = defineComponent({ + name: "RadioGroup", + props: RadioGroupSchema, + description: "Radio button group", + component: RadioGroupRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/select.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/select.tsx new file mode 100644 index 000000000..2a15ca094 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/select.tsx @@ -0,0 +1,99 @@ +"use client"; + +import { ListBox, Select as HeroUISelect } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useEffect, useMemo } 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", + component: () => null, +}); + +const SelectSchema = z.object({ + name: z.string(), + items: z.array(SelectItem.ref), + placeholder: z.string().optional(), + rules: rulesSchema, +}); + +function SelectRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => parseStructuredRules(props.rules), [props.rules]); + const items = (props.items ?? []).filter((item) => item.props.value); + const value = getFieldValue(formName, fieldName); + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName)); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0]); + + return ( + { + const val = String(key); + setFieldValue(formName, "Select", fieldName, val, true); + if (rules.length > 0) { + formValidation?.validateField(fieldName, val, rules); + } + }} + isDisabled={isStreaming} + > + + + + + + + {items.map((item) => ( + + {item.props.label || item.props.value} + + ))} + + + + ); +} + +export const Select = defineComponent({ + name: "Select", + props: SelectSchema, + description: "Dropdown select", + component: SelectRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/slider.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/slider.tsx new file mode 100644 index 000000000..4f414564f --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/slider.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { Label, Slider as HeroUISlider } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + parseStructuredRules, + useFormName, + useFormValidation, + useGetFieldValue, + useIsStreaming, + useSetDefaultValue, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useEffect, useMemo } from "react"; +import { z } from "zod"; +import { rulesSchema } from "../rules"; + +const SliderSchema = z.object({ + name: z.string(), + variant: z.enum(["continuous", "discrete"]), + min: z.number(), + max: z.number(), + step: z.number().optional(), + defaultValue: z.array(z.number()).optional(), + label: z.string().optional(), + rules: rulesSchema, +}); + +function SliderRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => parseStructuredRules(props.rules), [props.rules]); + const existingValue = getFieldValue(formName, fieldName); + const defaultVal = props.defaultValue; + + useSetDefaultValue({ + formName, + componentType: "Slider", + name: fieldName, + existingValue, + defaultValue: defaultVal, + }); + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => getFieldValue(formName, fieldName)); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0]); + + const value = existingValue ?? defaultVal; + const step = props.variant === "discrete" ? (props.step ?? 1) : props.step; + + return ( + { + const normalized = Array.isArray(vals) ? vals : [vals]; + setFieldValue(formName, "Slider", fieldName, normalized, true); + if (rules.length > 0) { + formValidation?.validateField(fieldName, normalized[0], rules); + } + }} + > + + + + + + + + ); +} + +export const Slider = defineComponent({ + name: "Slider", + props: SliderSchema, + description: "Numeric slider input", + component: SliderRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx new file mode 100644 index 000000000..f7b89f8e7 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { + Description, + Label, + Switch, + SwitchGroup as HeroUISwitchGroup, +} from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + useFormName, + useGetFieldValue, + useIsStreaming, + useSetFieldValue, +} from "@openuidev/react-lang"; +import { useCallback } from "react"; +import { z } from "zod"; + +const SwitchItemSchema = z.object({ + label: z.string().optional(), + description: z.string().optional(), + name: z.string(), + defaultChecked: z.boolean().optional(), +}); + +export const SwitchItem = defineComponent({ + name: "SwitchItem", + props: SwitchItemSchema, + description: "Individual switch toggle", + component: () => null, +}); + +const SwitchGroupSchema = z.object({ + name: z.string(), + items: z.array(SwitchItem.ref), +}); + +function SwitchGroupRenderer({ props }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + + const fieldName = props.name; + const items = props.items ?? []; + + const getAggregate = useCallback((): Record => { + const stored = getFieldValue(formName, fieldName) as Record | undefined; + const result: Record = {}; + for (const item of items) { + result[item.props.name] = stored?.[item.props.name] ?? item.props.defaultChecked ?? false; + } + return result; + }, [formName, fieldName, items, getFieldValue]); + + if (!items.length) return null; + + const aggregate = getAggregate(); + + return ( + + {items.map((item) => ( + { + const newAggregate = { ...getAggregate(), [item.props.name]: isSelected }; + setFieldValue(formName, "SwitchGroup", fieldName, newAggregate, true); + }} + isDisabled={isStreaming} + > + + + + + {item.props.label && } + {item.props.description && ( + {item.props.description} + )} + + + ))} + + ); +} + +export const SwitchGroup = defineComponent({ + name: "SwitchGroup", + props: SwitchGroupSchema, + description: "Group of switch toggles", + component: SwitchGroupRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx index 56c17ea69..e2e91d4a0 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/tabs.tsx @@ -33,6 +33,7 @@ function TabsRenderer({ props, renderNode }: ComponentRenderProps { const first = items[0]; if (items.length && !activeTab && first) { + // eslint-disable-next-line react-hooks/set-state-in-effect setActiveTab(first.props.value); } }, [items.length, activeTab]); @@ -55,6 +56,7 @@ function TabsRenderer({ props, renderNode }: ComponentRenderProps>) { + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); + const setFieldValue = useSetFieldValue(); + const isStreaming = useIsStreaming(); + const formValidation = useFormValidation(); + + const fieldName = props.name; + const rules = useMemo(() => { + const r = props.rules; + if (Array.isArray(r)) return parseRules(r); + return parseStructuredRules(r); + }, [props.rules]); + const savedValue = getFieldValue(formName, fieldName) ?? ""; + const error = formValidation?.errors[fieldName]; + + useEffect(() => { + if (!isStreaming && rules.length > 0 && formValidation) { + formValidation.registerField(fieldName, rules, () => + getFieldValue(formName, fieldName), + ); + return () => formValidation.unregisterField(fieldName); + } + return undefined; + }, [isStreaming, rules.length > 0, formValidation, fieldName, formName]); + + return ( + { + setFieldValue(formName, "TextArea", fieldName, e.target.value, true); + }} + onFocus={() => formValidation?.clearFieldError(fieldName)} + onBlur={() => { + if (rules.length > 0) { + formValidation?.validateField( + fieldName, + getFieldValue(formName, fieldName), + rules, + ); + } + }} + disabled={isStreaming} + /> + ); +} + +export const TextArea = defineComponent({ + name: "TextArea", + props: TextAreaSchema, + description: "Multiline text input", + component: TextAreaRenderer, +}); diff --git a/examples/heroui-chat/src/lib/heroui-genui/index.tsx b/examples/heroui-chat/src/lib/heroui-genui/index.tsx index 8526603e3..571fbbb45 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/index.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/index.tsx @@ -8,16 +8,34 @@ import { Accordion, AccordionItem } from "./components/accordion"; import { Button } from "./components/button"; import { Buttons } from "./components/buttons"; import { Callout } from "./components/callout"; +import { CheckBoxGroup, CheckBoxItem } from "./components/checkbox-group"; import { FollowUpBlock, FollowUpItem } from "./components/follow-up"; +import { Form } from "./components/form"; +import { FormControl } from "./components/form-control"; +import { Input } from "./components/input"; import { MarkDownRenderer } from "./components/markdown-renderer"; +import { NumberField } from "./components/number-field"; +import { RadioGroup, RadioItem } from "./components/radio-group"; +import { Select, SelectItem } from "./components/select"; import { Separator } from "./components/separator"; +import { Slider } from "./components/slider"; +// TODO: HeroUI's Switch causes scroll/layout breakage on long forms when toggled. +// The root cause is unclear — the same pattern works fine with Radix switches in +// @openuidev/react-ui. Disabled until the HeroUI v3 Switch interaction is resolved. +// import { SwitchGroup, SwitchItem } from "./components/switch-group"; import { Col, Table } from "./components/table"; import { Tag, TagBlock } from "./components/tag"; import { TabItem, Tabs } from "./components/tabs"; +import { TextArea } from "./components/textarea"; import { TextContent } from "./components/text-content"; import { ChatContentChildUnion } from "./unions"; -const ChatCardChildUnion = z.union([...ChatContentChildUnion.options, Tabs.ref, Accordion.ref]); +const ChatCardChildUnion = z.union([ + ...ChatContentChildUnion.options, + Tabs.ref, + Accordion.ref, + Form.ref, +]); const ChatCard = defineComponent({ name: "Card", @@ -50,6 +68,25 @@ export const herouiComponentGroups: ComponentGroup[] = [ name: "Layout", components: ["TabItem", "Tabs", "AccordionItem", "Accordion"], }, + { + name: "Forms", + components: [ + "Form", + "FormControl", + "Input", + "TextArea", + "Select", + "SelectItem", + "NumberField", + "Slider", + "CheckBoxGroup", + "CheckBoxItem", + "RadioGroup", + "RadioItem", + // "SwitchGroup", + // "SwitchItem", + ], + }, { name: "Chat", components: ["FollowUpItem", "FollowUpBlock"], @@ -115,6 +152,19 @@ f3 = FollowUpItem("Compare features")`, root = Card([title, tags]) title = TextContent("Categories", "large-heavy") tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"])`, + + `Example 10 — Form with inputs: +root = Card([title, form]) +title = TextContent("Contact Us", "large-heavy") +form = Form("contact", btns, [fc1, fc2, fc3]) +fc1 = FormControl("Name", input1) +fc2 = FormControl("Email", input2, "We'll never share your email.") +fc3 = FormControl("Message", ta1) +input1 = Input("name", "Your name", "text", { required: true }) +input2 = Input("email", "you@example.com", "email", { required: true, email: true }) +ta1 = TextArea("message", "How can we help?", 4) +btns = Buttons([b1]) +b1 = Button("Submit", { type: "continue_conversation" }, "primary")`, ]; export const herouiAdditionalRules: string[] = [ @@ -124,6 +174,9 @@ export const herouiAdditionalRules: string[] = [ "Callout description supports markdown.", "Table columns are defined with Col, rows are a 2D array of primitives.", "FollowUpBlock items become new user messages on click.", + "For Form fields, define EACH FormControl as its own reference — do NOT inline all controls in one array. This allows progressive field-by-field streaming.", + "NEVER nest Form inside Form — each Form should be a standalone container.", + "Form requires explicit buttons. Always pass a Buttons(...) reference as the buttons argument.", ]; export const herouiPromptOptions: PromptOptions = { @@ -152,6 +205,20 @@ export const herouiChatLibrary = createLibrary({ Tabs, AccordionItem, Accordion, + Form, + FormControl, + Input, + TextArea, + Select, + SelectItem, + NumberField, + Slider, + CheckBoxGroup, + CheckBoxItem, + RadioGroup, + RadioItem, + // SwitchGroup, + // SwitchItem, FollowUpItem, FollowUpBlock, ], diff --git a/examples/heroui-chat/src/lib/heroui-genui/rules.ts b/examples/heroui-chat/src/lib/heroui-genui/rules.ts new file mode 100644 index 000000000..cd77461f9 --- /dev/null +++ b/examples/heroui-chat/src/lib/heroui-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; From 55da8a6a4fe225123032bf0b942796d5121fd9d6 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Mon, 23 Mar 2026 12:05:53 +0530 Subject: [PATCH 04/14] Fix validation corner case in core --- .../react-lang/src/hooks/useFormValidation.ts | 11 ++++++++++- packages/react-lang/src/utils/validation.ts | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/react-lang/src/hooks/useFormValidation.ts b/packages/react-lang/src/hooks/useFormValidation.ts index 6b8f566b5..dfaea6708 100644 --- a/packages/react-lang/src/hooks/useFormValidation.ts +++ b/packages/react-lang/src/hooks/useFormValidation.ts @@ -53,7 +53,16 @@ export function useCreateFormValidation(): FormValidationContextValue { const newErrors: Record = {}; for (const [name, reg] of Object.entries(fieldsRef.current)) { - const value = reg.getValue(); + let value = reg.getValue(); + // Normalize: form state stores { value, componentType }; extract actual value if needed + if ( + value != null && + typeof value === "object" && + "value" in value && + "componentType" in value + ) { + value = (value as { value: unknown }).value; + } const error = validate(value, reg.rules); newErrors[name] = error; if (error) allValid = false; diff --git a/packages/react-lang/src/utils/validation.ts b/packages/react-lang/src/utils/validation.ts index 0902e9f79..c6dc710fc 100644 --- a/packages/react-lang/src/utils/validation.ts +++ b/packages/react-lang/src/utils/validation.ts @@ -30,6 +30,24 @@ export type ValidatorFn = (value: unknown, arg?: number | string) => string | un function isEmpty(value: unknown): boolean { if (value === null || value === undefined || value === "") return true; if (Array.isArray(value) && value.length === 0) return true; + // Form state stores { value, componentType } — extract actual value for validation + if ( + value && + typeof value === "object" && + !Array.isArray(value) && + "value" in value + ) { + return isEmpty((value as { value: unknown }).value); + } + // Empty plain object (e.g. CheckBoxGroup with no selection) + if ( + typeof value === "object" && + value !== null && + !Array.isArray(value) && + Object.keys(value).length === 0 + ) { + return true; + } return false; } From e2e7329c84c3f2400766fcae809c05e5feff79d1 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Mon, 23 Mar 2026 16:41:12 +0530 Subject: [PATCH 05/14] Add @tailwindcss/typography support and update example components - Added @tailwindcss/typography version 0.5.19 to dependencies in package.json files. - Updated globals.css to include the typography plugin. - Modified layout.tsx to reflect a new title and description. - Enhanced system-prompt.txt with updated content examples. - Refactored form and input components to improve validation handling and rendering. - Adjusted text-content component to support heading levels. - Updated various components to ensure consistent error handling and accessibility attributes. --- examples/heroui-chat/package.json | 1 + examples/heroui-chat/src/app/globals.css | 1 + examples/heroui-chat/src/app/layout.tsx | 4 +- .../src/generated/system-prompt.txt | 63 ++-------------- .../heroui-genui/components/form-control.tsx | 75 +++++++++++-------- .../src/lib/heroui-genui/components/form.tsx | 38 +++++----- .../src/lib/heroui-genui/components/input.tsx | 11 +-- .../heroui-genui/components/switch-group.tsx | 13 ++-- .../src/lib/heroui-genui/components/tag.tsx | 4 +- .../heroui-genui/components/text-content.tsx | 23 +++--- .../lib/heroui-genui/components/textarea.tsx | 9 +-- .../src/lib/heroui-genui/index.tsx | 61 +-------------- examples/openui-chat/package.json | 1 + pnpm-lock.yaml | 25 +++++++ 14 files changed, 128 insertions(+), 201 deletions(-) diff --git a/examples/heroui-chat/package.json b/examples/heroui-chat/package.json index 8b47fc8ad..e11507d95 100644 --- a/examples/heroui-chat/package.json +++ b/examples/heroui-chat/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@openuidev/cli": "workspace:*", "@tailwindcss/postcss": "^4", + "@tailwindcss/typography": "^0.5.19", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/examples/heroui-chat/src/app/globals.css b/examples/heroui-chat/src/app/globals.css index ed35a3374..a81949356 100644 --- a/examples/heroui-chat/src/app/globals.css +++ b/examples/heroui-chat/src/app/globals.css @@ -1,2 +1,3 @@ @import "tailwindcss"; +@plugin "@tailwindcss/typography"; @import "@heroui/styles"; diff --git a/examples/heroui-chat/src/app/layout.tsx b/examples/heroui-chat/src/app/layout.tsx index ee2918ae5..008d12d5a 100644 --- a/examples/heroui-chat/src/app/layout.tsx +++ b/examples/heroui-chat/src/app/layout.tsx @@ -3,8 +3,8 @@ import type { Metadata } from "next"; import "./globals.css"; export const metadata: Metadata = { - title: "OpenUI Chat", - description: "Generative UI Chat with OpenAI SDK", + title: "OpenUI + HeroUI Chat", + description: "Generative UI Chat with OpenAI SDK + HeroUI components", }; export default function RootLayout({ diff --git a/examples/heroui-chat/src/generated/system-prompt.txt b/examples/heroui-chat/src/generated/system-prompt.txt index 011602762..ab3fc2b6b 100644 --- a/examples/heroui-chat/src/generated/system-prompt.txt +++ b/examples/heroui-chat/src/generated/system-prompt.txt @@ -19,7 +19,7 @@ Arguments marked with ? are optional. Sub-components can be inline or referenced The `action` prop type accepts: ContinueConversation (sends message to LLM), OpenUrl (navigates to URL), or Custom (app-defined). ### Content -TextContent(text: string, size?: "small" | "default" | "large" | "small-heavy" | "large-heavy") — Plain text block +TextContent(text: string, level?: "h2" | "h3" | "h4") — Plain text block; optional level is a section heading, omit for body paragraph. MarkDownRenderer(textMarkdown: string, variant?: "clear" | "card" | "sunk") — Renders markdown text with optional container variant Callout(status: "default" | "accent" | "warning" | "danger" | "success", title: string, description: string) — Callout banner with status, title, and description Separator(orientation?: "horizontal" | "vertical", decorative?: boolean) — Visual divider between content sections @@ -76,66 +76,13 @@ Always write the root = Card(...) statement first so the UI shell appears immedi ## Examples -Example 1 — Simple text response: -root = Card([text]) -text = TextContent("Here is the information you requested.", "default") - -Example 2 — Markdown with formatting: +Example 1 — Markdown with formatting: root = Card([md]) md = MarkDownRenderer("## Summary\n\nHere are the key points:\n\n- **First** item\n- **Second** item\n- [Learn more](https://example.com)") -Example 3 — Text with action buttons: -root = Card([text, btns]) -text = TextContent("Select one of the actions below to continue.") -btns = Buttons([b1, b2]) -b1 = Button("Continue", { type: "continue_conversation" }, "primary") -b2 = Button("Cancel", { type: "continue_conversation" }, "outline") - -Example 4 — Card header with callout: -root = Card([hdr, callout]) -hdr = CardHeader("Important Update", "Please read carefully") -callout = Callout("accent", "New feature", "We've added **dark mode** support.") - -Example 5 — Data table: -root = Card([title, tbl]) -title = TextContent("Sales Report", "large-heavy") -tbl = Table([c1, c2, c3], [["Widget A", 150, true], ["Widget B", 230, false]]) -c1 = Col("Product") -c2 = Col("Units", "number") -c3 = Col("In Stock") - -Example 6 — Tabs: -root = Card([tabs]) -tabs = Tabs([t1, t2]) -t1 = TabItem("overview", "Overview", [md1]) -t2 = TabItem("details", "Details", [md2]) -md1 = MarkDownRenderer("## Overview\nHigh-level summary.") -md2 = MarkDownRenderer("## Details\nDetailed breakdown.") - -Example 7 — Accordion: -root = Card([acc]) -acc = Accordion([a1, a2]) -a1 = AccordionItem("faq1", "What is OpenUI?", [text1]) -a2 = AccordionItem("faq2", "How does it work?", [text2]) -text1 = TextContent("OpenUI is a generative UI framework.") -text2 = TextContent("It streams structured component trees from an LLM.") - -Example 8 — Follow-up suggestions: -root = Card([text, followups]) -text = TextContent("Here's what I found. Want to explore more?") -followups = FollowUpBlock([f1, f2, f3]) -f1 = FollowUpItem("Tell me more about pricing") -f2 = FollowUpItem("Show me alternatives") -f3 = FollowUpItem("Compare features") - -Example 9 — Tags: -root = Card([title, tags]) -title = TextContent("Categories", "large-heavy") -tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"]) - -Example 10 — Form with inputs: +Example 2 — Form with inputs: root = Card([title, form]) -title = TextContent("Contact Us", "large-heavy") +title = TextContent("Contact Us", "h2") form = Form("contact", btns, [fc1, fc2, fc3]) fc1 = FormControl("Name", input1) fc2 = FormControl("Email", input2, "We'll never share your email.") @@ -157,7 +104,7 @@ b1 = Button("Submit", { type: "continue_conversation" }, "primary") - Every response is a single Card(children) — children stack vertically automatically. - Card is the only layout container. -- Use TextContent for titles and short plain text. Use MarkDownRenderer for formatted content with links, bold, lists, tables, code. +- Use TextContent for titles and short plain text; use optional level for headings (omit for body). Use MarkDownRenderer for formatted content with links, bold, lists, tables, code. - Callout description supports markdown. - Table columns are defined with Col, rows are a 2D array of primitives. - FollowUpBlock items become new user messages on click. diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx index a7728d073..a61f68723 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/form-control.tsx @@ -1,7 +1,11 @@ "use client"; -import { Description, FieldError, Label } from "@heroui/react"; -import { defineComponent, useFormValidation } from "@openuidev/react-lang"; +import { Description, Label } from "@heroui/react"; +import { + type ComponentRenderProps, + defineComponent, + useFormValidation, +} from "@openuidev/react-lang"; import { z } from "zod"; import { CheckBoxGroup } from "./checkbox-group"; @@ -34,38 +38,45 @@ function getFieldNameFromInput(input: unknown): string | undefined { return obj?.type === "element" ? obj.props?.name : undefined; } +type FormControlProps = z.infer; + +function FormControlRenderer({ + props, + renderNode, +}: ComponentRenderProps) { + const formValidation = useFormValidation(); + const inputObj = props.input as { + type?: string; + props?: { name?: string; rules?: { required?: boolean } }; + }; + const fieldName = getFieldNameFromInput(props.input); + const error = fieldName ? formValidation?.errors[fieldName] : undefined; + const isRequired = + inputObj?.type === "element" && inputObj.props?.rules?.required === true; + + return ( +
+ + {renderNode(props.input)} + {error ? ( +

+ {error} +

+ ) : props.hint ? ( + + {props.hint} + + ) : null} +
+ ); +} + export const FormControl = defineComponent({ name: "FormControl", props: FormControlSchema, description: "Field with label, input component, and optional hint text", - component: ({ props, renderNode }) => { - const formValidation = useFormValidation(); - const inputObj = props.input as { - type?: string; - props?: { name?: string; rules?: { required?: boolean } }; - }; - const fieldName = getFieldNameFromInput(props.input); - const error = fieldName ? formValidation?.errors[fieldName] : undefined; - const isRequired = - inputObj?.type === "element" && inputObj.props?.rules?.required === true; - - return ( -
- - {renderNode(props.input)} - {error ? ( -

- {error} -

- ) : props.hint ? ( - - {props.hint} - - ) : null} -
- ); - }, + component: FormControlRenderer, }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx index 7a029ae94..c0952d22d 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/form.tsx @@ -1,6 +1,7 @@ "use client"; import { + type ComponentRenderProps, FormNameContext, FormValidationContext, defineComponent, @@ -17,26 +18,27 @@ const FormSchema = z.object({ fields: z.array(FormControl.ref).default([]), }); +type FormProps = z.infer; + +function FormRenderer({ props, renderNode }: ComponentRenderProps) { + const formValidation = useCreateFormValidation(); + const formName = props.name; + + return ( + + +
+ {renderNode(props.fields)} + {renderNode(props.buttons)} +
+
+
+ ); +} + export const Form = defineComponent({ name: "Form", props: FormSchema, description: "Form container with fields and explicit action buttons", - component: ({ props, renderNode }) => { - const formValidation = useCreateFormValidation(); - const formName = props.name; - - return ( - - -
- {renderNode(props.fields)} - {renderNode(props.buttons)} -
-
-
- ); - }, + component: FormRenderer, }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx index af9191819..a5d0ec663 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/input.tsx @@ -55,19 +55,16 @@ function InputRenderer({ props }: ComponentRenderProps { setFieldValue(formName, "Input", fieldName, e.target.value, true); }} onFocus={() => formValidation?.clearFieldError(fieldName)} onBlur={() => { if (rules.length > 0) { - formValidation?.validateField( - fieldName, - getFieldValue(formName, fieldName), - rules, - ); + formValidation?.validateField(fieldName, getFieldValue(formName, fieldName), rules); } }} disabled={isStreaming} diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx index f7b89f8e7..7b3a04456 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/switch-group.tsx @@ -43,24 +43,23 @@ function SwitchGroupRenderer({ props }: ComponentRenderProps => { const stored = getFieldValue(formName, fieldName) as Record | undefined; const result: Record = {}; - for (const item of items) { + for (const item of props.items) { result[item.props.name] = stored?.[item.props.name] ?? item.props.defaultChecked ?? false; } return result; - }, [formName, fieldName, items, getFieldValue]); + }, [formName, fieldName, props.items, getFieldValue]); - if (!items.length) return null; + if (!props.items.length) return null; const aggregate = getAggregate(); return ( - {items.map((item) => ( + {props.items.map((item) => ( {item.props.label && } - {item.props.description && ( - {item.props.description} - )} + {item.props.description && {item.props.description}} ))} diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx index baba52e7e..500cdc919 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/tag.tsx @@ -4,13 +4,13 @@ import { Chip } from "@heroui/react"; import { defineComponent } from "@openuidev/react-lang"; import { z } from "zod"; -const variantToColor = { +const variantToColor: Record["color"]> = { neutral: "default", info: "accent", success: "success", warning: "warning", danger: "danger", -} satisfies Record; +}; const TagSchema = z.object({ text: z.string(), diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx index 2723f77cd..bcc2d5457 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/text-content.tsx @@ -5,25 +5,26 @@ import { z } from "zod"; const TextContentSchema = z.object({ text: z.string(), - size: z.enum(["small", "default", "large", "small-heavy", "large-heavy"]).optional(), + level: z.enum(["h2", "h3", "h4"]).optional(), }); -const sizeClasses: Record = { - small: "text-sm text-muted", - default: "text-base", - large: "text-lg", - "small-heavy": "text-sm font-semibold", - "large-heavy": "text-lg font-semibold", +const levelClasses: Record<"h2" | "h3" | "h4", string> = { + h2: "text-2xl font-semibold tracking-tight", + h3: "text-xl font-semibold", + h4: "text-lg font-semibold", }; export const TextContent = defineComponent({ name: "TextContent", props: TextContentSchema, - description: "Plain text block", + description: "Plain text block; optional level is a section heading, omit for body paragraph.", component: ({ props }) => { const text = props.text == null ? "" : String(props.text); - const size = props.size ?? "default"; - const cls = sizeClasses[size]; - return

{text}

; + const level = props.level; + if (level) { + const Tag = level; + return {text}; + } + return

{text}

; }, }); diff --git a/examples/heroui-chat/src/lib/heroui-genui/components/textarea.tsx b/examples/heroui-chat/src/lib/heroui-genui/components/textarea.tsx index 510aad5db..c1e62dbbf 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/components/textarea.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/components/textarea.tsx @@ -55,18 +55,15 @@ function TextAreaRenderer({ props }: ComponentRenderProps { setFieldValue(formName, "TextArea", fieldName, e.target.value, true); }} onFocus={() => formValidation?.clearFieldError(fieldName)} onBlur={() => { if (rules.length > 0) { - formValidation?.validateField( - fieldName, - getFieldValue(formName, fieldName), - rules, - ); + formValidation?.validateField(fieldName, getFieldValue(formName, fieldName), rules); } }} disabled={isStreaming} diff --git a/examples/heroui-chat/src/lib/heroui-genui/index.tsx b/examples/heroui-chat/src/lib/heroui-genui/index.tsx index 571fbbb45..5d8da02ec 100644 --- a/examples/heroui-chat/src/lib/heroui-genui/index.tsx +++ b/examples/heroui-chat/src/lib/heroui-genui/index.tsx @@ -96,66 +96,13 @@ export const herouiComponentGroups: ComponentGroup[] = [ // ── Prompt Options ── export const herouiExamples: string[] = [ - `Example 1 — Simple text response: -root = Card([text]) -text = TextContent("Here is the information you requested.", "default")`, - - `Example 2 — Markdown with formatting: + `Example 1 — Markdown with formatting: root = Card([md]) md = MarkDownRenderer("## Summary\\n\\nHere are the key points:\\n\\n- **First** item\\n- **Second** item\\n- [Learn more](https://example.com)")`, - `Example 3 — Text with action buttons: -root = Card([text, btns]) -text = TextContent("Select one of the actions below to continue.") -btns = Buttons([b1, b2]) -b1 = Button("Continue", { type: "continue_conversation" }, "primary") -b2 = Button("Cancel", { type: "continue_conversation" }, "outline")`, - - `Example 4 — Card header with callout: -root = Card([hdr, callout]) -hdr = CardHeader("Important Update", "Please read carefully") -callout = Callout("accent", "New feature", "We've added **dark mode** support.")`, - - `Example 5 — Data table: -root = Card([title, tbl]) -title = TextContent("Sales Report", "large-heavy") -tbl = Table([c1, c2, c3], [["Widget A", 150, true], ["Widget B", 230, false]]) -c1 = Col("Product") -c2 = Col("Units", "number") -c3 = Col("In Stock")`, - - `Example 6 — Tabs: -root = Card([tabs]) -tabs = Tabs([t1, t2]) -t1 = TabItem("overview", "Overview", [md1]) -t2 = TabItem("details", "Details", [md2]) -md1 = MarkDownRenderer("## Overview\\nHigh-level summary.") -md2 = MarkDownRenderer("## Details\\nDetailed breakdown.")`, - - `Example 7 — Accordion: -root = Card([acc]) -acc = Accordion([a1, a2]) -a1 = AccordionItem("faq1", "What is OpenUI?", [text1]) -a2 = AccordionItem("faq2", "How does it work?", [text2]) -text1 = TextContent("OpenUI is a generative UI framework.") -text2 = TextContent("It streams structured component trees from an LLM.")`, - - `Example 8 — Follow-up suggestions: -root = Card([text, followups]) -text = TextContent("Here's what I found. Want to explore more?") -followups = FollowUpBlock([f1, f2, f3]) -f1 = FollowUpItem("Tell me more about pricing") -f2 = FollowUpItem("Show me alternatives") -f3 = FollowUpItem("Compare features")`, - - `Example 9 — Tags: -root = Card([title, tags]) -title = TextContent("Categories", "large-heavy") -tags = TagBlock(["React", "TypeScript", "HeroUI", "OpenUI"])`, - - `Example 10 — Form with inputs: + `Example 2 — Form with inputs: root = Card([title, form]) -title = TextContent("Contact Us", "large-heavy") +title = TextContent("Contact Us", "h2") form = Form("contact", btns, [fc1, fc2, fc3]) fc1 = FormControl("Name", input1) fc2 = FormControl("Email", input2, "We'll never share your email.") @@ -170,7 +117,7 @@ b1 = Button("Submit", { type: "continue_conversation" }, "primary")`, export const herouiAdditionalRules: string[] = [ "Every response is a single Card(children) — children stack vertically automatically.", "Card is the only layout container.", - "Use TextContent for titles and short plain text. Use MarkDownRenderer for formatted content with links, bold, lists, tables, code.", + "Use TextContent for titles and short plain text; use optional level for headings (omit for body). Use MarkDownRenderer for formatted content with links, bold, lists, tables, code.", "Callout description supports markdown.", "Table columns are defined with Col, rows are a 2D array of primitives.", "FollowUpBlock items become new user messages on click.", diff --git a/examples/openui-chat/package.json b/examples/openui-chat/package.json index 3371ffc8e..d42aad525 100644 --- a/examples/openui-chat/package.json +++ b/examples/openui-chat/package.json @@ -22,6 +22,7 @@ "devDependencies": { "@openuidev/cli": "workspace:*", "@tailwindcss/postcss": "^4", + "@tailwindcss/typography": "^0.5.19", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0aa68ec76..0b067b742 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,6 +178,9 @@ importers: '@tailwindcss/postcss': specifier: ^4 version: 4.2.1 + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@4.2.1) '@types/node': specifier: ^20 version: 20.19.35 @@ -233,6 +236,9 @@ importers: '@tailwindcss/postcss': specifier: ^4 version: 4.2.1 + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@4.2.1) '@types/node': specifier: ^20 version: 20.19.35 @@ -5210,6 +5216,11 @@ packages: '@tailwindcss/postcss@4.2.1': resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@takumi-rs/core-darwin-arm64@0.68.17': resolution: {integrity: sha512-toMlVnB19J+eUVDOV3mhVOqFn+fMazycMZoeQSJQR/GXtog2peRp2dKqSE8w6gi7nGUOrZzX4u4L+eX+vwnfww==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} @@ -8970,6 +8981,10 @@ packages: peerDependencies: postcss: ^8.4 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -16329,6 +16344,11 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.2.1 + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.1)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.2.1 + '@takumi-rs/core-darwin-arm64@0.68.17': optional: true @@ -21028,6 +21048,11 @@ snapshots: dependencies: postcss: 8.5.6 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 From a14e6e13953294e02fb67ef3af3e9e09fa5e3c71 Mon Sep 17 00:00:00 2001 From: Rabi Shanker Guha Date: Tue, 24 Mar 2026 16:32:33 +0530 Subject: [PATCH 06/14] Move to form builder instead of chat --- examples/heroui-chat/README.md | 214 +++++++++++- examples/heroui-chat/next.config.ts | 2 +- examples/heroui-chat/package.json | 1 - .../heroui-chat/src/app/api/chat/route.ts | 145 +------- examples/heroui-chat/src/app/layout.tsx | 9 +- examples/heroui-chat/src/app/page.tsx | 310 +++++++++++++++--- .../src/generated/system-prompt.txt | 70 +++- .../lib/heroui-genui/components/form-row.tsx | 32 ++ .../src/lib/heroui-genui/components/form.tsx | 3 +- .../src/lib/heroui-genui/index.tsx | 84 +++++ examples/heroui-chat/src/library.ts | 2 +- 11 files changed, 651 insertions(+), 221 deletions(-) create mode 100644 examples/heroui-chat/src/lib/heroui-genui/components/form-row.tsx diff --git a/examples/heroui-chat/README.md b/examples/heroui-chat/README.md index 03a161d8b..15a5fa83d 100644 --- a/examples/heroui-chat/README.md +++ b/examples/heroui-chat/README.md @@ -1,27 +1,211 @@ -This is an [OpenUI](https://openui.com) Agent Chat project bootstrapped with [`openui-cli`](https://openui.com/docs/chat/quick-start). +# HeroUI Form Generator Example + +A full-stack **generative form builder** that demonstrates wiring [OpenUI Lang](https://www.openui.com/docs/openui-lang/overview) to a custom component library built on [HeroUI](https://www.heroui.com/) ([`@heroui/react`](https://www.npmjs.com/package/@heroui/react)). + +Describe a form in plain language, get a live preview built from HeroUI components. Refine it with follow-up instructions — each iteration streams a fresh updated form. + +[View source on GitHub →](https://github.com/thesysdev/openui/tree/main/examples/heroui-chat) + +--- + +## How It Works + +The LLM is prompted with a form-focused system prompt that describes every available form component — name, props, and when to use it. Instead of returning prose, the model responds in **OpenUI Lang**: a declarative markup syntax that maps directly to React components. For example: + +``` +root = Card([title, form]) +title = TextContent("Contact Us", "h2") +form = Form("contact", btns, [fc1, fc2, fc3]) +fc1 = FormControl("Name", input1) +fc2 = FormControl("Email", input2) +fc3 = FormControl("Message", ta1) +input1 = Input("name", "Your name", "text", { required: true }) +input2 = Input("email", "you@example.com", "email", { required: true }) +ta1 = TextArea("message", "How can we help?", 4) +btns = Buttons([b1]) +b1 = Button("Submit", { type: "continue_conversation" }, "primary") +``` + +On the client, `` from `@openuidev/react-lang` parses the incoming SSE stream and renders each OpenUI Lang node using `herouiChatLibrary` — the custom library defined in `src/lib/heroui-genui/`. The app shell is built entirely with `@heroui/react` (no `@openuidev/react-ui` dependency). + +--- + +## Architecture + +``` +┌──────────────────────────────────────────┐ ┌────────────────────────────────────┐ +│ Browser │ HTTP │ Next.js API Route │ +│ │ ──────►│ │ +│ • Left panel: HeroUI TextArea + Button │ │ • Loads system-prompt.txt │ +│ • Iteration history (in-memory messages)│ │ • Calls LLM via OpenAI SDK │ +│ • processStreamedMessage + openAIAdapter│◄────── │ • Streams response as SSE events │ +│ • Right panel: preview │ SSE │ │ +│ • formFieldSnapshot via onStateUpdate │ │ │ +└──────────────────────────────────────────┘ └────────────────────────────────────┘ +``` + +### Request / Response Flow + +1. User types a form description and clicks **Generate Form** (or ⌘ Enter). +2. The page builds an `openAIMessageFormat.toApi(messages)` payload and `POST`s to `/api/chat`. +3. The API route reads `system-prompt.txt` and streams the LLM response as SSE events. +4. On the client, `processStreamedMessage` + `openAIAdapter()` accumulate the streamed text into an `AssistantMessage`. +5. `` parses the OpenUI Lang markup and progressively renders each node as a HeroUI-backed component. +6. The streamed assistant `content` (OpenUI Lang text only) is appended to the in-memory message list for the next iteration. + +### Iterative Refinement + +Submit a follow-up instruction (e.g. "add a phone number field" or "rename the submit button to 'Send'") and the page: + +- Optionally prefixes the new user message with a JSON snapshot of current form values from `onStateUpdate`. +- POSTs the **full** message list so the model can see the previous form spec and return one complete updated `Card`/`Form`. +- `` hydrates the preview with the latest field values, so filled data is not lost on re-render. + +Click **Reset** to clear history and start a fresh form. + +--- + +## Project Structure + +``` +heroui-chat/ +├── src/ +│ ├── app/ +│ │ ├── api/chat/route.ts # Streaming endpoint (OpenAI SDK + SSE, no tools) +│ │ ├── page.tsx # Two-column form generator UI +│ │ ├── layout.tsx # Root layout (no external provider needed) +│ │ └── globals.css # Tailwind + HeroUI styles +│ ├── lib/ +│ │ └── heroui-genui/ # Custom OpenUI component library +│ │ ├── index.tsx # Library + herouiFormPromptOptions export +│ │ ├── action.ts # Button action Zod schemas +│ │ ├── rules.ts # Form validation rule schemas +│ │ ├── unions.ts # Zod union types for component children +│ │ └── components/ # One file per component +│ ├── generated/ +│ │ └── system-prompt.txt # Auto-generated — do not edit manually +│ └── library.ts # CLI entry — re-exports library + promptOptions +└── package.json +``` + +--- ## Getting Started -First, run the development server: +### Prerequisites + +- Node.js 18+ +- pnpm +- An OpenAI API key + +### 1. Install dependencies + +```bash +cd examples/heroui-chat +pnpm install +``` + +### 2. Configure environment + +Create a `.env.local` file in the `examples/heroui-chat/` directory: + +``` +OPENAI_API_KEY=sk-... +``` + +### 3. Start the dev server ```bash -npm run dev -# or -yarn dev -# or pnpm dev -# or -bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +This runs `generate:prompt` first (builds the OpenUI CLI, compiles `src/library.ts` → `src/generated/system-prompt.txt`) then starts the Next.js dev server at `http://localhost:3000`. -You can start editing the page by modifying `src/app/api/route.ts` and improving your agent -by adding system prompts or tools. +--- -## Learn More +## What's in This Example + +### System Prompt Generation + +Component definitions and form-focused prompt options live in `src/lib/heroui-genui/index.tsx` (`createLibrary()`, `herouiFormPromptOptions`). The file `src/library.ts` re-exports them as `library` and `promptOptions` for the OpenUI CLI. + +When you run `pnpm dev` or `pnpm generate:prompt`, the CLI generates `src/generated/system-prompt.txt` — a file listing every form component's name, prop schema, description, and examples. This is the LLM's system prompt. + +`herouiFormPromptOptions` includes: +- **Two form examples** (contact form, registration with select + checkbox) +- **Rules** that instruct the model to: always wrap in `Card`, always include `Buttons` with a primary submit, define each `FormControl` as its own reference for streaming, treat follow-up instructions as refinements of the current form + +Re-run generation any time you change component definitions: + +```bash +pnpm generate:prompt +``` + +### `src/app/api/chat/route.ts` — Backend + +Accepts `POST /api/chat` with a `messages` array, prepends the system prompt, and streams the LLM response as **Server-Sent Events (SSE)** compatible with `openAIAdapter()`. -To learn more about OpenUI, take a look at the following resources: +Tools are intentionally omitted — the model focuses purely on generating OpenUI Lang form definitions. + +### `src/app/page.tsx` — Frontend + +Two-column layout built entirely with `@heroui/react`: + +| Panel | Contents | +| ----- | -------- | +| **Left** | `TextArea` for instructions, `Button` to generate/refine, example `Chip`s, instruction history | +| **Right** | `` inside a `Card` — live OpenUI Lang preview, scrollable, updates on each iteration | + +Key client-side state: + +| State | Purpose | +| ----- | ------- | +| `messages` | In-memory OpenAI-shaped message list sent on every request | +| `latestCode` | Last assistant's OpenUI Lang text; passed as `Renderer` `response` | +| `streamingCode` | Accumulates tokens live; passed during streaming | +| `formFieldSnapshot` | Current form field values from `Renderer` `onStateUpdate`; used for `initialState` and optionally prepended to next user message | + +### `src/lib/heroui-genui/` — Custom Component Library + +Each component is defined with `defineComponent()` from `@openuidev/react-lang`: + +- `name` — the OpenUI Lang node name the LLM will emit +- `props` — a Zod schema that validates and types the node's props as they stream in +- `description` — included in the system prompt +- `component` — the React render function; `renderNode()` recursively renders child nodes + +#### Form Components + +| Component | HeroUI Backing | Purpose | +| --------- | -------------- | ------- | +| `Form` | div | Container with validation context | +| `FormControl` | `Label`, `Description` | Labeled field wrapper | +| `Input` | `Input` | Text, email, password, etc. | +| `TextArea` | `TextArea` | Multi-line text | +| `Select` / `SelectItem` | `Select`, `ListBox` | Dropdown | +| `NumberField` | `NumberField` | Numeric input with stepper | +| `Slider` | `Slider` | Range input | +| `CheckBoxGroup` / `CheckBoxItem` | `CheckboxGroup`, `Checkbox` | Multi-select | +| `RadioGroup` / `RadioItem` | `RadioGroup`, `Radio` | Single-select | +| `Button` / `Buttons` | `Button`, `ButtonGroup` | Submit and action buttons | + +--- + +## Scripts + +| Script | Description | +| ------ | ----------- | +| `pnpm dev` | Build OpenUI CLI, generate system prompt, start Next.js dev server | +| `pnpm generate:prompt` | Build `@openuidev/cli` and regenerate `system-prompt.txt` | +| `pnpm build` | Build for production | +| `pnpm start` | Start the production server | + +--- + +## Learn More -- [OpenUI Documentation](https://openui.com/docs) - learn about OpenUI features and API. -- [OpenUI GitHub repository](https://github.com/thesysdev/openui) - your feedback and contributions are welcome! +- [OpenUI Lang overview](https://www.openui.com/docs/openui-lang/overview) — Library, Prompt Generator, Parser, Renderer +- [Defining Components](https://www.openui.com/docs/openui-lang/defining-components) — `defineComponent` and `createLibrary` API +- [HeroUI](https://www.heroui.com/) — the underlying React component library +- [`@openuidev/react-lang` package](../../packages/react-lang) +- [`@openuidev/react-headless` package](../../packages/react-headless) diff --git a/examples/heroui-chat/next.config.ts b/examples/heroui-chat/next.config.ts index 69512fdcd..313d13c1f 100644 --- a/examples/heroui-chat/next.config.ts +++ b/examples/heroui-chat/next.config.ts @@ -2,7 +2,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { turbopack: {}, - transpilePackages: ["@openuidev/react-ui", "@openuidev/react-headless", "@openuidev/react-lang"], + transpilePackages: ["@openuidev/react-headless", "@openuidev/react-lang"], }; export default nextConfig; diff --git a/examples/heroui-chat/package.json b/examples/heroui-chat/package.json index e11507d95..927589e13 100644 --- a/examples/heroui-chat/package.json +++ b/examples/heroui-chat/package.json @@ -14,7 +14,6 @@ "@heroui/styles": "^3.0.1", "@openuidev/react-headless": "workspace:*", "@openuidev/react-lang": "workspace:*", - "@openuidev/react-ui": "workspace:*", "lucide-react": "^0.575.0", "next": "16.1.6", "openai": "^6.22.0", diff --git a/examples/heroui-chat/src/app/api/chat/route.ts b/examples/heroui-chat/src/app/api/chat/route.ts index 0ebcc126b..24aececd4 100644 --- a/examples/heroui-chat/src/app/api/chat/route.ts +++ b/examples/heroui-chat/src/app/api/chat/route.ts @@ -6,147 +6,6 @@ import { join } from "path"; const systemPrompt = readFileSync(join(process.cwd(), "src/generated/system-prompt.txt"), "utf-8"); -// ── 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( @@ -209,8 +68,6 @@ export async function POST(req: NextRequest) { .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; } @@ -245,7 +102,7 @@ export async function POST(req: NextRequest) { const runner = (client.chat.completions as any).runTools({ model: MODEL, messages: chatMessages, - tools, + tools: [], stream: true }); diff --git a/examples/heroui-chat/src/app/layout.tsx b/examples/heroui-chat/src/app/layout.tsx index 008d12d5a..598ec1385 100644 --- a/examples/heroui-chat/src/app/layout.tsx +++ b/examples/heroui-chat/src/app/layout.tsx @@ -1,10 +1,9 @@ -import { ThemeProvider } from "@openuidev/react-ui"; import type { Metadata } from "next"; import "./globals.css"; export const metadata: Metadata = { - title: "OpenUI + HeroUI Chat", - description: "Generative UI Chat with OpenAI SDK + HeroUI components", + title: "OpenUI Form Generator", + description: "Generate and refine forms from natural language prompts using OpenUI + HeroUI", }; export default function RootLayout({ @@ -14,9 +13,7 @@ export default function RootLayout({ }>) { return ( - - {children} - + {children} ); } diff --git a/examples/heroui-chat/src/app/page.tsx b/examples/heroui-chat/src/app/page.tsx index 18e6fdec2..9cf2204dd 100644 --- a/examples/heroui-chat/src/app/page.tsx +++ b/examples/heroui-chat/src/app/page.tsx @@ -1,48 +1,280 @@ "use client"; -import "@openuidev/react-ui/components.css"; import { herouiChatLibrary } from "@/lib/heroui-genui"; -import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless"; -import { FullScreen } from "@openuidev/react-ui"; +import { Button, Card, Chip, Spinner, TextArea } from "@heroui/react"; +import type { Message } from "@openuidev/react-headless"; +import { + openAIAdapter, + openAIMessageFormat, + processStreamedMessage, +} from "@openuidev/react-headless"; +import { Renderer } from "@openuidev/react-lang"; +import { useCallback, useRef, useState } from "react"; + +const STARTERS = [ + { + label: "Contact form", + prompt: "Build me a contact form with name, email, topic, and message fields.", + }, + { + label: "Registration", + prompt: + "Create a user registration form with first name, last name, email, password, and country fields.", + }, + { + label: "Job application", + prompt: + "Build a job application form with personal info, position, experience, and a cover letter field.", + }, + { + label: "Feedback survey", + prompt: + "Create a feedback survey form with a rating slider, category dropdown, and comments textarea.", + }, +]; export default function Page() { + const [instruction, setInstruction] = useState(""); + const [messages, setMessages] = useState([]); + const [streamingCode, setStreamingCode] = useState(null); + const [latestCode, setLatestCode] = useState(null); + const [formFieldSnapshot, setFormFieldSnapshot] = useState>({}); + const [isStreaming, setIsStreaming] = useState(false); + const [error, setError] = useState(null); + const abortRef = useRef(null); + + const handleSubmit = useCallback(async () => { + const text = instruction.trim(); + if (!text || isStreaming) return; + + setError(null); + setInstruction(""); + + let userContent = text; + if (Object.keys(formFieldSnapshot).length > 0) { + userContent = `Current form values (JSON): ${JSON.stringify(formFieldSnapshot)}\n\n${text}`; + } + + const userMsg: Message = { id: crypto.randomUUID(), role: "user", content: userContent }; + const nextMessages = [...messages, userMsg]; + setMessages(nextMessages); + + const abortController = new AbortController(); + abortRef.current = abortController; + setIsStreaming(true); + setStreamingCode(""); + + let draftContent = ""; + + try { + const response = await fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + messages: openAIMessageFormat.toApi(nextMessages as any), + }), + signal: abortController.signal, + }); + + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const assistantId = crypto.randomUUID(); + + await processStreamedMessage({ + response, + adapter: openAIAdapter(), + createMessage: (msg) => { + draftContent = msg.content ?? ""; + setStreamingCode(draftContent); + }, + updateMessage: (msg) => { + draftContent = msg.content ?? ""; + setStreamingCode(draftContent); + }, + deleteMessage: () => { + draftContent = ""; + setStreamingCode(""); + }, + }); + + const assistantMsg: Message = { + id: assistantId, + role: "assistant", + content: draftContent, + }; + setMessages((prev) => [...prev, assistantMsg]); + setLatestCode(draftContent); + setStreamingCode(null); + } catch (err) { + if ((err as Error).name !== "AbortError") { + setError((err as Error).message ?? "Something went wrong."); + } + } finally { + setIsStreaming(false); + abortRef.current = null; + } + }, [instruction, isStreaming, messages, formFieldSnapshot]); + + const handleReset = useCallback(() => { + abortRef.current?.abort(); + setMessages([]); + setLatestCode(null); + setStreamingCode(null); + setFormFieldSnapshot({}); + setError(null); + setInstruction(""); + }, []); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + handleSubmit(); + } + }, + [handleSubmit], + ); + + const rendererResponse = streamingCode ?? latestCode; + const hasForm = rendererResponse !== null; + + const pastInstructions = messages + .filter((m) => m.role === "user") + .map((m) => { + const content = (m.content as string) ?? ""; + const jsonPrefix = /^Current form values \(JSON\): .+\n\n/; + return content.replace(jsonPrefix, ""); + }); + return ( -
- { - return fetch("/api/chat", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - messages: openAIMessageFormat.toApi(messages), - }), - signal: abortController.signal, - }); - }} - streamProtocol={openAIAdapter()} - componentLibrary={herouiChatLibrary} - agentName="OpenUI Chat" - theme={{ mode: "light" }} - conversationStarters={{ - variant: "short", - options: [ - { - displayText: "Weather in Tokyo", - prompt: "What's the weather like in Tokyo right now?", - }, - { displayText: "AAPL stock price", prompt: "What's the current Apple stock price?" }, - { - displayText: "Contact form", - prompt: "Build me a contact form with name, email, topic, and message fields.", - }, - { - displayText: "Data table", - prompt: - "Show me a table of the top 5 programming languages by popularity with year created.", - }, - ], - }} - /> +
+ {/* Header */} +
+
+

Form Generator

+

+ Describe a form and get a live preview +

+
+ {hasForm && ( + + )} +
+ + {/* Body */} +
+ {/* Left panel */} +