From c47cab25f02a8b2680cce43fd882b50ed129bdd0 Mon Sep 17 00:00:00 2001 From: Nizar Izzuddin Yatim Fadlan Date: Tue, 7 Apr 2026 00:41:50 +0700 Subject: [PATCH 1/3] Add solid-lang runtime package for OpenUI --- .github/workflows/publish-npm-package.yml | 1 + README.md | 4 +- packages/solid-lang/README.md | 130 ++++++ packages/solid-lang/eslint.config.cjs | 73 ++++ packages/solid-lang/package.json | 75 ++++ packages/solid-lang/src/Renderer.tsx | 164 +++++++ .../src/__tests__/Renderer.test.tsx | 81 ++++ .../solid-lang/src/__tests__/library.test.ts | 119 +++++ .../src/__tests__/validation.test.ts | 113 +++++ packages/solid-lang/src/context.ts | 80 ++++ packages/solid-lang/src/index.ts | 61 +++ packages/solid-lang/src/library.ts | 46 ++ packages/solid-lang/src/validation.ts | 85 ++++ packages/solid-lang/tsconfig.json | 15 + packages/solid-lang/tsconfig.test.json | 9 + packages/solid-lang/vite.config.ts | 10 + pnpm-lock.yaml | 407 ++++++++++++++---- 17 files changed, 1392 insertions(+), 81 deletions(-) create mode 100644 packages/solid-lang/README.md create mode 100644 packages/solid-lang/eslint.config.cjs create mode 100644 packages/solid-lang/package.json create mode 100644 packages/solid-lang/src/Renderer.tsx create mode 100644 packages/solid-lang/src/__tests__/Renderer.test.tsx create mode 100644 packages/solid-lang/src/__tests__/library.test.ts create mode 100644 packages/solid-lang/src/__tests__/validation.test.ts create mode 100644 packages/solid-lang/src/context.ts create mode 100644 packages/solid-lang/src/index.ts create mode 100644 packages/solid-lang/src/library.ts create mode 100644 packages/solid-lang/src/validation.ts create mode 100644 packages/solid-lang/tsconfig.json create mode 100644 packages/solid-lang/tsconfig.test.json create mode 100644 packages/solid-lang/vite.config.ts diff --git a/.github/workflows/publish-npm-package.yml b/.github/workflows/publish-npm-package.yml index 7e2aef79c..3941f3971 100644 --- a/.github/workflows/publish-npm-package.yml +++ b/.github/workflows/publish-npm-package.yml @@ -18,6 +18,7 @@ on: - react-email - openui-cli - svelte-lang + - solid-lang - vue-lang jobs: diff --git a/README.md b/README.md index d67cebb5c..5a4b7bf3e 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Try it yourself in the [Playground](https://www.openui.com/playground) — gener | Package | Description | | :--- | :--- | | [`@openuidev/react-lang`](./packages/react-lang) | Core runtime — component definitions, parser, renderer, prompt generation | +| [`@openuidev/solid-lang`](./packages/solid-lang) | SolidJS runtime — component definitions, parser, renderer, prompt generation | | [`@openuidev/react-headless`](./packages/react-headless) | Headless chat state, streaming adapters, message format converters | | [`@openuidev/react-ui`](./packages/react-ui) | Prebuilt chat layouts and two built-in component libraries | | [`@openuidev/cli`](./packages/openui-cli) | CLI for scaffolding apps and generating system prompts | @@ -133,7 +134,8 @@ Detailed documentation is available at [openui.com](https://openui.com). ``` openui/ ├── packages/ -│ ├── react-lang/ # Core runtime (parser, renderer, prompt generation) +│ ├── react-lang/ # React runtime (parser, renderer, prompt generation) +│ ├── solid-lang/ # SolidJS runtime (parser, renderer, prompt generation) │ ├── react-headless/ # Headless chat state & streaming adapters │ ├── react-ui/ # Prebuilt chat layouts & component libraries │ └── openui-cli/ # CLI for scaffolding & prompt generation diff --git a/packages/solid-lang/README.md b/packages/solid-lang/README.md new file mode 100644 index 000000000..476aaba08 --- /dev/null +++ b/packages/solid-lang/README.md @@ -0,0 +1,130 @@ +# @openuidev/solid-lang + +Core runtime for [OpenUI](https://openui.com) in SolidJS — define component libraries, generate model prompts, and render structured UI from streaming LLM output. + +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/thesysdev/openui/blob/main/LICENSE) + +## Install + +```bash +npm install @openuidev/solid-lang +# or +pnpm add @openuidev/solid-lang +``` + +**Peer dependencies:** `solid-js >=1.9.0` + +## Overview + +`@openuidev/solid-lang` provides three core capabilities: + +1. **Define components** — Use `defineComponent` and `createLibrary` to declare what the model is allowed to generate, with typed props via Zod schemas. +2. **Generate prompts** — Call `library.prompt()` to produce a system prompt that instructs the model how to emit OpenUI Lang output. +3. **Render output** — Use `` to parse and progressively render streamed OpenUI Lang into SolidJS components. + +## Quick Start + +### 1. Define a component + +```tsx +import { defineComponent } from "@openuidev/solid-lang"; +import { z } from "zod"; + +const Greeting = defineComponent({ + name: "Greeting", + description: "Displays a greeting message", + props: z.object({ + name: z.string().describe("The person's name"), + mood: z.enum(["happy", "excited"]).optional().describe("Tone of the greeting"), + }), + component: ({ props }) => ( +
Hello, {props.name}!
+ ), +}); +``` + +### 2. Create a library + +```ts +import { createLibrary } from "@openuidev/solid-lang"; + +const library = createLibrary({ + components: [Greeting, Card, Table /* ... */], + root: "Card", +}); +``` + +### 3. Generate a system prompt + +```ts +const systemPrompt = library.prompt({ + preamble: "You are a helpful assistant.", + additionalRules: ["Always greet the user by name."], + examples: ['root = Greeting("Alice", "happy")'], +}); +``` + +### 4. Render streamed output + +```tsx +import { Renderer } from "@openuidev/solid-lang"; + + console.log("Action:", event)} +/>; +``` + +## API Reference + +### Component Definition + +| Export | Description | +| :-------------------------- | :-------------------------------------------------------------------------------------- | +| `defineComponent(config)` | Define a single component with a name, Zod props schema, description, and Solid renderer | +| `createLibrary(definition)` | Create a library from an array of defined components | + +### Rendering + +| Export | Description | +| :--------- | :--------------------------------------------------------- | +| `Renderer` | Solid component that parses and renders OpenUI Lang output | + +### Context helpers + +Use these inside component renderers to interact with the rendering context: + +- `useOpenUI()` +- `useRenderNode()` +- `useTriggerAction()` +- `useIsStreaming()` +- `useGetFieldValue()` +- `useSetFieldValue()` +- `useSetDefaultValue()` +- `useFormName()` + +### Form Validation + +| Export | Description | +| :----------------------- | :---------------------------------------------------- | +| `useFormValidation()` | Access form validation state | +| `createFormValidation()` | Create a form validation context | +| `validate(value, rules)` | Run validation rules against a value | +| `builtInValidators` | Built-in validators (required, email, min, max, etc.) | + +### Parser (Server-Side) + +| Export | Description | +| :------------------------------ | :----------------------------------------------------- | +| `createParser(schema)` | Create a one-shot parser for complete OpenUI Lang text | +| `createStreamingParser(schema)` | Create an incremental parser for streaming input | + +## Documentation + +Full documentation, guides, and the language specification are available at **[openui.com](https://openui.com)**. + +## License + +[MIT](https://github.com/thesysdev/openui/blob/main/LICENSE) diff --git a/packages/solid-lang/eslint.config.cjs b/packages/solid-lang/eslint.config.cjs new file mode 100644 index 000000000..4e9c2056b --- /dev/null +++ b/packages/solid-lang/eslint.config.cjs @@ -0,0 +1,73 @@ +const eslint = require("@eslint/js"); +const tseslint = require("@typescript-eslint/eslint-plugin"); +const typescript = require("@typescript-eslint/parser"); +const prettier = require("eslint-config-prettier"); +const unusedImports = require("eslint-plugin-unused-imports"); +const eslintPluginPrettier = require("eslint-plugin-prettier"); + +module.exports = [ + { + files: ["**/__tests__/**/*.{ts,tsx}", "**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"], + languageOptions: { + parser: typescript, + parserOptions: { + project: "./tsconfig.test.json", + sourceType: "module", + }, + }, + }, + { + files: ["**/*.{ts,tsx}"], + ignores: [ + "**/*.stories.tsx", + "**/__tests__/**/*.{ts,tsx}", + "**/*.test.{ts,tsx}", + "**/*.spec.{ts,tsx}", + ], + languageOptions: { + parser: typescript, + parserOptions: { + project: "./tsconfig.json", + sourceType: "module", + }, + }, + plugins: { + "@typescript-eslint": tseslint, + "unused-imports": unusedImports, + prettier: eslintPluginPrettier, + }, + rules: { + "@typescript-eslint/interface-name-prefix": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "no-undefined": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", + }, + ], + "@typescript-eslint/no-use-before-define": [ + "error", + { + functions: false, + classes: false, + variables: false, + }, + ], + "unused-imports/no-unused-imports": "error", + "no-console": [ + "error", + { + allow: ["error", "warn", "info"], + }, + ], + ...eslintPluginPrettier.configs.recommended.rules, + }, + }, + prettier, +]; diff --git a/packages/solid-lang/package.json b/packages/solid-lang/package.json new file mode 100644 index 000000000..0946b7d92 --- /dev/null +++ b/packages/solid-lang/package.json @@ -0,0 +1,75 @@ +{ + "name": "@openuidev/solid-lang", + "version": "0.1.0", + "description": "Define component libraries, generate LLM system prompts, and render streaming OpenUI Lang output in SolidJS — the Solid runtime for OpenUI generative UI", + "license": "MIT", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "README.md" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "scripts": { + "test": "vitest run", + "build": "tsc -p .", + "watch": "tsc -p . --watch", + "lint:check": "eslint ./src", + "lint:fix": "eslint ./src --fix", + "format:fix": "prettier --write ./src", + "format:check": "prettier --check ./src", + "prepare": "pnpm run build", + "ci": "pnpm run lint:check && pnpm run format:check" + }, + "keywords": [ + "openui", + "generative-ui", + "solid", + "solidjs", + "llm", + "streaming", + "renderer", + "parser", + "ai", + "components", + "prompt-generation", + "zod", + "ui-generation", + "model-driven-ui", + "openui-lang" + ], + "homepage": "https://openui.com", + "repository": { + "type": "git", + "url": "https://github.com/thesysdev/openui.git", + "directory": "packages/solid-lang" + }, + "bugs": { + "url": "https://github.com/thesysdev/openui/issues" + }, + "author": "engineering@thesys.dev", + "dependencies": { + "@openuidev/lang-core": "workspace:^", + "zod": "^4.0.0" + }, + "peerDependencies": { + "solid-js": ">=1.9.0" + }, + "devDependencies": { + "@solidjs/testing-library": "^0.8.10", + "@testing-library/jest-dom": "^6.9.1", + "jsdom": "^26.1.0", + "solid-js": "^1.9.0", + "typescript": "^5.0.0", + "vite": "^6.0.0", + "vite-plugin-solid": "^2.11.6", + "vitest": "^3.0.0" + } +} diff --git a/packages/solid-lang/src/Renderer.tsx b/packages/solid-lang/src/Renderer.tsx new file mode 100644 index 000000000..850dd4aed --- /dev/null +++ b/packages/solid-lang/src/Renderer.tsx @@ -0,0 +1,164 @@ +import type { ActionEvent, ElementNode, ParseResult } from "@openuidev/lang-core"; +import { BuiltinActionType, createParser } from "@openuidev/lang-core"; +import { + createEffect, + createMemo, + createSignal, + For, + Match, + Show, + Switch, + type JSX, +} from "solid-js"; +import { Dynamic } from "solid-js/web"; +import { OpenUIContext, type ActionConfig } from "./context"; +import type { Library } from "./library"; + +export interface RendererProps { + response: string | null; + library: Library; + isStreaming?: boolean; + onAction?: (event: ActionEvent) => void; + onStateUpdate?: (state: Record) => void; + initialState?: Record; + onParseResult?: (result: ParseResult | null) => void; +} + +function renderDeep(value: unknown, library: Library, renderNode: (value: unknown) => JSX.Element) { + if (value == null) return null; + if (typeof value === "string") return value; + if (typeof value === "number" || typeof value === "boolean") return String(value); + if (Array.isArray(value)) { + return {(item) => <>{renderDeep(item, library, renderNode)}}; + } + if (typeof value === "object" && (value as any).type === "element") { + return ; + } + return null; +} + +function RenderNode(props: { + node: ElementNode; + library: Library; + renderNode: (value: unknown) => JSX.Element; +}) { + const Comp = () => props.library.components[props.node.typeName]?.component; + + return ( + + {(ResolvedComp) => ( + + )} + + ); +} + +export function Renderer(props: RendererProps) { + const parser = createParser(props.library.toJSONSchema()); + + const [formState, setFormState] = createSignal>(props.initialState ?? {}); + const [previousInitialState, setPreviousInitialState] = createSignal(props.initialState); + + createEffect(() => { + if (previousInitialState() !== props.initialState) { + setPreviousInitialState(props.initialState); + setFormState(props.initialState ?? {}); + } + }); + + const result = createMemo(() => { + if (!props.response) return null; + try { + return parser.parse(props.response); + } catch (error) { + console.error("[openui] Parse error:", error); + return null; + } + }); + + createEffect(() => { + props.onParseResult?.(result()); + }); + + function getFieldValue(formName: string | undefined, name: string): any { + const state = formState(); + return formName ? state[formName]?.[name]?.value : state[name]?.value; + } + + function setFieldValue( + formName: string | undefined, + componentType: string | undefined, + name: string, + value: any, + shouldTriggerSaveCallback: boolean = true, + ): void { + setFormState((prev) => { + const next = { ...prev }; + + if (formName) { + next[formName] = { + ...next[formName], + [name]: { value, componentType }, + }; + } else { + next[name] = { value, componentType }; + } + + if (shouldTriggerSaveCallback && props.onStateUpdate) { + props.onStateUpdate(next); + } + + return next; + }); + } + + function triggerAction(userMessage: string, formName?: string, action?: ActionConfig): void { + const actionType = action?.type || BuiltinActionType.ContinueConversation; + const actionParams = action?.params; + const state = formState(); + + let relevantState: Record | undefined; + if (formName && state[formName]) { + relevantState = { [formName]: state[formName] }; + } else if (Object.keys(state).length > 0) { + relevantState = state; + } + + if (!props.onAction) return; + + props.onAction({ + type: actionType, + params: actionParams || {}, + humanFriendlyMessage: userMessage, + formState: relevantState, + formName, + }); + } + + const renderNode = (value: unknown) => renderDeep(value, props.library, renderNode); + + const contextValue = { + get library() { + return props.library; + }, + renderNode, + triggerAction, + isStreaming: () => props.isStreaming ?? false, + getFieldValue, + setFieldValue, + }; + + return ( + + + + {(root) => } + + + + ); +} diff --git a/packages/solid-lang/src/__tests__/Renderer.test.tsx b/packages/solid-lang/src/__tests__/Renderer.test.tsx new file mode 100644 index 000000000..f2d2b353d --- /dev/null +++ b/packages/solid-lang/src/__tests__/Renderer.test.tsx @@ -0,0 +1,81 @@ +import { render } from "@solidjs/testing-library"; +import { describe, expect, it, vi } from "vitest"; +import { z } from "zod"; +import { Renderer } from "../Renderer"; +import { createLibrary, defineComponent } from "../library"; + +const DummyComponent = (() => null) as any; + +const TextContent = defineComponent({ + name: "TextContent", + props: z.object({ text: z.string() }), + description: "Displays text content", + component: DummyComponent, +}); + +const library = createLibrary({ + components: [TextContent], + root: "TextContent", +}); + +const VALID_RESPONSE = 'root = TextContent("Hello world")'; + +describe("Renderer", () => { + it("renders without errors when response is null", () => { + const { container } = render(() => ); + expect(container).toBeDefined(); + }); + + it("renders without errors when response is empty string", () => { + const { container } = render(() => ); + expect(container).toBeDefined(); + }); + + it("calls onParseResult with null when response is null", async () => { + const onParseResult = vi.fn(); + + render(() => ); + + await Promise.resolve(); + expect(onParseResult).toHaveBeenCalledWith(null); + }); + + it("calls onParseResult with a ParseResult when given valid openui-lang", async () => { + const onParseResult = vi.fn(); + + render(() => ( + + )); + + await Promise.resolve(); + + expect(onParseResult).toHaveBeenCalled(); + const result = onParseResult.mock.calls[onParseResult.mock.calls.length - 1]![0]; + expect(result).not.toBeNull(); + expect(result.root).toBeDefined(); + expect(result.root).not.toBeNull(); + }); + + it("parse result contains the correct component typeName", async () => { + const onParseResult = vi.fn(); + + render(() => ( + + )); + + await Promise.resolve(); + + const result = onParseResult.mock.calls[onParseResult.mock.calls.length - 1]![0]; + expect(result?.root?.typeName).toBe("TextContent"); + }); + + it("defaults isStreaming to false", () => { + const { container } = render(() => ); + expect(container).toBeDefined(); + }); + + it("accepts isStreaming prop without errors", () => { + const { container } = render(() => ); + expect(container).toBeDefined(); + }); +}); diff --git a/packages/solid-lang/src/__tests__/library.test.ts b/packages/solid-lang/src/__tests__/library.test.ts new file mode 100644 index 000000000..00855b16a --- /dev/null +++ b/packages/solid-lang/src/__tests__/library.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from "vitest"; +import { z } from "zod"; +import { createLibrary, defineComponent } from "../library"; + +const DummyComponent = (() => null) as any; + +function makeComponent(name: string, schema: z.ZodObject, description: string) { + return defineComponent({ + name, + props: schema, + description, + component: DummyComponent, + }); +} + +describe("defineComponent", () => { + it("returns an object with name, props, description, component, and ref", () => { + const schema = z.object({ label: z.string() }); + const result = defineComponent({ + name: "Badge", + props: schema, + description: "A simple badge", + component: DummyComponent, + }); + + expect(result.name).toBe("Badge"); + expect(result.props).toBe(schema); + expect(result.description).toBe("A simple badge"); + expect(result.component).toBe(DummyComponent); + expect(result.ref).toBeDefined(); + }); + + it("registers the Zod schema in the global registry", () => { + const schema = z.object({ title: z.string() }); + const comp = defineComponent({ + name: "Heading", + props: schema, + description: "A heading element", + component: DummyComponent, + }); + + expect(z.globalRegistry.has(comp.props)).toBe(true); + }); +}); + +describe("createLibrary", () => { + const TextContent = makeComponent( + "TextContent", + z.object({ text: z.string() }), + "Displays text content", + ); + + const Container = makeComponent( + "Container", + z.object({ title: z.string() }), + "A container with a title", + ); + + it("creates a library with a components record", () => { + const lib = createLibrary({ components: [TextContent, Container] }); + + expect(lib.components.TextContent).toBe(TextContent); + expect(lib.components.Container).toBe(Container); + expect(Object.keys(lib.components)).toHaveLength(2); + }); + + it("stores root and componentGroups", () => { + const lib = createLibrary({ + components: [TextContent], + root: "TextContent", + componentGroups: [{ name: "Display", components: ["TextContent"] }], + }); + + expect(lib.root).toBe("TextContent"); + expect(lib.componentGroups).toEqual([{ name: "Display", components: ["TextContent"] }]); + }); + + it("throws if root component is not found in components", () => { + expect(() => + createLibrary({ + components: [TextContent], + root: "NonExistent", + }), + ).toThrow(/Root component "NonExistent" was not found/); + }); + + it("prompt() returns a string containing component descriptions", () => { + const lib = createLibrary({ + components: [TextContent, Container], + root: "TextContent", + }); + + const prompt = lib.prompt(); + expect(typeof prompt).toBe("string"); + expect(prompt.length).toBeGreaterThan(0); + expect(prompt).toContain("TextContent"); + }); + + it("toJSONSchema() returns an object with $defs", () => { + const lib = createLibrary({ + components: [TextContent], + root: "TextContent", + }); + + const schema = lib.toJSONSchema() as Record; + expect(schema).toBeDefined(); + expect(typeof schema).toBe("object"); + expect(schema["$defs"]).toBeDefined(); + expect(typeof schema["$defs"]).toBe("object"); + }); + + it("works without a root component", () => { + const lib = createLibrary({ components: [TextContent] }); + + expect(lib.root).toBeUndefined(); + expect(typeof lib.prompt()).toBe("string"); + expect(lib.toJSONSchema()).toBeDefined(); + }); +}); diff --git a/packages/solid-lang/src/__tests__/validation.test.ts b/packages/solid-lang/src/__tests__/validation.test.ts new file mode 100644 index 000000000..45acd801c --- /dev/null +++ b/packages/solid-lang/src/__tests__/validation.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, it } from "vitest"; +import { builtInValidators, parseRules, parseStructuredRules, validate } from "../validation"; + +describe("builtInValidators", () => { + it("has all expected validators", () => { + const expected = [ + "required", + "email", + "url", + "numeric", + "min", + "max", + "minLength", + "maxLength", + "pattern", + ]; + for (const name of expected) { + expect(builtInValidators[name]).toBeDefined(); + expect(typeof builtInValidators[name]).toBe("function"); + } + }); +}); + +describe("parseRules", () => { + it("parses simple rule strings into ParsedRule objects", () => { + const result = parseRules(["required", "email"]); + expect(result).toEqual([{ type: "required" }, { type: "email" }]); + }); + + it("parses rules with numeric arguments", () => { + const result = parseRules(["min:8", "maxLength:100"]); + expect(result).toEqual([ + { type: "min", arg: 8 }, + { type: "maxLength", arg: 100 }, + ]); + }); + + it("parses rules with string arguments", () => { + const result = parseRules(["pattern:^[a-z]+"]); + expect(result).toEqual([{ type: "pattern", arg: "^[a-z]+" }]); + }); + + it("returns an empty array for non-array input", () => { + expect(parseRules(null)).toEqual([]); + expect(parseRules(undefined)).toEqual([]); + expect(parseRules("required")).toEqual([]); + }); + + it("filters out non-string entries", () => { + const result = parseRules(["required", 42, null, "email"]); + expect(result).toEqual([{ type: "required" }, { type: "email" }]); + }); +}); + +describe("validate", () => { + it("returns error string for required on empty value", () => { + const rules = [{ type: "required" }]; + const error = validate("", rules); + expect(error).toBe("This field is required"); + }); + + it("returns undefined when valid value passes required", () => { + const rules = [{ type: "required" }]; + expect(validate("hello", rules)).toBeUndefined(); + }); + + it("validates email format", () => { + const rules = [{ type: "email" }]; + expect(validate("bad-email", rules)).toBe("Please enter a valid email"); + expect(validate("test@email.com", rules)).toBeUndefined(); + }); + + it("validates min/max with numeric arguments", () => { + expect(validate(3, [{ type: "min", arg: 5 }])).toBe("Must be at least 5"); + expect(validate(10, [{ type: "min", arg: 5 }])).toBeUndefined(); + expect(validate(20, [{ type: "max", arg: 10 }])).toBe("Must be no more than 10"); + expect(validate(5, [{ type: "max", arg: 10 }])).toBeUndefined(); + }); + + it("stops on first error with multiple rules", () => { + const rules = [{ type: "required" }, { type: "email" }]; + expect(validate("", rules)).toBe("This field is required"); + }); + + it("returns undefined when no rules match", () => { + expect(validate("anything", [{ type: "nonExistentRule" }])).toBeUndefined(); + }); +}); + +describe("parseStructuredRules", () => { + it("parses an object of rules into ParsedRule array", () => { + const result = parseStructuredRules({ required: true, minLength: 5 }); + expect(result).toContainEqual({ type: "required" }); + expect(result).toContainEqual({ type: "minLength", arg: 5 }); + }); + + it("skips false/undefined/null values", () => { + const result = parseStructuredRules({ + required: true, + email: false, + max: undefined, + min: null, + }); + expect(result).toEqual([{ type: "required" }]); + }); + + it("returns empty array for non-object input", () => { + expect(parseStructuredRules(null)).toEqual([]); + expect(parseStructuredRules(undefined)).toEqual([]); + expect(parseStructuredRules([])).toEqual([]); + expect(parseStructuredRules("string")).toEqual([]); + }); +}); diff --git a/packages/solid-lang/src/context.ts b/packages/solid-lang/src/context.ts new file mode 100644 index 000000000..7af0325a6 --- /dev/null +++ b/packages/solid-lang/src/context.ts @@ -0,0 +1,80 @@ +import { createContext, createEffect, useContext, type Accessor, type JSX } from "solid-js"; +import type { Library } from "./library"; + +export interface ActionConfig { + type?: string; + params?: Record; +} + +export interface OpenUIContextValue { + library: Library; + renderNode: (value: unknown) => JSX.Element; + triggerAction: (userMessage: string, formName?: string, action?: ActionConfig) => void; + isStreaming: Accessor; + getFieldValue: (formName: string | undefined, name: string) => any; + setFieldValue: ( + formName: string | undefined, + componentType: string | undefined, + name: string, + value: any, + shouldTriggerSaveCallback?: boolean, + ) => void; +} + +export const OpenUIContext = createContext(); +export const FormNameContext = createContext>(); + +export function useOpenUI(): OpenUIContextValue { + const ctx = useContext(OpenUIContext); + if (!ctx) { + throw new Error("useOpenUI must be used within a component."); + } + return ctx; +} + +export function useRenderNode() { + return useOpenUI().renderNode; +} + +export function useTriggerAction() { + return useOpenUI().triggerAction; +} + +export function useIsStreaming(): Accessor { + return useOpenUI().isStreaming; +} + +export function useGetFieldValue() { + return useOpenUI().getFieldValue; +} + +export function useSetFieldValue() { + return useOpenUI().setFieldValue; +} + +export function useFormName(): Accessor | undefined { + return useContext(FormNameContext); +} + +export function useSetDefaultValue({ + formName, + componentType, + name, + defaultValue, + shouldTriggerSaveCallback = false, +}: { + formName?: string; + componentType?: string; + name: string; + defaultValue: unknown; + shouldTriggerSaveCallback?: boolean; +}) { + const ctx = useOpenUI(); + + createEffect(() => { + const existing = ctx.getFieldValue(formName, name); + if (!ctx.isStreaming() && existing === undefined && defaultValue !== undefined) { + ctx.setFieldValue(formName, componentType, name, defaultValue, shouldTriggerSaveCallback); + } + }); +} diff --git a/packages/solid-lang/src/index.ts b/packages/solid-lang/src/index.ts new file mode 100644 index 000000000..27689bda6 --- /dev/null +++ b/packages/solid-lang/src/index.ts @@ -0,0 +1,61 @@ +export { createLibrary, defineComponent } from "./library"; +export type { + ComponentGroup, + ComponentRenderProps, + ComponentRenderer, + DefinedComponent, + Library, + LibraryDefinition, + PromptOptions, + SubComponentOf, + ToolDescriptor, +} from "./library"; + +export { Renderer } from "./Renderer"; +export type { RendererProps } from "./Renderer"; + +export { + FormNameContext, + OpenUIContext, + useFormName, + useGetFieldValue, + useIsStreaming, + useOpenUI, + useRenderNode, + useSetDefaultValue, + useSetFieldValue, + useTriggerAction, +} from "./context"; +export type { ActionConfig, OpenUIContextValue } from "./context"; + +export { FormValidationContext, createFormValidation, useFormValidation } from "./validation"; +export type { FormValidationContextValue } from "./validation"; + +export { builtInValidators, parseRules, parseStructuredRules, validate } from "./validation"; +export type { ParsedRule, ValidatorFn } from "./validation"; + +export { ACTION_STEPS, BuiltinActionType } from "@openuidev/lang-core"; +export type { + ActionEvent, + ActionPlan, + ActionStep, + ElementNode, + OpenUIError, + ParseResult, +} from "@openuidev/lang-core"; + +export { createParser, createStreamingParser, generatePrompt } from "@openuidev/lang-core"; +export type { + ComponentPromptSpec, + EvaluationContext, + McpClientLike, + PromptSpec, + ReactiveAssign, + StateField, + ToolProvider, + ToolSpec, +} from "@openuidev/lang-core"; + +export { mergeStatements } from "@openuidev/lang-core"; + +export { ToolNotFoundError, extractToolResult, isReactiveAssign } from "@openuidev/lang-core"; diff --git a/packages/solid-lang/src/library.ts b/packages/solid-lang/src/library.ts new file mode 100644 index 000000000..496574399 --- /dev/null +++ b/packages/solid-lang/src/library.ts @@ -0,0 +1,46 @@ +import { + createLibrary as coreCreateLibrary, + defineComponent as coreDefineComponent, + type DefinedComponent as CoreDefinedComponent, + type Library as CoreLibrary, + type LibraryDefinition as CoreLibraryDefinition, + type ComponentRenderProps as CoreRenderProps, +} from "@openuidev/lang-core"; +import type { JSX } from "solid-js"; +import { z } from "zod"; + +export type { + ComponentGroup, + LibraryJSONSchema, + PromptOptions, + SubComponentOf, + ToolDescriptor, +} from "@openuidev/lang-core"; + +export type ComponentRenderProps

> = CoreRenderProps; + +export type ComponentRenderer

> = ( + props: ComponentRenderProps

, +) => JSX.Element; + +export type DefinedComponent = z.ZodObject> = CoreDefinedComponent< + T, + ComponentRenderer> +>; + +export type Library = CoreLibrary>; + +export type LibraryDefinition = CoreLibraryDefinition>; + +export function defineComponent>(config: { + name: string; + props: T; + description: string; + component: ComponentRenderer>; +}): DefinedComponent { + return coreDefineComponent>>(config); +} + +export function createLibrary(input: LibraryDefinition): Library { + return coreCreateLibrary>(input) as Library; +} diff --git a/packages/solid-lang/src/validation.ts b/packages/solid-lang/src/validation.ts new file mode 100644 index 000000000..8fdd9621b --- /dev/null +++ b/packages/solid-lang/src/validation.ts @@ -0,0 +1,85 @@ +import { + builtInValidators, + parseRules, + parseStructuredRules, + validate, + type ParsedRule, + type ValidatorFn, +} from "@openuidev/lang-core"; +import { createContext, useContext } from "solid-js"; + +export { builtInValidators, parseRules, parseStructuredRules, validate }; +export type { ParsedRule, ValidatorFn }; + +export interface FormValidationContextValue { + errors: Record; + validateField: (name: string, value: unknown, rules: ParsedRule[]) => boolean; + registerField: (name: string, rules: ParsedRule[], getValue: () => unknown) => void; + unregisterField: (name: string) => void; + validateForm: () => boolean; + clearFieldError: (name: string) => void; +} + +export const FormValidationContext = createContext(null); + +export function useFormValidation(): FormValidationContextValue | null { + return useContext(FormValidationContext); +} + +interface FieldRegistration { + rules: ParsedRule[]; + getValue: () => unknown; +} + +export function createFormValidation(): FormValidationContextValue { + let errors: Record = {}; + const fields: Record = {}; + + function validateField(name: string, value: unknown, rules: ParsedRule[]): boolean { + const error = validate(value, rules); + if (errors[name] !== error) { + errors = { ...errors, [name]: error }; + } + return !error; + } + + function registerField(name: string, rules: ParsedRule[], getValue: () => unknown): void { + fields[name] = { rules, getValue }; + } + + function unregisterField(name: string): void { + delete fields[name]; + } + + function validateForm(): boolean { + let allValid = true; + const newErrors: Record = {}; + + for (const [name, reg] of Object.entries(fields)) { + const value = reg.getValue(); + const error = validate(value, reg.rules); + newErrors[name] = error; + if (error) allValid = false; + } + + errors = newErrors; + return allValid; + } + + function clearFieldError(name: string): void { + if (errors[name] !== undefined) { + errors = { ...errors, [name]: undefined }; + } + } + + return { + get errors() { + return errors; + }, + validateField, + registerField, + unregisterField, + validateForm, + clearFieldError, + }; +} diff --git a/packages/solid-lang/tsconfig.json b/packages/solid-lang/tsconfig.json new file mode 100644 index 000000000..994fae661 --- /dev/null +++ b/packages/solid-lang/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "exclude": ["src/**/__tests__/**", "src/**/*.test.ts", "src/**/*.test.tsx"], + "compilerOptions": { + "moduleResolution": "bundler", + "module": "ESNext", + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "jsx": "preserve", + "jsxImportSource": "solid-js" + } +} diff --git a/packages/solid-lang/tsconfig.test.json b/packages/solid-lang/tsconfig.test.json new file mode 100644 index 000000000..60b3001d5 --- /dev/null +++ b/packages/solid-lang/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/solid-lang/vite.config.ts b/packages/solid-lang/vite.config.ts new file mode 100644 index 000000000..9530d0507 --- /dev/null +++ b/packages/solid-lang/vite.config.ts @@ -0,0 +1,10 @@ +import solid from "vite-plugin-solid"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [solid()], + test: { + include: ["src/**/*.test.{ts,tsx}"], + environment: "jsdom", + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0d64cbad..6d8d6f788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,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.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)) fumadocs-ui: specifier: 16.6.5 version: 16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1) @@ -204,7 +204,7 @@ importers: version: 0.0.45 '@ag-ui/mastra': specifier: ^1.0.1 - version: 1.0.1(ypgyhrljo5kobpqehkhvtoi6ry) + version: 1.0.1(f492161cd3b3b3348703189a1f50f2a4) '@mastra/core': specifier: 1.15.0 version: 1.15.0(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@3.25.76))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@3.25.76) @@ -1285,6 +1285,40 @@ importers: specifier: ^5.0.0 version: 5.4.19(@types/node@22.15.32)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0) + packages/solid-lang: + dependencies: + '@openuidev/lang-core': + specifier: workspace:^ + version: link:../lang-core + zod: + specifier: ^4.0.0 + version: 4.3.6 + devDependencies: + '@solidjs/testing-library': + specifier: ^0.8.10 + version: 0.8.10(solid-js@1.9.12) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + jsdom: + specifier: ^26.1.0 + version: 26.1.0 + solid-js: + specifier: ^1.9.0 + version: 1.9.12 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + vite: + specifier: ^6.0.0 + version: 6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + vite-plugin-solid: + specifier: ^2.11.6 + version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.2)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + packages/svelte-lang: dependencies: '@openuidev/lang-core': @@ -1610,10 +1644,6 @@ packages: resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.5': - resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} @@ -1655,8 +1685,8 @@ packages: resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + '@babel/helper-module-imports@7.18.6': + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.28.6': @@ -3301,89 +3331,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -3785,48 +3831,56 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-gnu@16.1.6': resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@15.5.12': resolution: {integrity: sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@15.5.12': resolution: {integrity: sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@15.5.12': resolution: {integrity: sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@15.5.12': resolution: {integrity: sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==} @@ -4177,48 +4231,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-C3zapJconWpl2Y7LR3GkRkH6jxpuV2iVUfkFcHT5Ffn4Zu7l88mZa2dhcfdULZDybN1Phka/P34YUzuskUUrXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-minify/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-2T/Bm+3/qTfuNS4gKSzL8qbiYk+ErHW2122CtDx+ilZAzvWcJ8IbqdZIbEWOlwwe03lESTxPwTBLFqVgQU2OeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-MKLjpldYkeoB4T+yAi4aIAb0waifxUjLcKkCUDmYAY3RqBJTvWK34KtfaKZL0IBMIXfD92CbKkcxQirDUS9Xcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-UFVcbPvKUStry6JffriobBp8BHtjmLLPl4bCY+JMxIn/Q3pykCpZzRwFTcDurG/kY8tm+uSNfKKdRNa5Nh9A7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-minify/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-B9GyPQ1NKbvpETVAMyJMfRlD3c6UJ7kiuFUAlx9LTYiQL+YIyT6vpuRlq1zgsXxavZluVrfeJv6x0owV4KDx4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-fXfhtr+WWBGNy4M5GjAF5vu/lpulR4Me34FjTyaK9nDrTZs7LM595UDsP1wliksqp4hD/KdoqHGmbCrC+6d4vA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-minify/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-jFBgGbx1oLadb83ntJmy1dWlAHSQanXTS21G4PgkxyONmxZdZ/UMKr7KsADzMuoPsd2YhJHxzRpwJd9U+4BFBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-minify/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-nxPd9vx1vYz8IlIMdl9HFdOK/ood1H5hzbSFsyO8JU55tkcJoBL8TLCbuFf9pHpOy27l2gcPyV6z3p4eAcTH5Q==} @@ -4296,48 +4358,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-QagKTDF4lrz8bCXbUi39Uq5xs7C7itAseKm51f33U+Dyar9eJY/zGKqfME9mKLOiahX7Fc1J3xMWVS0AdDXLPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-RPddpcE/0xxWaommWy0c5i/JdrXcXAkxBS2GOrAUh5LKmyCh03hpJedOAWszG4ADsKQwoUQQ1/tZVGRhZIWtKA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-ur/WVZF9FSOiZGxyP+nfxZzuv6r5OJDYoVxJnUR7fM/hhXLh4V/be6rjbzm9KLCDBRwYCEKJtt+XXNccwd06IA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-ujGcAx8xAMvhy7X5sBFi3GXML1EtyORuJZ5z2T6UV3U416WgDX/4OCi3GnoteeenvxIf6JgP45B+YTHpt71vpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-hbsfKjUwRjcMZZvvmpZSc+qS0bHcHRu8aV/I3Ikn9BzOA0ZAgUE7ctPtce5zCU7bM8dnTLi4sJ1Pi9YHdx6Urw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-1QrTrf8rige7UPJrYuDKJLQOuJlgkt+nRSJLBMHWNm9TdivzP48HaK3f4q18EjNlglKtn03lgjMu4fryDm8X4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-gRvK6HPzF5ITRL68fqb2WYYs/hGviPIbkV84HWCgiJX+LkaOpp+HIHQl3zVZdyKHwopXToTbXbtx/oFjDjl8pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-parser/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-QPJvFbnnDZZY7xc+xpbIBWLThcGBakwaYA9vKV8b3+oS5MGfAZUoTFJcix5+Zg2Ri46sOfrUim6Y6jsKNcssAQ==} @@ -4418,48 +4488,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-arm64-musl@0.117.0': resolution: {integrity: sha512-ykxpPQp0eAcSmhy0Y3qKvdanHY4d8THPonDfmCoktUXb6r0X6qnjpJB3V+taN1wevW55bOEZd97kxtjTKjqhmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-transform/binding-linux-ppc64-gnu@0.117.0': resolution: {integrity: sha512-Rvspti4Kr7eq6zSrURK5WjscfWQPvmy/KjJZV45neRKW8RLonE3r9+NgrwSLGoHvQ3F24fbqlkplox1RtlhH5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-riscv64-gnu@0.117.0': resolution: {integrity: sha512-Dr2ZW9ZZ4l1eQ5JUEUY3smBh4JFPCPuybWaDZTLn3ADZjyd8ZtNXEjeMT8rQbbhbgSL9hEgbwaqraole3FNThQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-riscv64-musl@0.117.0': resolution: {integrity: sha512-oD1Bnes1bIC3LVBSrWEoSUBj6fvatESPwAVWfJVGVQlqWuOs/ZBn1e4Nmbipo3KGPHK7DJY75r/j7CQCxhrOFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-transform/binding-linux-s390x-gnu@0.117.0': resolution: {integrity: sha512-qT//IAPLvse844t99Kff5j055qEbXfwzWgvCMb0FyjisnB8foy25iHZxZIocNBe6qwrCYWUP1M8rNrB/WyfS1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-gnu@0.117.0': resolution: {integrity: sha512-2YEO5X+KgNzFqRVO5dAkhjcI5gwxus4NSWVl/+cs2sI6P0MNPjqE3VWPawl4RTC11LvetiiZdHcujUCPM8aaUw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-transform/binding-linux-x64-musl@0.117.0': resolution: {integrity: sha512-3wqWbTSaIFZvDr1aqmTul4cg8PRWYh6VC52E8bLI7ytgS/BwJLW+sDUU2YaGIds4sAf/1yKeJRmudRCDPW9INg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-transform/binding-openharmony-arm64@0.117.0': resolution: {integrity: sha512-Ebxx6NPqhzlrjvx4+PdSqbOq+li0f7X59XtJljDghkbJsbnkHvhLmPR09ifHt5X32UlZN63ekjwcg/nbmHLLlA==} @@ -4519,36 +4597,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-wasm@2.5.6': resolution: {integrity: sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==} @@ -6570,121 +6654,145 @@ packages: resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-gnueabihf@4.60.1': resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.43.0': resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.43.0': resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.43.0': resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.43.0': resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.43.0': resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.43.0': resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.43.0': resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.43.0': resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.43.0': resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.43.0': resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} @@ -6809,6 +6917,16 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@solidjs/testing-library@0.8.10': + resolution: {integrity: sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ==} + engines: {node: '>= 14'} + peerDependencies: + '@solidjs/router': '>=0.9.0' + solid-js: '>=1.0.0' + peerDependenciesMeta: + '@solidjs/router': + optional: true + '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} @@ -7183,48 +7301,56 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.1': resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.1': resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.1': resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.1': resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} @@ -7307,24 +7433,28 @@ packages: engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@takumi-rs/core-linux-arm64-musl@0.68.17': resolution: {integrity: sha512-4CiEF518wDnujF0fjql2XN6uO+OXl0svy0WgAF2656dCx2gJtWscaHytT2rsQ0ZmoFWE0dyWcDW1g/FBVPvuvA==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@takumi-rs/core-linux-x64-gnu@0.68.17': resolution: {integrity: sha512-jm8lTe2E6Tfq2b97GJC31TWK1JAEv+MsVbvL9DCLlYcafgYFlMXDUnOkZFMjlrmh0HcFAYDaBkniNDgIQfXqzg==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@takumi-rs/core-linux-x64-musl@0.68.17': resolution: {integrity: sha512-nbdzQgC4ywzltDDV1fer1cKswwGE+xXZHdDiacdd7RM5XBng209Bmo3j1iv9dsX+4xXhByzCCGbxdWhhHqVXmw==} engines: {node: '>= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@takumi-rs/core-win32-arm64-msvc@0.68.17': resolution: {integrity: sha512-kE4F0LRmuhSwiNkFG7dTY9ID8+B7zb97QedyN/IO2fBJmRQDkqCGcip2gloh8YPPhCuKGjCqqqh2L+Tg9PKW7w==} @@ -7370,6 +7500,10 @@ packages: resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@testing-library/svelte-core@1.0.0': resolution: {integrity: sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==} engines: {node: '>=16'} @@ -7794,41 +7928,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -8445,6 +8587,11 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + babel-plugin-jsx-dom-expressions@0.40.6: + resolution: {integrity: sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==} + peerDependencies: + '@babel/core': ^7.20.12 + babel-plugin-polyfill-corejs2@0.4.16: resolution: {integrity: sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==} peerDependencies: @@ -8498,6 +8645,15 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + babel-preset-solid@1.9.12: + resolution: {integrity: sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg==} + peerDependencies: + '@babel/core': ^7.0.0 + solid-js: ^1.9.12 + peerDependenciesMeta: + solid-js: + optional: true + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -10745,6 +10901,9 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-entities@2.3.3: + resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} + html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -11115,6 +11274,10 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -11480,48 +11643,56 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.31.1: resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.31.1: resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.31.1: resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.31.1: resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} @@ -11811,6 +11982,10 @@ packages: memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} + merge-anything@5.1.7: + resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} + engines: {node: '>=12.13'} + merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -13751,6 +13926,12 @@ packages: resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} engines: {node: '>=20.0.0'} + seroval-plugins@1.5.2: + resolution: {integrity: sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + seroval@1.5.1: resolution: {integrity: sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==} engines: {node: '>=10'} @@ -13868,6 +14049,14 @@ packages: resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} engines: {node: '>=20.0.0'} + solid-js@1.9.12: + resolution: {integrity: sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw==} + + solid-refresh@0.6.3: + resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} + peerDependencies: + solid-js: ^1.3 + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -14886,6 +15075,16 @@ packages: '@nuxt/kit': optional: true + vite-plugin-solid@2.11.12: + resolution: {integrity: sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA==} + peerDependencies: + '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* + solid-js: ^1.7.2 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@testing-library/jest-dom': + optional: true + vite-plugin-vue-tracer@1.3.0: resolution: {integrity: sha512-Cgfce6VikzOw5MUJTpeg50s5rRjzU1Vr61ZjuHunVVHLjZZ5AUlgyExHthZ3r59vtoz9W2rDt23FYG81avYBKw==} peerDependencies: @@ -15294,18 +15493,6 @@ packages: utf-8-validate: optional: true - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.20.0: resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} @@ -15532,12 +15719,12 @@ snapshots: '@ag-ui/core': 0.0.49 '@ag-ui/proto': 0.0.49 - '@ag-ui/langgraph@0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@ag-ui/langgraph@0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 - '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)) - '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)) + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) partial-json: 0.1.7 rxjs: 7.8.1 transitivePeerDependencies: @@ -15548,12 +15735,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@1.0.1(ypgyhrljo5kobpqehkhvtoi6ry)': + '@ag-ui/mastra@1.0.1(f492161cd3b3b3348703189a1f50f2a4)': dependencies: '@ag-ui/client': 0.0.49 '@ag-ui/core': 0.0.45 '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) - '@copilotkit/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.49)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.46)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@mastra/client-js': 1.11.2(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@3.25.76))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@3.25.76) '@mastra/core': 1.15.0(@cfworker/json-schema@4.1.1)(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@3.25.76))(@types/json-schema@7.0.15)(openapi-types@12.1.3)(zod@3.25.76) rxjs: 7.8.1 @@ -15810,14 +15997,14 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 + '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) '@babel/helpers': 7.27.6 '@babel/parser': 7.27.5 '@babel/template': 7.27.2 '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 convert-source-map: 2.0.0 debug: 4.4.3 gensync: 1.0.0-beta.2 @@ -15846,14 +16033,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.27.5': - dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - '@babel/generator@7.29.1': dependencies: '@babel/parser': 7.29.0 @@ -15922,12 +16101,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.27.1': + '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.29.0 '@babel/helper-module-imports@7.28.6': dependencies: @@ -15939,7 +16115,7 @@ snapshots: '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': dependencies: '@babel/core': 7.27.4 - '@babel/helper-module-imports': 7.27.1 + '@babel/helper-module-imports': 7.28.6 '@babel/helper-validator-identifier': 7.27.1 '@babel/traverse': 7.27.4 transitivePeerDependencies: @@ -16004,7 +16180,7 @@ snapshots: '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@babel/helpers@7.28.6': dependencies: @@ -16020,7 +16196,7 @@ snapshots: '@babel/parser@7.27.5': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@babel/parser@7.29.0': dependencies: @@ -16447,7 +16623,7 @@ snapshots: dependencies: '@babel/code-frame': 7.27.1 '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@babel/template@7.28.6': dependencies: @@ -16458,10 +16634,10 @@ snapshots: '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.5 + '@babel/generator': 7.29.1 '@babel/parser': 7.27.5 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: @@ -16546,19 +16722,19 @@ snapshots: '@cloudflare/kv-asset-handler@0.4.2': {} - '@copilotkit/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.49)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@copilotkit/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/encoder@0.0.46)(@cfworker/json-schema@4.1.1)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 - '@ag-ui/langgraph': 0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ag-ui/langgraph': 0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@ai-sdk/anthropic': 2.0.71(zod@3.25.76) '@ai-sdk/openai': 2.0.101(zod@3.25.76) '@copilotkit/shared': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/core@0.0.46) '@copilotkitnext/agent': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@cfworker/json-schema@4.1.1) - '@copilotkitnext/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.49)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603) + '@copilotkitnext/runtime': 0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.46)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603) '@graphql-yoga/plugin-defer-stream': 3.19.0(graphql-yoga@5.19.0(graphql@16.13.2))(graphql@16.13.2) '@hono/node-server': 1.19.12(hono@4.12.9) - '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)) '@scarf/scarf': 1.4.0 ai: 5.0.161(zod@3.25.76) class-transformer: 0.5.1 @@ -16575,8 +16751,8 @@ snapshots: type-graphql: 2.0.0-rc.1(class-validator@0.14.4)(graphql-scalars@1.25.0(graphql@16.13.2))(graphql@16.13.2) zod: 3.25.76 optionalDependencies: - '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - openai: 4.104.0(zod@3.25.76) + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + openai: 4.104.0(ws@8.20.0)(zod@3.25.76) transitivePeerDependencies: - '@ag-ui/encoder' - '@cfworker/json-schema' @@ -16615,11 +16791,11 @@ snapshots: - '@cfworker/json-schema' - supports-color - '@copilotkitnext/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.49)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)': + '@copilotkitnext/runtime@0.0.0-mme-ag-ui-0-0-46-20260227141603(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@ag-ui/encoder@0.0.46)(@copilotkitnext/shared@0.0.0-mme-ag-ui-0-0-46-20260227141603)': dependencies: '@ag-ui/client': 0.0.46 '@ag-ui/core': 0.0.46 - '@ag-ui/encoder': 0.0.49 + '@ag-ui/encoder': 0.0.46 '@copilotkitnext/shared': 0.0.0-mme-ag-ui-0-0-46-20260227141603 cors: 2.8.6 express: 4.22.1 @@ -17158,7 +17334,7 @@ snapshots: terminal-link: 2.1.1 undici: 6.24.1 wrap-ansi: 7.0.0 - ws: 8.18.2 + ws: 8.20.0 optionalDependencies: react-native: 0.83.2(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0) transitivePeerDependencies: @@ -17924,14 +18100,14 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} - '@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76))': + '@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -17944,14 +18120,14 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -22515,6 +22691,11 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@solidjs/testing-library@0.8.10(solid-js@1.9.12)': + dependencies: + '@testing-library/dom': 10.4.0 + solid-js: 1.9.12 + '@speed-highlight/core@1.2.15': {} '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@3.25.76))(zod@3.25.76)': @@ -23056,6 +23237,15 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.3 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + '@testing-library/svelte-core@1.0.0(svelte@5.55.1)': dependencies: svelte: 5.55.1 @@ -23085,23 +23275,23 @@ snapshots: '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.7 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.29.0 '@types/body-parser@1.19.6': dependencies: @@ -24350,6 +24540,15 @@ snapshots: '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.7 + babel-plugin-jsx-dom-expressions@0.40.6(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.18.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + html-entities: 2.3.3 + parse5: 7.3.0 + babel-plugin-polyfill-corejs2@0.4.16(@babel/core@7.29.0): dependencies: '@babel/compat-data': 7.29.0 @@ -24451,6 +24650,13 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-solid@1.9.12(@babel/core@7.29.0)(solid-js@1.9.12): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jsx-dom-expressions: 0.40.6(@babel/core@7.29.0) + optionalDependencies: + solid-js: 1.9.12 + bail@2.0.2: {} balanced-match@1.0.2: {} @@ -25881,7 +26087,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: @@ -25903,7 +26109,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 @@ -26611,7 +26817,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.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 @@ -27123,6 +27329,8 @@ snapshots: - '@noble/hashes' optional: true + html-entities@2.3.3: {} + html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -27477,6 +27685,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-what@4.1.16: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -27669,7 +27879,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.2 + ws: 8.20.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -27777,7 +27987,7 @@ snapshots: vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.1.0 - langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(zod@3.25.76)): + langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.20.0)(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 @@ -27788,7 +27998,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 4.104.0(zod@3.25.76) + openai: 4.104.0(ws@8.20.0)(zod@3.25.76) language-subtag-registry@0.3.23: {} @@ -28297,6 +28507,10 @@ snapshots: dependencies: map-or-similar: 1.5.0 + merge-anything@5.1.7: + dependencies: + is-what: 4.1.16 + merge-descriptors@1.0.3: {} merge-descriptors@2.0.0: {} @@ -29602,7 +29816,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.104.0(ws@8.20.0)(zod@4.3.6): + openai@4.104.0(ws@8.20.0)(zod@3.25.76): dependencies: '@types/node': 18.19.130 '@types/node-fetch': 2.6.11 @@ -29613,11 +29827,12 @@ snapshots: node-fetch: 2.7.0 optionalDependencies: ws: 8.20.0 - zod: 4.3.6 + zod: 3.25.76 transitivePeerDependencies: - encoding + optional: true - openai@4.104.0(zod@3.25.76): + openai@4.104.0(ws@8.20.0)(zod@4.3.6): dependencies: '@types/node': 18.19.130 '@types/node-fetch': 2.6.11 @@ -29627,10 +29842,10 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - zod: 3.25.76 + ws: 8.20.0 + zod: 4.3.6 transitivePeerDependencies: - encoding - optional: true openai@6.22.0(ws@8.20.0)(zod@4.3.6): optionalDependencies: @@ -31437,6 +31652,10 @@ snapshots: serialize-javascript@7.0.5: {} + seroval-plugins@1.5.2(seroval@1.5.1): + dependencies: + seroval: 1.5.1 + seroval@1.5.1: {} serve-placeholder@2.0.2: @@ -31610,6 +31829,21 @@ snapshots: smob@1.6.1: {} + solid-js@1.9.12: + dependencies: + csstype: 3.2.3 + seroval: 1.5.1 + seroval-plugins: 1.5.2(seroval@1.5.1) + + solid-refresh@0.6.3(solid-js@1.9.12): + dependencies: + '@babel/generator': 7.29.1 + '@babel/helper-module-imports': 7.28.6 + '@babel/types': 7.29.0 + solid-js: 1.9.12 + transitivePeerDependencies: + - supports-color + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -32759,6 +32993,21 @@ snapshots: transitivePeerDependencies: - supports-color + vite-plugin-solid@2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)): + dependencies: + '@babel/core': 7.29.0 + '@types/babel__core': 7.20.5 + babel-preset-solid: 1.9.12(@babel/core@7.29.0)(solid-js@1.9.12) + merge-anything: 5.1.7 + solid-js: 1.9.12 + solid-refresh: 0.6.3(solid-js@1.9.12) + vite: 6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + vitefu: 1.1.2(vite@6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)) + optionalDependencies: + '@testing-library/jest-dom': 6.9.1 + transitivePeerDependencies: + - supports-color + vite-plugin-vue-tracer@1.3.0(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3))(vue@3.5.31(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 @@ -32786,8 +33035,8 @@ snapshots: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.43.0 + postcss: 8.5.8 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.3.2 @@ -32801,11 +33050,11 @@ snapshots: vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.8 - rollup: 4.43.0 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.3.2 @@ -33145,8 +33394,6 @@ snapshots: ws@7.5.10: {} - ws@8.18.2: {} - ws@8.20.0: {} wsl-utils@0.1.0: From 2b0843864d5880dd5682b8a581a6a81fcb7603d8 Mon Sep 17 00:00:00 2001 From: Nizar Izzuddin Yatim Fadlan Date: Tue, 7 Apr 2026 13:47:50 +0700 Subject: [PATCH 2/3] Add solid-chat example with resilient streaming UI --- README.md | 38 +- examples/solid-chat/.env.example | 3 + examples/solid-chat/.gitignore | 3 + examples/solid-chat/README.md | 54 + .../solid-chat/generated/system-prompt.txt | 126 +++ examples/solid-chat/index.html | 12 + examples/solid-chat/package.json | 26 + .../solid-chat/scripts/generate-prompt.mjs | 186 ++++ examples/solid-chat/src/App.tsx | 287 +++++ examples/solid-chat/src/components/Badge.tsx | 37 + examples/solid-chat/src/components/Button.tsx | 51 + examples/solid-chat/src/components/Card.tsx | 105 ++ examples/solid-chat/src/components/Chart.tsx | 101 ++ .../solid-chat/src/components/KpiTile.tsx | 40 + .../solid-chat/src/components/MetricList.tsx | 61 ++ examples/solid-chat/src/components/Stack.tsx | 67 ++ .../solid-chat/src/components/TextContent.tsx | 25 + .../solid-chat/src/components/Timeline.tsx | 58 + examples/solid-chat/src/lib/library.tsx | 176 ++++ examples/solid-chat/src/main.tsx | 4 + examples/solid-chat/src/vite-env.d.ts | 1 + examples/solid-chat/tsconfig.json | 12 + examples/solid-chat/vite.config.ts | 144 +++ pnpm-lock.yaml | 987 +++++++++++++++++- 24 files changed, 2573 insertions(+), 31 deletions(-) create mode 100644 examples/solid-chat/.env.example create mode 100644 examples/solid-chat/.gitignore create mode 100644 examples/solid-chat/README.md create mode 100644 examples/solid-chat/generated/system-prompt.txt create mode 100644 examples/solid-chat/index.html create mode 100644 examples/solid-chat/package.json create mode 100644 examples/solid-chat/scripts/generate-prompt.mjs create mode 100644 examples/solid-chat/src/App.tsx create mode 100644 examples/solid-chat/src/components/Badge.tsx create mode 100644 examples/solid-chat/src/components/Button.tsx create mode 100644 examples/solid-chat/src/components/Card.tsx create mode 100644 examples/solid-chat/src/components/Chart.tsx create mode 100644 examples/solid-chat/src/components/KpiTile.tsx create mode 100644 examples/solid-chat/src/components/MetricList.tsx create mode 100644 examples/solid-chat/src/components/Stack.tsx create mode 100644 examples/solid-chat/src/components/TextContent.tsx create mode 100644 examples/solid-chat/src/components/Timeline.tsx create mode 100644 examples/solid-chat/src/lib/library.tsx create mode 100644 examples/solid-chat/src/main.tsx create mode 100644 examples/solid-chat/src/vite-env.d.ts create mode 100644 examples/solid-chat/tsconfig.json create mode 100644 examples/solid-chat/vite.config.ts diff --git a/README.md b/README.md index 5a4b7bf3e..6feceec50 100644 --- a/README.md +++ b/README.md @@ -12,18 +12,12 @@ - OpenUI is a full-stack Generative UI framework — a compact streaming-first language, a React runtime with built-in component libraries, and ready-to-use chat interfaces — that is up to 67% more token-efficient than JSON. - - --- - - [Docs](https://openui.com) · [Playground](https://www.openui.com/playground) · [Sample Chat App](./examples/openui-chat) · [Discord](https://discord.com/invite/Pbv5PsqUSv) · [Contributing](./CONTRIBUTING.md) · [Code of Conduct](./CODE_OF_CONDUCT.md) · [Security](./SECURITY.md) · [License](./LICENSE) - --- ## What is OpenUI @@ -43,7 +37,6 @@ At the center of OpenUI is **OpenUI Lang**: a compact, streaming-first language - **Streaming renderer** — Parse and render model output progressively in React as tokens arrive. - **Chat and app surfaces** - Use the same foundation for assistants, copilots, and broader interactive product flows. - ## Quick Start ```bash @@ -62,8 +55,6 @@ What this gives you: - **Streaming support** - Update the UI progressively as output arrives. - **Working app foundation** - Start from a ready-to-run example instead of wiring everything manually. - - ## How it works Your components define what the model can generate. @@ -87,13 +78,13 @@ Try it yourself in the [Playground](https://www.openui.com/playground) — gener ## Packages -| Package | Description | -| :--- | :--- | -| [`@openuidev/react-lang`](./packages/react-lang) | Core runtime — component definitions, parser, renderer, prompt generation | -| [`@openuidev/solid-lang`](./packages/solid-lang) | SolidJS runtime — component definitions, parser, renderer, prompt generation | -| [`@openuidev/react-headless`](./packages/react-headless) | Headless chat state, streaming adapters, message format converters | -| [`@openuidev/react-ui`](./packages/react-ui) | Prebuilt chat layouts and two built-in component libraries | -| [`@openuidev/cli`](./packages/openui-cli) | CLI for scaffolding apps and generating system prompts | +| Package | Description | +| :------------------------------------------------------- | :--------------------------------------------------------------------------- | +| [`@openuidev/react-lang`](./packages/react-lang) | Core runtime — component definitions, parser, renderer, prompt generation | +| [`@openuidev/solid-lang`](./packages/solid-lang) | SolidJS runtime — component definitions, parser, renderer, prompt generation | +| [`@openuidev/react-headless`](./packages/react-headless) | Headless chat state, streaming adapters, message format converters | +| [`@openuidev/react-ui`](./packages/react-ui) | Prebuilt chat layouts and two built-in component libraries | +| [`@openuidev/cli`](./packages/openui-cli) | CLI for scaffolding apps and generating system prompts | ```bash npm install @openuidev/react-lang @openuidev/react-ui @@ -142,7 +133,8 @@ openui/ ├── skills/ │ └── openui/ # Claude Code skill for AI-assisted development ├── examples/ -│ └── openui-chat/ # Full working example app (Next.js) +│ ├── openui-chat/ # Full working example app (Next.js) +│ └── solid-chat/ # SolidJS chat example with @openuidev/solid-lang ├── docs/ # Documentation site (openui.com) └── benchmarks/ # Token efficiency benchmarks ``` @@ -151,6 +143,7 @@ Good places to start: - [openui.com](https://openui.com) for the full docs - [`examples/openui-chat`](./examples/openui-chat) for a working app +- [`examples/solid-chat`](./examples/solid-chat) for SolidJS runtime usage - [`CONTRIBUTING.md`](./CONTRIBUTING.md) if you want to contribute ## Community @@ -158,25 +151,24 @@ Good places to start: - [Discord](https://discord.com/invite/Pbv5PsqUSv) — Ask questions, share what you're building - [GitHub Issues](https://github.com/thesysdev/openui/issues) — Report bugs or request features - ## Contributing Contributions are welcome. See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for contribution guidelines and ways to get involved. ## Agent Skill - + OpenUI ships an [Agent Skill](https://agentskills.io) so AI coding assistants (Claude Code, Codex, Cursor, Copilot, etc.) can help you scaffold, build, and debug Generative UI apps using OpenUI Lang. - + ### Install - + ```bash # With the skills CLI (works across all agents) npx skills add thesysdev/openui --skill openui - + # Manual — copy into your project cp -r skills/openui .claude/skills/openui ``` - + The skill covers component library design, OpenUI Lang syntax, system prompt generation, the Renderer, SDK packages, and debugging malformed LLM output. ## License diff --git a/examples/solid-chat/.env.example b/examples/solid-chat/.env.example new file mode 100644 index 000000000..f73f4e388 --- /dev/null +++ b/examples/solid-chat/.env.example @@ -0,0 +1,3 @@ +OPENAI_BASE_URL=http://localhost:11434/v1 +OPENAI_MODEL=llama3.1:8b +OPENAI_API_KEY=ollama diff --git a/examples/solid-chat/.gitignore b/examples/solid-chat/.gitignore new file mode 100644 index 000000000..9c97bbd46 --- /dev/null +++ b/examples/solid-chat/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +.env diff --git a/examples/solid-chat/README.md b/examples/solid-chat/README.md new file mode 100644 index 000000000..4dd6ce6b0 --- /dev/null +++ b/examples/solid-chat/README.md @@ -0,0 +1,54 @@ +# OpenUI Solid Chat + +Example chat app for `@openuidev/solid-lang` using a real AI model (default: Ollama/OpenAI-compatible API), Ark UI Field input, and ECharts-based visualizations. + +## Goals + +- Show how to define OpenUI components for Solid. +- Show how to render OpenUI Lang responses with `Renderer`. +- Show the action loop from components (for example, `Button`) back into the chat flow. + +## Run + +From the monorepo root: + +```bash +pnpm install +cp examples/solid-chat/.env.example examples/solid-chat/.env +pnpm --filter solid-chat generate:prompt +pnpm --filter solid-chat dev +``` + +Then open `http://localhost:5174`. + +## Model Configuration + +- Default (`.env.example`) uses local Ollama: + +```bash +OPENAI_BASE_URL=http://localhost:11434/v1 +OPENAI_MODEL=llama3.1:8b +OPENAI_API_KEY=ollama +``` + +- To use OpenAI cloud, change to: + +```bash +OPENAI_BASE_URL=https://api.openai.com/v1 +OPENAI_MODEL=gpt-4.1 +OPENAI_API_KEY=sk-... +``` + +## Notes + +- The `/api/chat` endpoint is implemented directly in `vite.config.ts` via dev middleware (fast demo setup). +- OpenUI components live in `src/components` and are registered in `src/lib/library.tsx`. +- Chat input uses `@ark-ui/solid` (`Field`). +- The `Chart` component uses `echarts` (bar/line/pie/doughnut). +- The system prompt is generated into `examples/solid-chat/generated/system-prompt.txt` for easier review and iteration. + +## Example Dashboard Prompts + +- `Build a weekly SaaS business dashboard with KPIs, revenue trend, and channel mix charts` +- `Create a support operations dashboard with ticket volume, SLA trend, and follow-up actions` +- `Create a Q1 executive dashboard with 3 metric cards and 2 charts` diff --git a/examples/solid-chat/generated/system-prompt.txt b/examples/solid-chat/generated/system-prompt.txt new file mode 100644 index 000000000..5c171e1c0 --- /dev/null +++ b/examples/solid-chat/generated/system-prompt.txt @@ -0,0 +1,126 @@ +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 = Stack(...)` +3. Expressions are: strings ("..."), numbers, booleans (true/false), null, arrays ([...]), objects ({...}), or component calls TypeName(arg1, arg2, ...) +4. Use references for readability: define `name = ...` on one line, then use `name` later +5. EVERY variable (except root) MUST be referenced by at least one other variable. Unreferenced variables are silently dropped and will NOT render. Always include defined variables in their parent's children/items array. +6. Arguments are POSITIONAL (order matters, not names). Write `Stack([children], "row", "l")` NOT `Stack([children], direction: "row", gap: "l")` — colon syntax is NOT supported and silently breaks +7. Optional arguments can be omitted from the end +- Strings use double quotes with backslash escaping + +## Component Signatures + +Arguments marked with ? are optional. Sub-components can be inline or referenced; prefer references for better streaming. +Props typed `ActionExpression` accept an Action([@steps...]) expression. See the Action section for available steps (@ToAssistant, @OpenUrl). +Props marked `$binding` accept a `$variable` reference for two-way binding. + +TextContent(text: string, tone?: "normal" | "muted" | "strong") — Displays a block of text. +Button(label: string, action?: string, variant?: "primary" | "secondary" | "ghost") — A clickable button. +Chart(title: string, type: "bar" | "line" | "pie" | "doughnut", labels: string[], values: number[], datasetLabel?: string) — Renders a chart. +Badge(label: string, tone?: "neutral" | "success" | "warning" | "danger" | "info") — Small status pill. +KpiTile(label: string, value: string, delta?: string, trend?: "up" | "down" | "neutral") — Compact KPI tile with value and trend. +MetricList(title: string, items: {label: string, value: string}[]) — Two-column labeled metric list. +Timeline(title: string, items: {title: string, detail: string, status?: "done" | "active" | "next"}[]) — Progress timeline with status dots. +Card(title: string, children: (TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline)[], subtitle?: string, variant?: "default" | "glass" | "accent", highlight?: string) — Card container with title and children. +Stack(children: (Card | TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline)[]) — Dashboard layout container. Use as root. + +## Built-in Functions + +Data functions prefixed with `@` to distinguish from components. These are the ONLY functions available — do NOT invent new ones. +Use @-prefixed built-in functions (@Count, @Sum, @Avg, @Min, @Max, @Round) on Query results — do NOT hardcode computed values. + +@Count(array) → number — Returns array length +@First(array) → element — Returns first element of array +@Last(array) → element — Returns last element of array +@Sum(numbers[]) → number — Sum of numeric array +@Avg(numbers[]) → number — Average of numeric array +@Min(numbers[]) → number — Minimum value in array +@Max(numbers[]) → number — Maximum value in array +@Sort(array, field, direction?) → sorted array — Sort array by field. Direction: "asc" (default) or "desc" +@Filter(array, field, operator: "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains", value) → filtered array — Filter array by field value +@Round(number, decimals?) → number — Round to N decimal places (default 0) +@Abs(number) → number — Absolute value +@Floor(number) → number — Round down to nearest integer +@Ceil(number) → number — Round up to nearest integer +@Each(array, varName, template) — Evaluate template for each element. varName is the loop variable — use it ONLY inside the template expression (inline). Do NOT create a separate statement for the template. + +Builtins compose — output of one is input to the next: +`@Count(@Filter(data.rows, "field", "==", "val"))` for KPIs/chart values, `@Round(@Avg(data.rows.score), 1)`, `@Each(data.rows, "item", Comp(item.field))` for per-item rendering. +Array pluck: `data.rows.field` extracts a field from every row → use with @Sum, @Avg, charts, tables. + +IMPORTANT @Each rule: The loop variable (e.g. "item") is ONLY available inside the @Each template expression. Always inline the template — do NOT extract it to a separate statement. +CORRECT: `Col("Actions", @Each(rows, "t", Button("Edit", Action([@Set($id, t.id)]))))` +WRONG: `myBtn = Button("Edit", Action([@Set($id, t.id)]))` then `Col("Actions", @Each(rows, "t", myBtn))` — t is undefined in myBtn. + +## Action — Button Behavior + +Action([@steps...]) wires button clicks to operations. Steps are @-prefixed built-in actions. Steps execute in order. +Buttons without an explicit Action prop automatically send their label to the assistant (equivalent to Action([@ToAssistant(label)])). + +Available steps: +- @ToAssistant("message") — Send a message to the assistant (for conversational buttons like "Tell me more", "Explain this") +- @OpenUrl("https://...") — Navigate to a URL + +Example — simple nav: +``` +viewBtn = Button("View", Action([@OpenUrl("https://example.com")])) +``` + +- Action can be assigned to a variable or inlined: Button("Go", onSubmit) and Button("Go", Action([...])) both work + +## 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 = Stack(...)` — UI shell appears immediately +2. $variable declarations — state ready for bindings +3. Query statements — defaults resolve immediately so components render with data +4. Component definitions — fill in with data already available +5. Data values — leaf content last + +Always write the root = Stack(...) statement first so the UI shell appears immediately, even before child data has streamed in. + +## Examples + +User: Build a SaaS KPI dashboard + +root = Stack([title, healthy, m1, m2, m3, m4, mainCard, action1, action2]) +title = TextContent("Q2 Growth Pulse", "strong") +healthy = Badge("Healthy", "success") +m1 = KpiTile("MRR", "$128.4k", "+8.2% MoM", "up") +m2 = KpiTile("New Users", "1,248", "+12.6% WoW", "up") +m3 = KpiTile("Churn", "2.4%", "-0.3pp", "up") +m4 = KpiTile("NPS", "47", "+2", "up") +trend = Chart("Revenue Trend", "line", ["Jan", "Feb", "Mar", "Apr", "May"], [92, 98, 107, 119, 128], "$k") +mix = Chart("Traffic Sources", "pie", ["Organic", "Paid", "Referral", "Social"], [44, 28, 18, 10], "%") +ops = MetricList("Ops Snapshot", [{"label":"Avg response", "value":"4.9h"}, {"label":"Backlog", "value":"31"}]) +flow = Timeline("Delivery Steps", [{"title":"Design", "detail":"Completed", "status":"done"}, {"title":"Dev", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Planned", "status":"next"}]) +mainCard = Card("Executive Dashboard", [trend, mix, ops, flow], "Weekly status and channel mix", "glass", "Live") +action1 = Button("Show risk drivers", "continue-conversation", "primary") +action2 = Button("Compare with previous quarter", "continue-conversation", "ghost") + +## Important Rules +- When asked about data, generate realistic/plausible data +- Choose components that best represent the content (tables for comparisons, charts for trends, forms for input, etc.) + +## Final Verification +Before finishing, walk your output and verify: +1. root = Stack(...) is the FIRST line (for optimal streaming). +2. Every referenced name is defined. Every defined name (other than root) is reachable from root. + +- Always use Stack as the root component. +- Use Stack only as root. Never place Stack inside Card children. +- Output openui-lang code only. No markdown fences, no explanations, no think blocks. +- All visible text must be English. +- Use references for readability and better streaming behavior. +- For dashboards, include at least four KpiTile components and at least two charts. +- Prefer Card with subtitle and highlight for richer hierarchy. +- Use Badge, MetricList, and Timeline when relevant instead of plain text-only layouts. +- Use positional arguments only. +- Never output reasoning or markdown. \ No newline at end of file diff --git a/examples/solid-chat/index.html b/examples/solid-chat/index.html new file mode 100644 index 000000000..3c3613d5d --- /dev/null +++ b/examples/solid-chat/index.html @@ -0,0 +1,12 @@ + + + + + + OpenUI Solid Chat + + +

+ + + diff --git a/examples/solid-chat/package.json b/examples/solid-chat/package.json new file mode 100644 index 000000000..a89f9832c --- /dev/null +++ b/examples/solid-chat/package.json @@ -0,0 +1,26 @@ +{ + "name": "solid-chat", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "generate:prompt": "node scripts/generate-prompt.mjs" + }, + "dependencies": { + "@ark-ui/solid": "^5.35.0", + "echarts": "^5.6.0", + "@openuidev/lang-core": "workspace:*", + "lucide-solid": "^0.542.0", + "@openuidev/solid-lang": "workspace:*", + "solid-js": "^1.9.9", + "zod": "^4.3.6" + }, + "devDependencies": { + "jiti": "^2.6.1", + "typescript": "^5.9.2", + "vite": "^6.4.1", + "vite-plugin-solid": "^2.11.8" + } +} diff --git a/examples/solid-chat/scripts/generate-prompt.mjs b/examples/solid-chat/scripts/generate-prompt.mjs new file mode 100644 index 000000000..c880c8fdd --- /dev/null +++ b/examples/solid-chat/scripts/generate-prompt.mjs @@ -0,0 +1,186 @@ +import { createJiti } from "jiti"; +import { mkdirSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const jiti = createJiti(import.meta.url); + +const { createLibrary, defineComponent } = await jiti.import("@openuidev/lang-core"); +const { z } = await jiti.import("zod"); + +const TextContentDef = defineComponent({ + name: "TextContent", + props: z.object({ text: z.string(), tone: z.enum(["normal", "muted", "strong"]).optional() }), + description: "Displays a block of text.", + component: null, +}); + +const ButtonDef = defineComponent({ + name: "Button", + props: z.object({ + label: z.string(), + action: z.string().optional(), + variant: z.enum(["primary", "secondary", "ghost"]).optional(), + }), + description: "A clickable button.", + component: null, +}); + +const BadgeDef = defineComponent({ + name: "Badge", + props: z.object({ + label: z.string(), + tone: z.enum(["neutral", "success", "warning", "danger", "info"]).optional(), + }), + description: "Small status pill.", + component: null, +}); + +const KpiTileDef = defineComponent({ + name: "KpiTile", + props: z.object({ + label: z.string(), + value: z.string(), + delta: z.string().optional(), + trend: z.enum(["up", "down", "neutral"]).optional(), + }), + description: "Compact KPI tile with value and trend.", + component: null, +}); + +const MetricListDef = defineComponent({ + name: "MetricList", + props: z.object({ + title: z.string(), + items: z.array(z.object({ label: z.string(), value: z.string() })), + }), + description: "Two-column labeled metric list.", + component: null, +}); + +const TimelineDef = defineComponent({ + name: "Timeline", + props: z.object({ + title: z.string(), + items: z.array( + z.object({ + title: z.string(), + detail: z.string(), + status: z.enum(["done", "active", "next"]).optional(), + }), + ), + }), + description: "Progress timeline with status dots.", + component: null, +}); + +const ChartDef = defineComponent({ + name: "Chart", + props: z.object({ + title: z.string(), + type: z.enum(["bar", "line", "pie", "doughnut"]), + labels: z.array(z.string()), + values: z.array(z.number()), + datasetLabel: z.string().optional(), + }), + description: "Renders a chart.", + component: null, +}); + +const CardDef = defineComponent({ + name: "Card", + props: z.object({ + title: z.string(), + children: z.array( + z.union([ + TextContentDef.ref, + ButtonDef.ref, + ChartDef.ref, + BadgeDef.ref, + KpiTileDef.ref, + MetricListDef.ref, + TimelineDef.ref, + ]), + ), + subtitle: z.string().optional(), + variant: z.enum(["default", "glass", "accent"]).optional(), + highlight: z.string().optional(), + }), + description: "Card container with title and children.", + component: null, +}); + +const StackDef = defineComponent({ + name: "Stack", + props: z.object({ + children: z.array( + z.union([ + CardDef.ref, + TextContentDef.ref, + ButtonDef.ref, + ChartDef.ref, + BadgeDef.ref, + KpiTileDef.ref, + MetricListDef.ref, + TimelineDef.ref, + ]), + ), + }), + description: "Dashboard layout container. Use as root.", + component: null, +}); + +const library = createLibrary({ + components: [ + TextContentDef, + ButtonDef, + ChartDef, + BadgeDef, + KpiTileDef, + MetricListDef, + TimelineDef, + CardDef, + StackDef, + ], + root: "Stack", +}); + +const promptOptions = { + additionalRules: [ + "Always use Stack as the root component.", + "Use Stack only as root. Never place Stack inside Card children.", + "Output openui-lang code only. No markdown fences, no explanations, no think blocks.", + "All visible text must be English.", + "Use references for readability and better streaming behavior.", + "For dashboards, include at least four KpiTile components and at least two charts.", + "Prefer Card with subtitle and highlight for richer hierarchy.", + "Use Badge, MetricList, and Timeline when relevant instead of plain text-only layouts.", + "Use positional arguments only.", + "Never output reasoning or markdown.", + ], + examples: [ + `User: Build a SaaS KPI dashboard + +root = Stack([title, healthy, m1, m2, m3, m4, mainCard, action1, action2]) +title = TextContent("Q2 Growth Pulse", "strong") +healthy = Badge("Healthy", "success") +m1 = KpiTile("MRR", "$128.4k", "+8.2% MoM", "up") +m2 = KpiTile("New Users", "1,248", "+12.6% WoW", "up") +m3 = KpiTile("Churn", "2.4%", "-0.3pp", "up") +m4 = KpiTile("NPS", "47", "+2", "up") +trend = Chart("Revenue Trend", "line", ["Jan", "Feb", "Mar", "Apr", "May"], [92, 98, 107, 119, 128], "$k") +mix = Chart("Traffic Sources", "pie", ["Organic", "Paid", "Referral", "Social"], [44, 28, 18, 10], "%") +ops = MetricList("Ops Snapshot", [{"label":"Avg response", "value":"4.9h"}, {"label":"Backlog", "value":"31"}]) +flow = Timeline("Delivery Steps", [{"title":"Design", "detail":"Completed", "status":"done"}, {"title":"Dev", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Planned", "status":"next"}]) +mainCard = Card("Executive Dashboard", [trend, mix, ops, flow], "Weekly status and channel mix", "glass", "Live") +action1 = Button("Show risk drivers", "continue-conversation", "primary") +action2 = Button("Compare with previous quarter", "continue-conversation", "ghost")`, + ], +}; + +const prompt = library.prompt(promptOptions); +const outPath = resolve(__dirname, "../generated/system-prompt.txt"); +mkdirSync(dirname(outPath), { recursive: true }); +writeFileSync(outPath, prompt, "utf-8"); +console.log(`Generated system prompt (${prompt.length} chars) -> ${outPath}`); diff --git a/examples/solid-chat/src/App.tsx b/examples/solid-chat/src/App.tsx new file mode 100644 index 000000000..d113922b5 --- /dev/null +++ b/examples/solid-chat/src/App.tsx @@ -0,0 +1,287 @@ +import { Field } from "@ark-ui/solid/field"; +import { BuiltinActionType, Renderer, type ActionEvent } from "@openuidev/solid-lang"; +import { createSignal, For, Show } from "solid-js"; +import { library } from "./lib/library"; + +type ChatMessage = + | { role: "user"; text: string } + | { role: "assistant"; response: string; streaming: boolean; started: boolean }; + +export default function App() { + const [messages, setMessages] = createSignal([]); + const [input, setInput] = createSignal(""); + const [isLoading, setIsLoading] = createSignal(false); + + async function sendMessage(text: string) { + const trimmed = text.trim(); + if (!trimmed || isLoading()) return; + + setIsLoading(true); + setMessages((prev) => [...prev, { role: "user", text: trimmed }]); + setInput(""); + setMessages((prev) => [ + ...prev, + { role: "assistant", response: "", streaming: true, started: false }, + ]); + + const openUiStartRegex = /[A-Za-z_][A-Za-z0-9_]*\s*=\s*[A-Za-z_][A-Za-z0-9_]*\s*\(/; + + const sanitizeModelText = (input: string) => + input + .replace(/[\s\S]*?<\/think>/gi, "") + .replace(/```openui-lang/gi, "") + .replace(/```/g, ""); + + const appendAssistantDelta = (delta: string) => { + if (!delta) return; + setMessages((prev) => { + const next = [...prev]; + for (let i = next.length - 1; i >= 0; i -= 1) { + const msg = next[i]; + if (msg?.role === "assistant" && msg.streaming) { + const response = msg.response + delta; + next[i] = { + ...msg, + response, + started: openUiStartRegex.test(sanitizeModelText(response)), + }; + break; + } + } + return next; + }); + }; + + const finishAssistantStream = (fallbackError?: string) => { + setMessages((prev) => { + const next = [...prev]; + for (let i = next.length - 1; i >= 0; i -= 1) { + const msg = next[i]; + if (msg?.role === "assistant" && msg.streaming) { + const raw = sanitizeModelText(msg.response || ""); + const assignIndex = raw.search( + /[A-Za-z_][A-Za-z0-9_]*\s*=\s*[A-Za-z_][A-Za-z0-9_]*\s*\(/, + ); + const cleaned = assignIndex >= 0 ? raw.slice(assignIndex).trim() : ""; + + const response = + cleaned || + `t1 = TextContent("Error: ${(fallbackError || "Empty response").replaceAll('"', '\\"')}")\ncard = Card("LLM Stream Error", [t1])\nroot = Stack([card])`; + next[i] = { ...msg, response, streaming: false }; + break; + } + } + return next; + }); + }; + + try { + const response = await fetch("/api/chat", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "text/event-stream", + }, + body: JSON.stringify({ message: trimmed }), + }); + + if (!response.ok || !response.body) { + finishAssistantStream(`HTTP ${response.status} from chat API`); + return; + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const parts = buffer.split("\n\n"); + buffer = parts.pop() ?? ""; + + for (const part of parts) { + const lines = part.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (!trimmedLine.startsWith("data:")) continue; + const payload = trimmedLine.slice(5).trim(); + if (!payload || payload === "[DONE]") continue; + + try { + const parsed = JSON.parse(payload) as { delta?: string }; + if (parsed.delta) appendAssistantDelta(parsed.delta); + } catch { + appendAssistantDelta(payload); + } + } + } + } + + finishAssistantStream(); + } catch (error) { + const message = error instanceof Error ? error.message : "Network error"; + finishAssistantStream(message); + } finally { + setIsLoading(false); + } + } + + function handleAction(event: ActionEvent) { + if (event.type === BuiltinActionType.ContinueConversation && event.humanFriendlyMessage) { + void sendMessage(event.humanFriendlyMessage); + } + } + + return ( +
+
+

+ OpenUI Solid Dashboard Chat +

+

+ Modern generative dashboard example using Ark UI and ECharts. +

+
+ +
+ + {(message) => ( +
+ +
+ {(message as { role: "user"; text: string }).text} +
+
+ + +
+ + + Generating UI... +
+ } + > + +
+
+ +
+ )} + +
+ +
{ + event.preventDefault(); + void sendMessage(input()); + }} + style={{ + display: "flex", + gap: "10px", + padding: "10px", + border: "1px solid rgba(148,163,184,0.28)", + "border-radius": "14px", + background: "#f8fafc", + }} + > + + + setInput(event.currentTarget.value) + } + placeholder="Ask anything..." + style={{ + width: "100%", + padding: "11px 12px", + border: "1px solid #cbd5e1", + "border-radius": "12px", + background: "#ffffff", + }} + /> + + +
+ + + + ); +} diff --git a/examples/solid-chat/src/components/Badge.tsx b/examples/solid-chat/src/components/Badge.tsx new file mode 100644 index 000000000..b2f58fd09 --- /dev/null +++ b/examples/solid-chat/src/components/Badge.tsx @@ -0,0 +1,37 @@ +interface BadgeProps { + label: string; + tone?: "neutral" | "success" | "warning" | "danger" | "info"; +} + +const toneStyles: Record< + NonNullable, + { bg: string; fg: string; border: string } +> = { + neutral: { bg: "#f1f5f9", fg: "#334155", border: "#cbd5e1" }, + success: { bg: "#dcfce7", fg: "#166534", border: "#86efac" }, + warning: { bg: "#fef3c7", fg: "#92400e", border: "#fde68a" }, + danger: { bg: "#fee2e2", fg: "#991b1b", border: "#fecaca" }, + info: { bg: "#dbeafe", fg: "#1e40af", border: "#93c5fd" }, +}; + +export function Badge(props: { props: BadgeProps }) { + const tone = props.props.tone || "neutral"; + const style = toneStyles[tone]; + + return ( + + {props.props.label} + + ); +} diff --git a/examples/solid-chat/src/components/Button.tsx b/examples/solid-chat/src/components/Button.tsx new file mode 100644 index 000000000..195c6c185 --- /dev/null +++ b/examples/solid-chat/src/components/Button.tsx @@ -0,0 +1,51 @@ +import { useTriggerAction } from "@openuidev/solid-lang"; +import { Send } from "lucide-solid"; + +interface ButtonProps { + label: string; + action?: string; + variant?: "primary" | "secondary" | "ghost"; +} + +export function Button(props: { props: ButtonProps }) { + const triggerAction = useTriggerAction(); + const variant = props.props.variant || "secondary"; + + const variantStyles: Record< + NonNullable, + { bg: string; fg: string; border: string } + > = { + primary: { bg: "#0f172a", fg: "#ffffff", border: "#0f172a" }, + secondary: { bg: "#f8fafc", fg: "#0f172a", border: "#cbd5e1" }, + ghost: { bg: "#ffffff", fg: "#1e40af", border: "#bfdbfe" }, + }; + const style = variantStyles[variant]; + + return ( + + ); +} diff --git a/examples/solid-chat/src/components/Card.tsx b/examples/solid-chat/src/components/Card.tsx new file mode 100644 index 000000000..cf699d9b1 --- /dev/null +++ b/examples/solid-chat/src/components/Card.tsx @@ -0,0 +1,105 @@ +import { BarChart3 } from "lucide-solid"; +import type { JSX } from "solid-js"; + +interface CardProps { + title: string; + subtitle?: string; + variant?: "default" | "glass" | "accent"; + highlight?: string; + children: unknown[]; +} + +function countCharts(children: unknown[]): number { + return children.filter( + (child) => + child && typeof child === "object" && (child as { typeName?: string }).typeName === "Chart", + ).length; +} + +function isRenderableNode(child: unknown): child is { typeName: string } { + return Boolean( + child && + typeof child === "object" && + typeof (child as { typeName?: unknown }).typeName === "string", + ); +} + +export function Card(props: { props: CardProps; renderNode: (value: unknown) => JSX.Element }) { + const safeChildren = props.props.children.filter(isRenderableNode); + const chartCount = countCharts(safeChildren); + const hasChart = chartCount > 0; + const variant = props.props.variant || "default"; + + const backgroundByVariant: Record, string> = { + default: "linear-gradient(180deg, rgba(255,255,255,0.96) 0%, rgba(248,250,252,0.96) 100%)", + glass: "linear-gradient(160deg, rgba(239,246,255,0.86) 0%, rgba(255,255,255,0.8) 100%)", + accent: "linear-gradient(160deg, #eff6ff 0%, #f8fafc 100%)", + }; + + return ( +
+
+
+ +
+

+ {props.props.title} +

+ {props.props.subtitle ? ( +

+ {props.props.subtitle} +

+ ) : null} +
+
+ {props.props.highlight ? ( + + {props.props.highlight} + + ) : null} +
+
+ {safeChildren.map((child) => ( +
{props.renderNode(child)}
+ ))} +
+
+ ); +} diff --git a/examples/solid-chat/src/components/Chart.tsx b/examples/solid-chat/src/components/Chart.tsx new file mode 100644 index 000000000..869b65331 --- /dev/null +++ b/examples/solid-chat/src/components/Chart.tsx @@ -0,0 +1,101 @@ +import type { ECharts } from "echarts"; +import * as echarts from "echarts"; +import { createEffect, onCleanup, onMount } from "solid-js"; + +interface ChartProps { + title: string; + type: "bar" | "line" | "pie" | "doughnut"; + labels: string[]; + values: number[]; + datasetLabel?: string; +} + +export function Chart(props: { props: ChartProps }) { + let chartEl: HTMLDivElement | undefined; + let chart: ECharts | undefined; + + function buildOption() { + const label = props.props.datasetLabel || "Value"; + if (props.props.type === "pie" || props.props.type === "doughnut") { + return { + color: ["#2563eb", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4"], + tooltip: { trigger: "item" }, + series: [ + { + type: "pie", + radius: props.props.type === "doughnut" ? ["45%", "70%"] : "70%", + label: { color: "#334155" }, + data: props.props.labels.map((name, i) => ({ + name, + value: props.props.values[i] ?? 0, + })), + }, + ], + }; + } + + return { + color: ["#2563eb", "#22c55e", "#8b5cf6"], + tooltip: { trigger: "axis" }, + xAxis: { + type: "category", + data: props.props.labels, + axisLabel: { color: "#475569" }, + axisLine: { lineStyle: { color: "#cbd5e1" } }, + }, + yAxis: { + type: "value", + name: label, + axisLabel: { color: "#475569" }, + splitLine: { lineStyle: { color: "rgba(148,163,184,0.25)" } }, + }, + series: [ + { + type: props.props.type, + data: props.props.values, + smooth: props.props.type === "line", + areaStyle: props.props.type === "line" ? { opacity: 0.12 } : undefined, + }, + ], + grid: { left: 56, right: 20, top: 24, bottom: 28, containLabel: true }, + }; + } + + onMount(() => { + if (!chartEl) return; + chart = echarts.init(chartEl); + chart.setOption(buildOption()); + const resize = () => chart?.resize(); + window.addEventListener("resize", resize); + onCleanup(() => { + window.removeEventListener("resize", resize); + chart?.dispose(); + }); + }); + + createEffect(() => { + if (!chart) return; + chart.setOption(buildOption(), true); + }); + + return ( +
+
+ {props.props.title} ({props.props.type}) +
+
+
+ ); +} diff --git a/examples/solid-chat/src/components/KpiTile.tsx b/examples/solid-chat/src/components/KpiTile.tsx new file mode 100644 index 000000000..cff426e41 --- /dev/null +++ b/examples/solid-chat/src/components/KpiTile.tsx @@ -0,0 +1,40 @@ +interface KpiTileProps { + label: string; + value: string; + delta?: string; + trend?: "up" | "down" | "neutral"; +} + +const trendColor: Record, string> = { + up: "#15803d", + down: "#b91c1c", + neutral: "#334155", +}; + +export function KpiTile(props: { props: KpiTileProps }) { + const trend = props.props.trend || "neutral"; + return ( +
+
+ {props.props.label} +
+
+ {props.props.value} +
+ {props.props.delta ? ( +
+ {props.props.delta} +
+ ) : null} +
+ ); +} diff --git a/examples/solid-chat/src/components/MetricList.tsx b/examples/solid-chat/src/components/MetricList.tsx new file mode 100644 index 000000000..30d72ba33 --- /dev/null +++ b/examples/solid-chat/src/components/MetricList.tsx @@ -0,0 +1,61 @@ +interface MetricItem { + label: string; + value: string; +} + +interface MetricListProps { + title: string; + items: MetricItem[]; +} + +export function MetricList(props: { props: MetricListProps }) { + return ( +
+
+ {props.props.title} +
+
+ {props.props.items.map((item, idx) => ( +
+ {item.label} + + {item.value} + +
+ ))} +
+
+ ); +} diff --git a/examples/solid-chat/src/components/Stack.tsx b/examples/solid-chat/src/components/Stack.tsx new file mode 100644 index 000000000..ee1da1811 --- /dev/null +++ b/examples/solid-chat/src/components/Stack.tsx @@ -0,0 +1,67 @@ +import { createSignal, onCleanup, onMount, type JSX } from "solid-js"; + +interface StackProps { + children: unknown[]; +} + +function getTypeName(value: unknown): string | undefined { + if (!value || typeof value !== "object") return undefined; + return (value as { typeName?: string }).typeName; +} + +export function Stack(props: { props: StackProps; renderNode: (value: unknown) => JSX.Element }) { + const [isCompact, setIsCompact] = createSignal(false); + const childTypes = props.props.children.map(getTypeName); + const isActionRow = childTypes.length > 0 && childTypes.every((name) => name === "Button"); + + const spanByType: Record = { + TextContent: "1 / -1", + Stack: "1 / -1", + Card: "span 6", + Chart: "span 6", + KpiTile: "span 3", + MetricList: "span 6", + Timeline: "span 6", + Badge: "span 2", + Button: "span 3", + }; + + onMount(() => { + const onResize = () => setIsCompact(window.innerWidth < 900); + onResize(); + window.addEventListener("resize", onResize); + onCleanup(() => window.removeEventListener("resize", onResize)); + }); + + return ( +
+ {props.props.children.map((child) => ( +
+ {props.renderNode(child)} +
+ ))} +
+ ); +} diff --git a/examples/solid-chat/src/components/TextContent.tsx b/examples/solid-chat/src/components/TextContent.tsx new file mode 100644 index 000000000..5329a58ae --- /dev/null +++ b/examples/solid-chat/src/components/TextContent.tsx @@ -0,0 +1,25 @@ +interface TextContentProps { + text: string; + tone?: "normal" | "muted" | "strong"; +} + +export function TextContent(props: { props: TextContentProps }) { + const tone = props.props.tone || "normal"; + const color = tone === "strong" ? "#0f172a" : tone === "muted" ? "#64748b" : "#334155"; + const weight = tone === "strong" ? 600 : 400; + return ( +

+ {props.props.text} +

+ ); +} diff --git a/examples/solid-chat/src/components/Timeline.tsx b/examples/solid-chat/src/components/Timeline.tsx new file mode 100644 index 000000000..02fe56f66 --- /dev/null +++ b/examples/solid-chat/src/components/Timeline.tsx @@ -0,0 +1,58 @@ +interface TimelineItem { + title: string; + detail: string; + status?: "done" | "active" | "next"; +} + +interface TimelineProps { + title: string; + items: TimelineItem[]; +} + +const dotColor: Record, string> = { + done: "#16a34a", + active: "#2563eb", + next: "#94a3b8", +}; + +export function Timeline(props: { props: TimelineProps }) { + return ( +
+
+ {props.props.title} +
+
+ {props.props.items.map((item) => { + const status = item.status || "next"; + return ( +
+ +
+ {item.title} + {item.detail} +
+
+ ); + })} +
+
+ ); +} diff --git a/examples/solid-chat/src/lib/library.tsx b/examples/solid-chat/src/lib/library.tsx new file mode 100644 index 000000000..436a789ad --- /dev/null +++ b/examples/solid-chat/src/lib/library.tsx @@ -0,0 +1,176 @@ +import { createLibrary, defineComponent, type PromptOptions } from "@openuidev/solid-lang"; +import { z } from "zod"; +import { Badge } from "../components/Badge"; +import { Button } from "../components/Button"; +import { Card } from "../components/Card"; +import { Chart } from "../components/Chart"; +import { KpiTile } from "../components/KpiTile"; +import { MetricList } from "../components/MetricList"; +import { Stack } from "../components/Stack"; +import { TextContent } from "../components/TextContent"; +import { Timeline } from "../components/Timeline"; + +const TextContentDef = defineComponent({ + name: "TextContent", + props: z.object({ text: z.string(), tone: z.enum(["normal", "muted", "strong"]).optional() }), + description: "Displays a block of text.", + component: TextContent, +}); + +const ButtonDef = defineComponent({ + name: "Button", + props: z.object({ + label: z.string(), + action: z.string().optional(), + variant: z.enum(["primary", "secondary", "ghost"]).optional(), + }), + description: "A clickable button.", + component: Button, +}); + +const BadgeDef = defineComponent({ + name: "Badge", + props: z.object({ + label: z.string(), + tone: z.enum(["neutral", "success", "warning", "danger", "info"]).optional(), + }), + description: "Small status pill.", + component: Badge, +}); + +const KpiTileDef = defineComponent({ + name: "KpiTile", + props: z.object({ + label: z.string(), + value: z.string(), + delta: z.string().optional(), + trend: z.enum(["up", "down", "neutral"]).optional(), + }), + description: "Compact KPI tile with value and trend.", + component: KpiTile, +}); + +const MetricListDef = defineComponent({ + name: "MetricList", + props: z.object({ + title: z.string(), + items: z.array(z.object({ label: z.string(), value: z.string() })), + }), + description: "Two-column labeled metric list.", + component: MetricList, +}); + +const TimelineDef = defineComponent({ + name: "Timeline", + props: z.object({ + title: z.string(), + items: z.array( + z.object({ + title: z.string(), + detail: z.string(), + status: z.enum(["done", "active", "next"]).optional(), + }), + ), + }), + description: "Progress timeline with status dots.", + component: Timeline, +}); + +const ChartDef = defineComponent({ + name: "Chart", + props: z.object({ + title: z.string(), + type: z.enum(["bar", "line", "pie", "doughnut"]), + labels: z.array(z.string()), + values: z.array(z.number()), + datasetLabel: z.string().optional(), + }), + description: "Simple chart-style visualization.", + component: Chart, +}); + +const CardDef = defineComponent({ + name: "Card", + props: z.object({ + title: z.string(), + children: z.array( + z.union([ + TextContentDef.ref, + ButtonDef.ref, + ChartDef.ref, + BadgeDef.ref, + KpiTileDef.ref, + MetricListDef.ref, + TimelineDef.ref, + ]), + ), + subtitle: z.string().optional(), + variant: z.enum(["default", "glass", "accent"]).optional(), + highlight: z.string().optional(), + }), + description: "Card container with title and children.", + component: Card, +}); + +const StackDef = defineComponent({ + name: "Stack", + props: z.object({ + children: z.array( + z.union([ + CardDef.ref, + TextContentDef.ref, + ButtonDef.ref, + ChartDef.ref, + BadgeDef.ref, + KpiTileDef.ref, + MetricListDef.ref, + TimelineDef.ref, + ]), + ), + }), + description: "Vertical root layout.", + component: Stack, +}); + +export const library = createLibrary({ + components: [ + TextContentDef, + ButtonDef, + ChartDef, + BadgeDef, + KpiTileDef, + MetricListDef, + TimelineDef, + CardDef, + StackDef, + ], + root: "Stack", +}); + +export const promptOptions: PromptOptions = { + additionalRules: [ + "Always use Stack as the root component.", + "Use Stack only as root. Never place Stack inside Card children.", + "Output openui-lang code only. No markdown fences, no explanations, no think blocks.", + "All visible text must be English.", + "Prefer dashboard composition: KpiTile + Chart + MetricList + Timeline for richer UIs.", + "Use Card subtitle/variant/highlight for hierarchy when relevant.", + "Use Badge for state labels (healthy, warning, risk).", + "For dashboard requests, compose multiple Cards in one Stack and include at least two Chart components.", + ], + examples: [ + `title = TextContent("Q2 Growth Pulse", "strong") +chip = Badge("Healthy", "success") +m1 = KpiTile("MRR", "$128.4k", "+8.2% MoM", "up") +m2 = KpiTile("New Users", "1,248", "+12.6% WoW", "up") +m3 = KpiTile("Churn", "2.4%", "-0.3pp", "up") +m4 = KpiTile("NPS", "47", "+2", "up") +trend = Chart("Revenue Trend", "line", ["Jan", "Feb", "Mar", "Apr", "May"], [92, 98, 107, 119, 128], "$k") +mix = Chart("Traffic Sources", "pie", ["Organic", "Paid", "Referral", "Social"], [44, 28, 18, 10], "%") +ops = MetricList("Ops Snapshot", [{"label":"Avg response","value":"4.9h"},{"label":"Backlog","value":"31"}]) +flow = Timeline("Delivery Steps", [{"title":"Design","detail":"Completed","status":"done"},{"title":"Dev","detail":"In progress","status":"active"},{"title":"Launch","detail":"Planned","status":"next"}]) +card = Card("Executive Dashboard", [chip, trend, mix, ops, flow], "Weekly status and channel mix", "glass", "Live") +a1 = Button("Show risk drivers", "continue-conversation", "primary") +root = Stack([title, m1, m2, m3, m4, card, a1])`, + ], +}; diff --git a/examples/solid-chat/src/main.tsx b/examples/solid-chat/src/main.tsx new file mode 100644 index 000000000..40005f4c7 --- /dev/null +++ b/examples/solid-chat/src/main.tsx @@ -0,0 +1,4 @@ +import { render } from "solid-js/web"; +import App from "./App"; + +render(() => , document.getElementById("root") as HTMLElement); diff --git a/examples/solid-chat/src/vite-env.d.ts b/examples/solid-chat/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/examples/solid-chat/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/solid-chat/tsconfig.json b/examples/solid-chat/tsconfig.json new file mode 100644 index 000000000..a6e086296 --- /dev/null +++ b/examples/solid-chat/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"] + }, + "include": ["src"] +} diff --git a/examples/solid-chat/vite.config.ts b/examples/solid-chat/vite.config.ts new file mode 100644 index 000000000..f6f133dbb --- /dev/null +++ b/examples/solid-chat/vite.config.ts @@ -0,0 +1,144 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { defineConfig, loadEnv } from "vite"; +import solid from "vite-plugin-solid"; + +const SYSTEM_PROMPT = readFileSync(resolve(process.cwd(), "generated/system-prompt.txt"), "utf-8"); + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + + return { + plugins: [ + solid(), + { + name: "chat-api", + configureServer(server) { + server.middlewares.use("/api/chat", async (req, res) => { + if (req.method !== "POST") { + res.statusCode = 405; + res.end("Method Not Allowed"); + return; + } + + const chunks: Buffer[] = []; + for await (const chunk of req) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + + let message = ""; + try { + const body = JSON.parse(Buffer.concat(chunks).toString("utf-8")); + message = String(body.message || ""); + } catch { + message = ""; + } + + const baseUrl = env["OPENAI_BASE_URL"] || "http://localhost:11434/v1"; + const apiKey = env["OPENAI_API_KEY"] || "ollama"; + const model = env["OPENAI_MODEL"] || "llama3.1:8b"; + + try { + const llmResp = await fetch(`${baseUrl}/chat/completions`, { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model, + messages: [ + { role: "system", content: SYSTEM_PROMPT }, + { role: "user", content: message }, + ], + temperature: 0.2, + stream: true, + }), + }); + + if (!llmResp.ok) { + res.statusCode = 502; + res.setHeader("Content-Type", "application/json"); + res.end( + JSON.stringify({ + error: `LLM request failed (${llmResp.status}). Check OPENAI_BASE_URL and OPENAI_MODEL.`, + }), + ); + return; + } + + res.statusCode = 200; + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + + if (!llmResp.body) { + res.write(`data: ${JSON.stringify({ delta: "" })}\n\n`); + res.write("data: [DONE]\n\n"); + res.end(); + return; + } + + const reader = llmResp.body.getReader(); + const decoder = new TextDecoder(); + let upstreamBuffer = ""; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + upstreamBuffer += decoder.decode(value, { stream: true }); + const chunks = upstreamBuffer.split("\n\n"); + upstreamBuffer = chunks.pop() ?? ""; + + for (const chunk of chunks) { + const lines = chunk.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (!trimmedLine.startsWith("data:")) continue; + + const payload = trimmedLine.slice(5).trim(); + if (!payload) continue; + if (payload === "[DONE]") { + res.write("data: [DONE]\n\n"); + res.end(); + return; + } + + try { + const parsed = JSON.parse(payload) as { + choices?: Array<{ + delta?: { content?: string; reasoning_content?: string }; + }>; + }; + const delta = parsed.choices?.[0]?.delta?.content ?? ""; + if (!delta) continue; + res.write(`data: ${JSON.stringify({ delta })}\n\n`); + } catch { + res.write(`data: ${JSON.stringify({ delta: payload })}\n\n`); + } + } + } + } + + res.write("data: [DONE]\n\n"); + res.end(); + } catch (error) { + res.statusCode = 502; + res.setHeader("Content-Type", "application/json"); + res.end( + JSON.stringify({ + error: `Cannot connect to LLM endpoint (${baseUrl}). Check OPENAI_BASE_URL / OPENAI_MODEL / network.`, + detail: error instanceof Error ? error.message : String(error), + }), + ); + } + }); + }, + }, + ], + server: { + port: 5174, + }, + }; +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d8d6f788..2b075b34c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -808,6 +808,43 @@ importers: specifier: ^5 version: 5.9.3 + examples/solid-chat: + dependencies: + '@ark-ui/solid': + specifier: ^5.35.0 + version: 5.35.0(solid-js@1.9.12) + '@openuidev/lang-core': + specifier: workspace:* + version: link:../../packages/lang-core + '@openuidev/solid-lang': + specifier: workspace:* + version: link:../../packages/solid-lang + echarts: + specifier: ^5.6.0 + version: 5.6.0 + lucide-solid: + specifier: ^0.542.0 + version: 0.542.0(solid-js@1.9.12) + solid-js: + specifier: ^1.9.9 + version: 1.9.12 + zod: + specifier: ^4.3.6 + version: 4.3.6 + devDependencies: + jiti: + specifier: ^2.6.1 + version: 2.6.1 + typescript: + specifier: ^5.9.2 + version: 5.9.3 + vite: + specifier: ^6.4.1 + version: 6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3) + vite-plugin-solid: + specifier: ^2.11.8 + version: 2.11.12(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.3)) + examples/svelte-chat: dependencies: '@ai-sdk/openai': @@ -1604,6 +1641,11 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@ark-ui/solid@5.35.0': + resolution: {integrity: sha512-iI5QgUd0gjnOtMxKybQK/GRMyMNsn9RQT53Aolxg03gEPEmM+Y/n3Q79aVQM4ISF+0ZBarG1zaMfAlARyC7xzA==} + peerDependencies: + solid-js: '>=1.6.0' + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -3156,15 +3198,24 @@ packages: '@floating-ui/core@1.7.1': resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + '@floating-ui/dom@1.7.1': resolution: {integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==} + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + '@floating-ui/react-dom@2.1.3': resolution: {integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} @@ -6917,6 +6968,11 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@solid-primitives/keyed@1.5.3': + resolution: {integrity: sha512-zNadtyYBhJSOjXtogkGHmRxjGdz9KHc8sGGVAGlUABkE8BED2tbIZoxkwSqzOwde8OcUEH0bb5DLZUWIMvyBSA==} + peerDependencies: + solid-js: ^1.6.12 + '@solidjs/testing-library@0.8.10': resolution: {integrity: sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ==} engines: {node: '>= 14'} @@ -8288,6 +8344,234 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@zag-js/accordion@1.38.2': + resolution: {integrity: sha512-lr3K8R4c8cY1ghcU4lpav4dAXCFqFcNOrieqy4lsS5n5CSrpLbL1EFrOdTBux/CCeijmipFp14ddUZGHWHIDJQ==} + + '@zag-js/anatomy@1.38.2': + resolution: {integrity: sha512-3wEwuHkHiErD1r36MrC1Jd4lqvQK+wQ5EgqdEk8r3L2koD7fTdq8CB5KbMcP0JM08eFCOo2CBl3gP3VBmiTNJA==} + + '@zag-js/angle-slider@1.38.2': + resolution: {integrity: sha512-VugKx/iClEiHA5AXY/d2zZzfpgAHD2hyV7f0k3FzuLIsgeJmZDTc3YNMX2BCrcbD8qNrambO3e2b31mMt1HxFg==} + + '@zag-js/aria-hidden@1.38.2': + resolution: {integrity: sha512-TvMdHUouslt6RZmKZ6k41B1KTqjOFaD4T6c4sTb6qSUkuTYgyYsA7ZqimiR0vcvmmG852nSaOepvr6Jj0sRPNA==} + + '@zag-js/async-list@1.38.2': + resolution: {integrity: sha512-URi5Wl0uJ4TJ/MFh5IKAsLKSb21e9tTPegZ7Vvr1XO5tWd9neY3nwVYImHq93Tv1nwhkttdOvUTKdnwlpr9NiQ==} + + '@zag-js/auto-resize@1.38.2': + resolution: {integrity: sha512-0nVYusVmxDfa1j8fyycQCxcULT07BUM+23BlEslPy0Hfzt5c7CwMQFan4JiMia82RzgnWv3s+5svVMvI++ZZqw==} + + '@zag-js/avatar@1.38.2': + resolution: {integrity: sha512-DmhoaJG+IcKFSWSrtfcI91WJNSJQx2PYDmChnOtNvyGIXibNHkoYXzvZTCrKsVGgKeUXFtOxSqSYMbzxgL2k2Q==} + + '@zag-js/carousel@1.38.2': + resolution: {integrity: sha512-/edO3DYuJ23znbR1LhxWKQeIAkd/VkSPFY5vNWKA879FWLjAf5ZWAGe+jKXayAOaWqcTFKp9uCUSbesO5eFiDg==} + + '@zag-js/cascade-select@1.38.2': + resolution: {integrity: sha512-5NONGij0U5utqgv1lFeWz+WZYcoa9nhI+EU0XGojJ5VUTZbQVgxWeS8WDxcpneT29n1xqQkmv+6SZ3fYcBDkxA==} + + '@zag-js/checkbox@1.38.2': + resolution: {integrity: sha512-AAljBvGog/jLWhdEFc2ADzsGODoGua8fWZs0RUYq/wQ9UBiG4w5VRyPAq5ZEzaIoSruoHm5AHN9D9VdWN9py3g==} + + '@zag-js/clipboard@1.38.2': + resolution: {integrity: sha512-LIDcuZDU70cvMAbIz7LgeXM+CvhRW8Ez84UPkorwT+EyDCOaCNEADabSY2AM4WKn81EV56UKkwSNblkhP3gmZA==} + + '@zag-js/collapsible@1.38.2': + resolution: {integrity: sha512-7yBlX/9d/A7Da/9PZoBye0nA+FjVZv7rjrUfmO2NhoERt2IV6r0S143DrwcW3ELjz+76HzV+wlP8ytruqaK1gQ==} + + '@zag-js/collection@1.38.2': + resolution: {integrity: sha512-1Edek5yrjHfpmciK4M5PoUbDFRgEEkFh3HMIwWjMRdcRzea9ibVhSGXvXsenafC/b3so/ApEXBhs1ch7dFn+NQ==} + + '@zag-js/color-picker@1.38.2': + resolution: {integrity: sha512-d2/FBAQ3waAY7T0zhMN5gbz7z2Z5vr3wtyT+qcMfwweeBBQEST8iQMc4JUZgTfZ/V+Uqv/A1L644/IRXq6Jpyg==} + + '@zag-js/color-utils@1.38.2': + resolution: {integrity: sha512-neo0qU1fvIbptrgDMzXI7gz3DSYvxFEf61YskVXJkPGY4u4+unx89j/R1G9wC4lF/pl8kPiTsb7TK7+UnFf5/Q==} + + '@zag-js/combobox@1.38.2': + resolution: {integrity: sha512-J2M1vB3Keepv2Wr/sC5kue18ver+nf/7gj9LgHxSU5sgwl9Fr4wZLPH1s9ZQ7FDhBXjWsitILbf9dqR1UpyLJQ==} + + '@zag-js/core@1.38.2': + resolution: {integrity: sha512-UaRU6TVOJV07phQkToR6fNceJbfPiNLx2itSh78ofHn8w9w860mNrS5tyn8HExuNwY5JVQdfMkyFlMF0LfgVLQ==} + + '@zag-js/date-picker@1.38.2': + resolution: {integrity: sha512-Jhss1wt33YjnMYWUAPMnze3ObkkLJnmqWN2H2fgHEBRQyJScVj+MHWhWPpAXsU2hf70Re19kdPld5vADIfACMA==} + peerDependencies: + '@internationalized/date': '>=3.0.0' + + '@zag-js/date-utils@1.38.2': + resolution: {integrity: sha512-TwyIlBNS4ctx4zMjFTq9IVqSFFiHwv4WeB4hauqrBVARFzj1twE7r9mLFPbpq7FYuqwLuA4b9CaaVv3STukszg==} + peerDependencies: + '@internationalized/date': '>=3.0.0' + + '@zag-js/dialog@1.38.2': + resolution: {integrity: sha512-cVYBXiEnQ30UjudJxODoV8Au/18VyePEvJ9SollGFJYnr+/2mCue5CoraOQJqoZVED3tdUCBodz9kyjS1CcvAw==} + + '@zag-js/dismissable@1.38.2': + resolution: {integrity: sha512-4B83ZGD8YKnwiXwL4J49txeXSPdS29bPt6p00v99u6nJPlQRRubzPUQo4OJr6NxUuT9cIn2kDqt5oj8ZCwhmng==} + + '@zag-js/dom-query@1.38.2': + resolution: {integrity: sha512-E2plmm/bDjMQhi4fmyhw2uhoQv2cgNYaG+fzLrJgepLqovCOTU85lwzaLFiB7ldG2a9DVmjbAnFX4nVVWvaxGA==} + + '@zag-js/drawer@1.38.2': + resolution: {integrity: sha512-QoXYUHKcppju84sOHuOUJVhJpnYcFAOUtV6RSSIwVcUpkAReUE+NmwGviMxqRAmaXZc8yz7QPCm/H55Ywm+d6Q==} + + '@zag-js/editable@1.38.2': + resolution: {integrity: sha512-S4QigLJneTTwR8EE3Nq44e/z/Byo3+4heuWWkBW6UF78EIeFdJrWK7EWzSDvSJTM5/ViEZpCkQElDonoxgzJUA==} + + '@zag-js/file-upload@1.38.2': + resolution: {integrity: sha512-sA5gKafIRDcKc6zsEYrByQQpl2am+ioRzGcuxhwBdOIacJP3G2vs4bJx0wCbdM8O9Ue11s9KBcqfJhenDlBWMg==} + + '@zag-js/file-utils@1.38.2': + resolution: {integrity: sha512-498kXYSlUrNXdjmn0mKfovd7zQv1PnLXhNW94L0DYX7GcAqd6SQ4QohtxbT4IHrzlV9FfskKhe9ahXxV2IlNVQ==} + + '@zag-js/floating-panel@1.38.2': + resolution: {integrity: sha512-awpjnVRML6Q3+q5akA/S/XAu2bWEjIPpdpG+vVE5Ki0HnRsVDOKyFJVJ2+LC0gApx8BzsSWZLXPxZZqbWmr5cA==} + + '@zag-js/focus-trap@1.38.2': + resolution: {integrity: sha512-m1CBTmUy7kHsMVBFlzOmxddxESv7ce6XOX+3YhXKWNJLvnt4a3bWMnh+C3CmV7B14pntTkQwfXJALR6e3gfS6Q==} + + '@zag-js/focus-visible@1.38.2': + resolution: {integrity: sha512-ME0zulSEZLxR9jJqtwDpcWuBnZKO/8xQ+UtuYrJPvqPyUyUVkxFon+jCqNUOGjK4rHB+OjEV19LrHAzq6CQjNg==} + + '@zag-js/highlight-word@1.38.2': + resolution: {integrity: sha512-wIhPX6FBUFyt9gVwH+GnjUtxOP2im8qK6ejW/igvn9L4viLZ3Fmwas2fuaWaLqW6yxo54zDtf9BPG+1rPDbnZQ==} + + '@zag-js/hover-card@1.38.2': + resolution: {integrity: sha512-Un/65bC+ppCktMBd798z7x90t6/kvvqkASxezV6ds0Ex56TB26KQnaRPUASV+fW6uH4nLdBOeGlCH3cC8KEPYg==} + + '@zag-js/i18n-utils@1.38.2': + resolution: {integrity: sha512-/thV2gwDtfubrL8gcBXGiNmQVcHACmYIXneLVW94wVADdsuRKV/s0QrMQXe/QH3f3M9QAU5VWQnoz9Z2ySilGQ==} + + '@zag-js/image-cropper@1.38.2': + resolution: {integrity: sha512-O4u7ub98qkyw7uass5Xr3clJCLWS7yJwTmM+aX+zwCMTVLK1dlZhwVZwrmsRA7FKVKKWJBmpVUd+DzLEwFvZKg==} + + '@zag-js/interact-outside@1.38.2': + resolution: {integrity: sha512-jszzVoozqcO3vn1iiZ726xYXpy/mR+fLnx1tBs4Dvkg/n52Mm3Uv+pFDBfHgswqS/kRJ/U8P0fItFJjOH0yvlA==} + + '@zag-js/json-tree-utils@1.38.2': + resolution: {integrity: sha512-qyUSoFgwDsg9ulMEQ+a3vIos6T2AJBCjBj34U72KjjWcked6nNFs/4Y8bNh9xEdqH82txV0Wkqme8qj2iU3AcA==} + + '@zag-js/listbox@1.38.2': + resolution: {integrity: sha512-gXJJ8ziTyRowkKaQa4sfUJ9TvU7IJdF/0YIACXRUDaZIaEIe7igZfYiasBASQDk4OvRGZzAhxNGceeOa8W8rbQ==} + + '@zag-js/live-region@1.38.2': + resolution: {integrity: sha512-6qVPHEZRPbO/BKUTGXC3otZXODpsRg/lliBHUHzK7Pg83eSW4yGQI+XKRjmJnoWvB7U5vFXDC3SXG3aO0dyLYQ==} + + '@zag-js/marquee@1.38.2': + resolution: {integrity: sha512-z1e6ZPynA1izDpAqgHENrC9PDt+N7Xg5dut4Vzw1w46CForFib5pv+xIRa4DxvUhXpuHCpZ1bDS0+8Hub/5Wlg==} + + '@zag-js/menu@1.38.2': + resolution: {integrity: sha512-n0AO5uz31csUcnk1daHf/O5T2Yqtdo8KV9PInrIxwKXwfeLM2uFTHubhP2bv/j7+R+LpnZkK5laXe9WxKueBVQ==} + + '@zag-js/navigation-menu@1.38.2': + resolution: {integrity: sha512-FDfeixgwrcpC/FxJGzGp+kKgurndCohUuh/lLiNJ5pNId5ThUMr1CNFKGZMcdjhJcNMO+R1hhgyLyuFwtS+kGg==} + + '@zag-js/number-input@1.38.2': + resolution: {integrity: sha512-WJJA6EusT1HGDJlzW9YpbgOJ6Qv3TM1jcBNftrGCLGNhePXBzLzsTZKp+IZdjkI+11bab5siXwU9wcQWUhmUNw==} + + '@zag-js/pagination@1.38.2': + resolution: {integrity: sha512-pqdR7Eek6FFK4EyVRZbxwRymvunguGVAZAHrg0Aij/CnPbZZATDk4gQbGKpIgEywSYSk6m4hurjLg8L+shkfVQ==} + + '@zag-js/password-input@1.38.2': + resolution: {integrity: sha512-kDtuYq77QCzUF9oOGgHzmdlOHAvLj/syMnx+Sb4KEwBAJCIBrpJKymgEUxK4VumySApvI6OMmuCZN9uRvB0HrA==} + + '@zag-js/pin-input@1.38.2': + resolution: {integrity: sha512-kMmlRTOLBqxVaBM4Ly7NZi4VbB5SJUfcmyj6pxHV+YSqzFpuS8/o24CTtbLWUg9Zh4wSuM7vnx8A0viWg92V2Q==} + + '@zag-js/popover@1.38.2': + resolution: {integrity: sha512-LPA4Ld/eD34/C+/jsojXU/GiG9Y4rYN1xFAJIeOdUGF9fAvMVr+TEhvNUnVV7FnIbK5hdFOy7EgAAEL34uqBow==} + + '@zag-js/popper@1.38.2': + resolution: {integrity: sha512-iqAFCeximjd2SGbsciXxbrHdrB/T5jeV4bf5KAPPGNM8Ce/dMcz5RteAArqBYJt1RDpB5px4RdeO8y+8vZOK/w==} + + '@zag-js/presence@1.38.2': + resolution: {integrity: sha512-jDBN6Jw6+oDZT7yms1/ZyfwTZjKI8wHpAOPdOOD+uwoJSW/zCrwyymLRf28QVqCfr60gD/oaHt3azKA8sO6ySQ==} + + '@zag-js/progress@1.38.2': + resolution: {integrity: sha512-PY8fD2whXqLWafcIAOt8puFcLtnzutk9z074bWrWAV0dhUAzCTuMXF4hPrB/Qf+11CHxZZS467zp8WbgdPhIEw==} + + '@zag-js/qr-code@1.38.2': + resolution: {integrity: sha512-CdPhZ+RfT3DXAKCJr/QY1Sud/b1PDbIpJ2zJxCQI5FyOBK1+rS/FOZ106R5E57+o8pFxBq7BXjp3lMPHYBsyag==} + + '@zag-js/radio-group@1.38.2': + resolution: {integrity: sha512-hyQq2AVCov/lgtfXraA/y2cKd9NKKdzXJT7sO2/59eKHyRYMylV3oatcQ2WqjrrnPxEfqLcQ7CWxl7NlcdnTow==} + + '@zag-js/rating-group@1.38.2': + resolution: {integrity: sha512-MggGwIcMWVvuRgDZJPnEvGnk5xYarbXOJnnP7BQK/31OgjRZmxwAAUKxHFDGY8vxRQ2XVTwmrPNTUVDDpQUJxQ==} + + '@zag-js/rect-utils@1.38.2': + resolution: {integrity: sha512-VX/EHbtq++h+rFGrj/ql3EFPuJwFQLAYjOxyMvEKF34L8N5imDodIyVqrQfumZl5MMGmKstz/x1kL4nHYAxyWQ==} + + '@zag-js/remove-scroll@1.38.2': + resolution: {integrity: sha512-2NInlGgJmMQKMOyd5J2pc+L9/wj4NBhz028VwE31yxCK4dm3swcm5NYHdBAdpNG96ymximJsJndTkry7pnNNIA==} + + '@zag-js/scroll-area@1.38.2': + resolution: {integrity: sha512-Z9XuPizDWb3G4RVY6KMj8TpGuurT7C6DQhqMCWfqZkVXwjvC/DOZVDeIxwFOMl/i59UawIL7ttpZCxieeaQhNQ==} + + '@zag-js/scroll-snap@1.38.2': + resolution: {integrity: sha512-Z3+B++TIv39ZpVES9eaL2qK8UCONctkIKLXzBMDsK1gq0RfTt2xS765D1TH4bitvSc2c0x0ojHCf+Ui99CkXcQ==} + + '@zag-js/select@1.38.2': + resolution: {integrity: sha512-F0qOw0ntyVO1G80iwktVFF3XYUodP9Tfet5XrCmDN+kM2trumn94wV+1plE/7/SRjDED7uwaCSpaBXaFiv8KUw==} + + '@zag-js/signature-pad@1.38.2': + resolution: {integrity: sha512-8ZY0cvESLNEKV3dTRLY2lwIaRpgABOSbUsCEi5Aa1w0s/kdUnlwLoJdK96ryE5yb+65lU1q4VvUT40rqIFxX8w==} + + '@zag-js/slider@1.38.2': + resolution: {integrity: sha512-EBiCCLpwOA5eMcFgIMcAxNKQDePUqcVE+tTePteUVzzRONK1F/HLsLevIkpdJ4UouhPcDSsu5o2AH+w3bxW4sA==} + + '@zag-js/solid@1.38.2': + resolution: {integrity: sha512-vOiQSj0co2xgZmE/uBP/ac20Ha5JcHlGHPR/WthLV7yhEXdolDhOc8l6QdQDRNw2ACZ3bbMnFmxvDOTqsPULcw==} + peerDependencies: + solid-js: '>=1.1.3' + + '@zag-js/splitter@1.38.2': + resolution: {integrity: sha512-Z7lQPTGJWv9GGrFHeg0OvpK6KpnCnoqnVS665rkLaAfmVhak84sh9lJsRiuzm68dobXmbgP/OaD5u0xHo2hgDQ==} + + '@zag-js/steps@1.38.2': + resolution: {integrity: sha512-5fxZVSrgJk5YElhdgoM2p4YhZd9HNVjzKg5uLj4966MOE7jGITUCfvEybMXI+/eOlrh+7CX9Y+iG0PGj+p5wVg==} + + '@zag-js/store@1.38.2': + resolution: {integrity: sha512-TlJM6M2dW5W9jr6US4/R/VobLmlFrSv48ls18XffmXp+uuwZ7Aisciq0Djtwx7Y7e5KhlUWtk4ncE5PtjP4AzQ==} + + '@zag-js/switch@1.38.2': + resolution: {integrity: sha512-EIK0DO8cFcDqE6cobEh66O07nufwQnRnpVvgOWpoB13Ts/LyQ/HUxF377JRPa4EYIY2/hIt6L8zvwfgFHUGAMg==} + + '@zag-js/tabs@1.38.2': + resolution: {integrity: sha512-yqCKQ+ugYtwAuF8ya6fkI577Yz96apRwHDawPem4xRq381YC5TYDrLpWvRj4UjakPOjs+1mHf2+53RYQhhEpEg==} + + '@zag-js/tags-input@1.38.2': + resolution: {integrity: sha512-WV7uz5UGXThL8PqTor8tFKtLsTO79B7mRjoVNwgSKJ473C+Z9tFhJx+S6cPXvSx0PcDxYBX1DXI0NFWVgWVD4Q==} + + '@zag-js/timer@1.38.2': + resolution: {integrity: sha512-CwF6F4CuT4pKcOsL2t3gKLi3l4Sbr13eGGuZyJTn1fwjPsepEdq4ppxKto+/pYC30pzyrQRRqsL59H4k757z/A==} + + '@zag-js/toast@1.38.2': + resolution: {integrity: sha512-VRTfTjRrnn9U8RVKr2dTUWBzMBALNy5PMxr+z7Q1hUufv0JYfzPsSkVXsbSjI5OZPklbJ5P1ugmUrtVrATyGHw==} + + '@zag-js/toggle-group@1.38.2': + resolution: {integrity: sha512-vl9hDypfxiwR3gcsai+lBwRcPR1NwwNVP8ZhS3mdMwi3v5Lr4R4UCZc0l2/qFPj+IQ0q01VIGJJbkkFCB7a3iQ==} + + '@zag-js/toggle@1.38.2': + resolution: {integrity: sha512-8Hk5E4IK7wkG6hk/PI1c9f1vcgVGxbVIDI7WqTk5EeCSIWSKvIGAsBSCuWK2N7nlDlXuUQ4Ns2tvNZF9SfBG7A==} + + '@zag-js/tooltip@1.38.2': + resolution: {integrity: sha512-pFVWc6KjLujIvyOz2CzFQu4e9NVC61yE0oQpjRIQwzZAICxxiPE3QeOtb0Aw3Jhd0n2Y7qxuLyFI6gcAxI9Sxg==} + + '@zag-js/tour@1.38.2': + resolution: {integrity: sha512-HwHN7gC30Epfz0VEQsgcnzUK71cpB/Wl/HXv9daeDgtXHdqhJcnpaRVthfQr8ebSEBBWWui4r8dAxGJxrtUing==} + + '@zag-js/tree-view@1.38.2': + resolution: {integrity: sha512-w2R+EI6/ovTjGI6Vu/z6pMS9ZUMjSsj7jAsm7ZKbfwNngSIJBAu+kdAAZbeovl9MW/gnNVgpuJuS6N4yqANdIw==} + + '@zag-js/types@1.38.2': + resolution: {integrity: sha512-fvUf3J4QOFAliuo5wHTO/awO0GKFAn5AHNOXbGH5IuoxIwa0oK5tSXVwdF8JE7ISOkQh4oB2fq3g4QjxmAXDyA==} + + '@zag-js/utils@1.38.2': + resolution: {integrity: sha512-8UuWDomJ5JLX/KSDjEA4poX9rzqkGL47RiSCuVod+qZpWD0jpyXP7Lf600GUFtirRAiuN3x1+7KE6Elt/0rxtw==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -9731,6 +10015,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + editorconfig@1.0.7: resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} engines: {node: '>=14'} @@ -11838,6 +12125,11 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lucide-solid@0.542.0: + resolution: {integrity: sha512-cMy0fZu9TEEm1IIP3BIXBJd07xJGeAYNebIE1Zti+7cnv1hJcHoNZx4+D2tHZIYKILoSB/Rhs5M0vdKzQTU33g==} + peerDependencies: + solid-js: ^1.4.7 + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -12867,6 +13159,9 @@ packages: perfect-debounce@2.1.0: resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + perfect-freehand@1.2.3: + resolution: {integrity: sha512-bHZSfqDHGNlPpgH2yxXgPHlQSPpEbo+qg7li0M78J9vNAi2yjwLeA4x79BEQhX44lEWpCLSFCeRZwpw0niiXPA==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -13277,6 +13572,12 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-compare@3.0.1: + resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + + proxy-memoize@3.0.1: + resolution: {integrity: sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==} + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -14593,6 +14894,9 @@ packages: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -15629,6 +15933,9 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} engines: {node: '>=12.7.0'} @@ -15944,6 +16251,76 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.0.2 + '@ark-ui/solid@5.35.0(solid-js@1.9.12)': + dependencies: + '@internationalized/date': 3.12.0 + '@zag-js/accordion': 1.38.2 + '@zag-js/anatomy': 1.38.2 + '@zag-js/angle-slider': 1.38.2 + '@zag-js/async-list': 1.38.2 + '@zag-js/auto-resize': 1.38.2 + '@zag-js/avatar': 1.38.2 + '@zag-js/carousel': 1.38.2 + '@zag-js/cascade-select': 1.38.2 + '@zag-js/checkbox': 1.38.2 + '@zag-js/clipboard': 1.38.2 + '@zag-js/collapsible': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/color-picker': 1.38.2 + '@zag-js/color-utils': 1.38.2 + '@zag-js/combobox': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/date-picker': 1.38.2(@internationalized/date@3.12.0) + '@zag-js/date-utils': 1.38.2(@internationalized/date@3.12.0) + '@zag-js/dialog': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/drawer': 1.38.2 + '@zag-js/editable': 1.38.2 + '@zag-js/file-upload': 1.38.2 + '@zag-js/file-utils': 1.38.2 + '@zag-js/floating-panel': 1.38.2 + '@zag-js/focus-trap': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/highlight-word': 1.38.2 + '@zag-js/hover-card': 1.38.2 + '@zag-js/i18n-utils': 1.38.2 + '@zag-js/image-cropper': 1.38.2 + '@zag-js/json-tree-utils': 1.38.2 + '@zag-js/listbox': 1.38.2 + '@zag-js/marquee': 1.38.2 + '@zag-js/menu': 1.38.2 + '@zag-js/navigation-menu': 1.38.2 + '@zag-js/number-input': 1.38.2 + '@zag-js/pagination': 1.38.2 + '@zag-js/password-input': 1.38.2 + '@zag-js/pin-input': 1.38.2 + '@zag-js/popover': 1.38.2 + '@zag-js/presence': 1.38.2 + '@zag-js/progress': 1.38.2 + '@zag-js/qr-code': 1.38.2 + '@zag-js/radio-group': 1.38.2 + '@zag-js/rating-group': 1.38.2 + '@zag-js/scroll-area': 1.38.2 + '@zag-js/select': 1.38.2 + '@zag-js/signature-pad': 1.38.2 + '@zag-js/slider': 1.38.2 + '@zag-js/solid': 1.38.2(solid-js@1.9.12) + '@zag-js/splitter': 1.38.2 + '@zag-js/steps': 1.38.2 + '@zag-js/switch': 1.38.2 + '@zag-js/tabs': 1.38.2 + '@zag-js/tags-input': 1.38.2 + '@zag-js/timer': 1.38.2 + '@zag-js/toast': 1.38.2 + '@zag-js/toggle': 1.38.2 + '@zag-js/toggle-group': 1.38.2 + '@zag-js/tooltip': 1.38.2 + '@zag-js/tour': 1.38.2 + '@zag-js/tree-view': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + solid-js: 1.9.12 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -17557,11 +17934,20 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.9 + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + '@floating-ui/dom@1.7.1': dependencies: '@floating-ui/core': 1.7.1 '@floating-ui/utils': 0.2.9 + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + '@floating-ui/react-dom@2.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@floating-ui/dom': 1.7.1 @@ -17574,6 +17960,8 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + '@floating-ui/utils@0.2.11': {} + '@floating-ui/utils@0.2.9': {} '@formatjs/ecma402-abstract@2.3.6': @@ -22691,6 +23079,10 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@solid-primitives/keyed@1.5.3(solid-js@1.9.12)': + dependencies: + solid-js: 1.9.12 + '@solidjs/testing-library@0.8.10(solid-js@1.9.12)': dependencies: '@testing-library/dom': 10.4.0 @@ -24208,6 +24600,562 @@ snapshots: '@xtuc/long@4.2.2': {} + '@zag-js/accordion@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/anatomy@1.38.2': {} + + '@zag-js/angle-slider@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/rect-utils': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/aria-hidden@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/async-list@1.38.2': + dependencies: + '@zag-js/core': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/auto-resize@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/avatar@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/carousel@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/scroll-snap': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/cascade-select@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/rect-utils': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/checkbox@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/clipboard@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/collapsible@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/collection@1.38.2': + dependencies: + '@zag-js/utils': 1.38.2 + + '@zag-js/color-picker@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/color-utils': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/color-utils@1.38.2': + dependencies: + '@zag-js/utils': 1.38.2 + + '@zag-js/combobox@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/aria-hidden': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/core@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/date-picker@1.38.2(@internationalized/date@3.12.0)': + dependencies: + '@internationalized/date': 3.12.0 + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/date-utils': 1.38.2(@internationalized/date@3.12.0) + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/live-region': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/date-utils@1.38.2(@internationalized/date@3.12.0)': + dependencies: + '@internationalized/date': 3.12.0 + + '@zag-js/dialog@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/aria-hidden': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-trap': 1.38.2 + '@zag-js/remove-scroll': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/dismissable@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + '@zag-js/interact-outside': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/dom-query@1.38.2': + dependencies: + '@zag-js/types': 1.38.2 + + '@zag-js/drawer@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/aria-hidden': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-trap': 1.38.2 + '@zag-js/remove-scroll': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/editable@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/interact-outside': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/file-upload@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/file-utils': 1.38.2 + '@zag-js/i18n-utils': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/file-utils@1.38.2': + dependencies: + '@zag-js/i18n-utils': 1.38.2 + + '@zag-js/floating-panel@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/rect-utils': 1.38.2 + '@zag-js/store': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/focus-trap@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/focus-visible@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/highlight-word@1.38.2': {} + + '@zag-js/hover-card@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/i18n-utils@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/image-cropper@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/interact-outside@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/json-tree-utils@1.38.2': {} + + '@zag-js/listbox@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/live-region@1.38.2': {} + + '@zag-js/marquee@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/menu@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/rect-utils': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/navigation-menu@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/number-input@1.38.2': + dependencies: + '@internationalized/number': 3.6.5 + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/pagination@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/password-input@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/pin-input@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/popover@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/aria-hidden': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-trap': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/remove-scroll': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/popper@1.38.2': + dependencies: + '@floating-ui/dom': 1.7.6 + '@zag-js/dom-query': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/presence@1.38.2': + dependencies: + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + + '@zag-js/progress@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/qr-code@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + proxy-memoize: 3.0.1 + uqr: 0.1.2 + + '@zag-js/radio-group@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/rating-group@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/rect-utils@1.38.2': {} + + '@zag-js/remove-scroll@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/scroll-area@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/scroll-snap@1.38.2': + dependencies: + '@zag-js/dom-query': 1.38.2 + + '@zag-js/select@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/signature-pad@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + perfect-freehand: 1.2.3 + + '@zag-js/slider@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/solid@1.38.2(solid-js@1.9.12)': + dependencies: + '@solid-primitives/keyed': 1.5.3(solid-js@1.9.12) + '@zag-js/core': 1.38.2 + '@zag-js/store': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + solid-js: 1.9.12 + + '@zag-js/splitter@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/steps@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/store@1.38.2': + dependencies: + proxy-compare: 3.0.1 + + '@zag-js/switch@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/tabs@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/tags-input@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/auto-resize': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/interact-outside': 1.38.2 + '@zag-js/live-region': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/timer@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/toast@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/toggle-group@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/toggle@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/tooltip@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-visible': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/tour@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dismissable': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/focus-trap': 1.38.2 + '@zag-js/interact-outside': 1.38.2 + '@zag-js/popper': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/tree-view@1.38.2': + dependencies: + '@zag-js/anatomy': 1.38.2 + '@zag-js/collection': 1.38.2 + '@zag-js/core': 1.38.2 + '@zag-js/dom-query': 1.38.2 + '@zag-js/types': 1.38.2 + '@zag-js/utils': 1.38.2 + + '@zag-js/types@1.38.2': + dependencies: + csstype: 3.2.3 + + '@zag-js/utils@1.38.2': {} + abbrev@2.0.0: {} abbrev@3.0.1: {} @@ -25725,6 +26673,11 @@ snapshots: eastasianwidth@0.2.0: {} + echarts@5.6.0: + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + editorconfig@1.0.7: dependencies: '@one-ini/wasm': 0.1.1 @@ -26045,8 +26998,8 @@ snapshots: '@next/eslint-plugin-next': 16.1.6 eslint: 9.29.0(jiti@2.6.1) 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-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.29.0(jiti@2.6.1)) @@ -26072,7 +27025,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -26083,22 +27036,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) 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@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-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@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) 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-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -26109,7 +27062,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@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-plugin-import@2.32.0(@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)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -28245,6 +29198,10 @@ snapshots: dependencies: react: 19.2.3 + lucide-solid@0.542.0(solid-js@1.9.12): + dependencies: + solid-js: 1.9.12 + lz-string@1.5.0: {} magic-regexp@0.10.0: @@ -30092,6 +31049,8 @@ snapshots: perfect-debounce@2.1.0: {} + perfect-freehand@1.2.3: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -30509,6 +31468,12 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-compare@3.0.1: {} + + proxy-memoize@3.0.1: + dependencies: + proxy-compare: 3.0.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -32433,6 +33398,8 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@2.3.0: {} + tslib@2.8.1: {} tsx@4.20.3: @@ -33509,6 +34476,10 @@ snapshots: zod@4.3.6: {} + zrender@5.6.1: + dependencies: + tslib: 2.3.0 + zustand@4.5.7(@types/react@19.2.14)(react@19.2.4): dependencies: use-sync-external-store: 1.5.0(react@19.2.4) From b687b71e0c26e0738accc6a5ae9201814b54185f Mon Sep 17 00:00:00 2001 From: Nizar Izzuddin Yatim Fadlan Date: Wed, 8 Apr 2026 21:25:47 +0700 Subject: [PATCH 3/3] feat(solid-chat): add form fields and divider Add InputField, TextAreaField, SelectField, ToggleField, and Divider components and wire them into the example library and Stack layout. These components enable form-like rendering and two-way binding using state references so example UIs can collect and reflect user input more naturally. feat(solid-chat): extend text tones and structured button actions Add additional TextContent tones (success, warning, danger, info) and allow Button action to accept structured action objects (open_url and continue_conversation) in addition to simple action strings. This expands the expressiveness of UI primitives and enables richer status styling and actionable button semantics from generated content. feat(solid-chat): add action schemas and parser Introduce Zod schemas for open_url and continue_conversation actions and an actionSchema union used by the prompt generator and library. Add a parser created from the library JSON schema for runtime parsing of assistant responses. These additions make action payloads explicit and allow the UI to validate and interpret structured actions produced by the assistant. feat(solid-chat): add raw/parsed/thinking views and UI improvements Enhance the chat UI with a parser-backed parsed view, a raw response panel, a thinking/debug panel, prompt starters, responsive compact mode, and auto-scroll for streaming assistant messages. Expand assistant message shape to capture raw, thinking, and reasoning --- examples/solid-chat/README.md | 9 +- .../solid-chat/generated/system-prompt.txt | 56 +- .../solid-chat/scripts/generate-prompt.mjs | 132 +++- examples/solid-chat/src/App.tsx | 687 ++++++++++++++---- examples/solid-chat/src/components/Button.tsx | 91 ++- .../solid-chat/src/components/Divider.tsx | 36 + .../solid-chat/src/components/InputField.tsx | 56 ++ .../solid-chat/src/components/SelectField.tsx | 57 ++ examples/solid-chat/src/components/Stack.tsx | 5 + .../src/components/TextAreaField.tsx | 58 ++ .../solid-chat/src/components/TextContent.tsx | 13 +- .../solid-chat/src/components/ToggleField.tsx | 77 ++ examples/solid-chat/src/lib/library.tsx | 138 +++- examples/solid-chat/vite.config.ts | 27 +- 14 files changed, 1230 insertions(+), 212 deletions(-) create mode 100644 examples/solid-chat/src/components/Divider.tsx create mode 100644 examples/solid-chat/src/components/InputField.tsx create mode 100644 examples/solid-chat/src/components/SelectField.tsx create mode 100644 examples/solid-chat/src/components/TextAreaField.tsx create mode 100644 examples/solid-chat/src/components/ToggleField.tsx diff --git a/examples/solid-chat/README.md b/examples/solid-chat/README.md index 4dd6ce6b0..6ccc3c93f 100644 --- a/examples/solid-chat/README.md +++ b/examples/solid-chat/README.md @@ -45,10 +45,15 @@ OPENAI_API_KEY=sk-... - OpenUI components live in `src/components` and are registered in `src/lib/library.tsx`. - Chat input uses `@ark-ui/solid` (`Field`). - The `Chart` component uses `echarts` (bar/line/pie/doughnut). +- Form-like rendering components are also available: `InputField`, `TextAreaField`, `SelectField`, `ToggleField`, and `Divider`. +- Form fields support `$state` binding (for example, `InputField("Email", "name@example.com", $email, "email")`) and will write values back into renderer form state. +- `Button` supports both simple action type strings and structured action objects from `Action(...)` expressions. - The system prompt is generated into `examples/solid-chat/generated/system-prompt.txt` for easier review and iteration. -## Example Dashboard Prompts +## Example Prompts - `Build a weekly SaaS business dashboard with KPIs, revenue trend, and channel mix charts` - `Create a support operations dashboard with ticket volume, SLA trend, and follow-up actions` -- `Create a Q1 executive dashboard with 3 metric cards and 2 charts` +- `Build a release readiness view with milestones, blockers, and next actions` +- `Create an onboarding status page with progress timeline and action buttons` +- `Create an account settings form with profile inputs, notification toggles, and save actions` diff --git a/examples/solid-chat/generated/system-prompt.txt b/examples/solid-chat/generated/system-prompt.txt index 5c171e1c0..235e7cf5f 100644 --- a/examples/solid-chat/generated/system-prompt.txt +++ b/examples/solid-chat/generated/system-prompt.txt @@ -17,15 +17,20 @@ Arguments marked with ? are optional. Sub-components can be inline or referenced Props typed `ActionExpression` accept an Action([@steps...]) expression. See the Action section for available steps (@ToAssistant, @OpenUrl). Props marked `$binding` accept a `$variable` reference for two-way binding. -TextContent(text: string, tone?: "normal" | "muted" | "strong") — Displays a block of text. -Button(label: string, action?: string, variant?: "primary" | "secondary" | "ghost") — A clickable button. +TextContent(text: string, tone?: "normal" | "muted" | "strong" | "success" | "warning" | "danger" | "info") — Displays a block of text. +Button(label: string, action?: string | {type: "open_url", url: string} | {type: "continue_conversation", context?: string}, variant?: "primary" | "secondary" | "ghost") — A clickable button. Chart(title: string, type: "bar" | "line" | "pie" | "doughnut", labels: string[], values: number[], datasetLabel?: string) — Renders a chart. Badge(label: string, tone?: "neutral" | "success" | "warning" | "danger" | "info") — Small status pill. KpiTile(label: string, value: string, delta?: string, trend?: "up" | "down" | "neutral") — Compact KPI tile with value and trend. MetricList(title: string, items: {label: string, value: string}[]) — Two-column labeled metric list. Timeline(title: string, items: {title: string, detail: string, status?: "done" | "active" | "next"}[]) — Progress timeline with status dots. -Card(title: string, children: (TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline)[], subtitle?: string, variant?: "default" | "glass" | "accent", highlight?: string) — Card container with title and children. -Stack(children: (Card | TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline)[]) — Dashboard layout container. Use as root. +InputField(label: string, placeholder?: string, value?: string, type?: "text" | "email" | "password" | "number" | "url") — Single-line text input with label. +TextAreaField(label: string, placeholder?: string, value?: string, rows?: number) — Multi-line text input with label. +SelectField(label: string, options: string[], selected?: string) — Dropdown-style field with selectable options. +ToggleField(label: string, checked?: boolean) — On/off setting toggle with label. +Divider(label?: string) — Visual divider line with optional label. +Card(title: string, children: (TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline | InputField | TextAreaField | SelectField | ToggleField | Divider)[], subtitle?: string, variant?: "default" | "glass" | "accent", highlight?: string) — Card container with title and children. +Stack(children: (Card | TextContent | Button | Chart | Badge | KpiTile | MetricList | Timeline | InputField | TextAreaField | SelectField | ToggleField | Divider)[]) — Dashboard layout container. Use as root. ## Built-in Functions @@ -88,22 +93,19 @@ Always write the root = Stack(...) statement first so the UI shell appears immed ## Examples -User: Build a SaaS KPI dashboard - -root = Stack([title, healthy, m1, m2, m3, m4, mainCard, action1, action2]) -title = TextContent("Q2 Growth Pulse", "strong") -healthy = Badge("Healthy", "success") -m1 = KpiTile("MRR", "$128.4k", "+8.2% MoM", "up") -m2 = KpiTile("New Users", "1,248", "+12.6% WoW", "up") -m3 = KpiTile("Churn", "2.4%", "-0.3pp", "up") -m4 = KpiTile("NPS", "47", "+2", "up") -trend = Chart("Revenue Trend", "line", ["Jan", "Feb", "Mar", "Apr", "May"], [92, 98, 107, 119, 128], "$k") -mix = Chart("Traffic Sources", "pie", ["Organic", "Paid", "Referral", "Social"], [44, 28, 18, 10], "%") -ops = MetricList("Ops Snapshot", [{"label":"Avg response", "value":"4.9h"}, {"label":"Backlog", "value":"31"}]) -flow = Timeline("Delivery Steps", [{"title":"Design", "detail":"Completed", "status":"done"}, {"title":"Dev", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Planned", "status":"next"}]) -mainCard = Card("Executive Dashboard", [trend, mix, ops, flow], "Weekly status and channel mix", "glass", "Live") -action1 = Button("Show risk drivers", "continue-conversation", "primary") -action2 = Button("Compare with previous quarter", "continue-conversation", "ghost") +User: Build a weekly product status view + +root = Stack([title, summary, panel, action]) +title = TextContent("Product Delivery Overview", "strong") +summary = TextContent("Current sprint highlights, blockers, and next actions.", "muted") +state = Badge("On Track", "success") +velocity = Chart("Story Points", "bar", ["W1", "W2", "W3", "W4"], [28, 31, 35, 33], "pts") +burn = Chart("Defect Trend", "line", ["W1", "W2", "W3", "W4"], [14, 11, 9, 7], "count") +metrics = MetricList("Team Metrics", [{"label":"Cycle time", "value":"3.2d"}, {"label":"PR review", "value":"6.8h"}, {"label":"Escaped bugs", "value":"2"}]) +steps = Timeline("Release Steps", [{"title":"Scope lock", "detail":"Done", "status":"done"}, {"title":"UAT", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Scheduled Friday", "status":"next"}]) +panel = Card("Delivery Health", [state, velocity, burn, metrics, steps], "Sprint 18 status", "glass", "Updated") +action = Button("Generate rollback checklist", "continue-conversation", "primary") + ## Important Rules - When asked about data, generate realistic/plausible data @@ -115,12 +117,20 @@ Before finishing, walk your output and verify: 2. Every referenced name is defined. Every defined name (other than root) is reachable from root. - Always use Stack as the root component. +- The FIRST line must be exactly a root assignment: root = Stack(...). +- Do not output any statement before root = Stack(...). - Use Stack only as root. Never place Stack inside Card children. - Output openui-lang code only. No markdown fences, no explanations, no think blocks. - All visible text must be English. +- Start directly with openui-lang code. Do not add preface text. +- Keep syntax valid while streaming: no TODO bullets, no pseudo-code, no planning sections. +- Match the requested UI type; do not force dashboard layouts for every request. - Use references for readability and better streaming behavior. -- For dashboards, include at least four KpiTile components and at least two charts. -- Prefer Card with subtitle and highlight for richer hierarchy. -- Use Badge, MetricList, and Timeline when relevant instead of plain text-only layouts. +- Prefer Card subtitle/highlight only when it improves hierarchy. +- Use Badge, MetricList, Timeline, Chart, and KpiTile when relevant. +- Use InputField, TextAreaField, SelectField, ToggleField, and Divider for form or settings UIs. +- Use state bindings in forms when needed (e.g. InputField("Email", "name@example.com", $email, "email")). +- Button action supports both simple action type strings and structured action objects. - Use positional arguments only. +- Prefer inline composition in Stack/Card to reduce forward-reference errors. - Never output reasoning or markdown. \ No newline at end of file diff --git a/examples/solid-chat/scripts/generate-prompt.mjs b/examples/solid-chat/scripts/generate-prompt.mjs index c880c8fdd..01ad58bac 100644 --- a/examples/solid-chat/scripts/generate-prompt.mjs +++ b/examples/solid-chat/scripts/generate-prompt.mjs @@ -7,11 +7,27 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); const jiti = createJiti(import.meta.url); const { createLibrary, defineComponent } = await jiti.import("@openuidev/lang-core"); +const { BuiltinActionType } = await jiti.import("@openuidev/lang-core"); const { z } = await jiti.import("zod"); +const continueConversationAction = z.object({ + type: z.literal(BuiltinActionType.ContinueConversation), + context: z.string().optional(), +}); + +const openUrlAction = z.object({ + type: z.literal(BuiltinActionType.OpenUrl), + url: z.string(), +}); + +const actionSchema = z.union([openUrlAction, continueConversationAction]); + const TextContentDef = defineComponent({ name: "TextContent", - props: z.object({ text: z.string(), tone: z.enum(["normal", "muted", "strong"]).optional() }), + props: z.object({ + text: z.string(), + tone: z.enum(["normal", "muted", "strong", "success", "warning", "danger", "info"]).optional(), + }), description: "Displays a block of text.", component: null, }); @@ -20,7 +36,7 @@ const ButtonDef = defineComponent({ name: "Button", props: z.object({ label: z.string(), - action: z.string().optional(), + action: z.union([z.string(), actionSchema]).optional(), variant: z.enum(["primary", "secondary", "ghost"]).optional(), }), description: "A clickable button.", @@ -88,6 +104,60 @@ const ChartDef = defineComponent({ component: null, }); +const InputFieldDef = defineComponent({ + name: "InputField", + props: z.object({ + label: z.string(), + placeholder: z.string().optional(), + value: z.string().or(z.any()).optional(), + type: z.enum(["text", "email", "password", "number", "url"]).optional(), + }), + description: "Single-line text input with label.", + component: null, +}); + +const TextAreaFieldDef = defineComponent({ + name: "TextAreaField", + props: z.object({ + label: z.string(), + placeholder: z.string().optional(), + value: z.string().or(z.any()).optional(), + rows: z.number().optional(), + }), + description: "Multi-line text input with label.", + component: null, +}); + +const SelectFieldDef = defineComponent({ + name: "SelectField", + props: z.object({ + label: z.string(), + options: z.array(z.string()), + selected: z.string().or(z.any()).optional(), + }), + description: "Dropdown-style field with selectable options.", + component: null, +}); + +const ToggleFieldDef = defineComponent({ + name: "ToggleField", + props: z.object({ + label: z.string(), + checked: z.boolean().or(z.any()).optional(), + }), + description: "On/off setting toggle with label.", + component: null, +}); + +const DividerDef = defineComponent({ + name: "Divider", + props: z.object({ + label: z.string().optional(), + }), + description: "Visual divider line with optional label.", + component: null, +}); + const CardDef = defineComponent({ name: "Card", props: z.object({ @@ -101,6 +171,11 @@ const CardDef = defineComponent({ KpiTileDef.ref, MetricListDef.ref, TimelineDef.ref, + InputFieldDef.ref, + TextAreaFieldDef.ref, + SelectFieldDef.ref, + ToggleFieldDef.ref, + DividerDef.ref, ]), ), subtitle: z.string().optional(), @@ -124,6 +199,11 @@ const StackDef = defineComponent({ KpiTileDef.ref, MetricListDef.ref, TimelineDef.ref, + InputFieldDef.ref, + TextAreaFieldDef.ref, + SelectFieldDef.ref, + ToggleFieldDef.ref, + DividerDef.ref, ]), ), }), @@ -140,6 +220,11 @@ const library = createLibrary({ KpiTileDef, MetricListDef, TimelineDef, + InputFieldDef, + TextAreaFieldDef, + SelectFieldDef, + ToggleFieldDef, + DividerDef, CardDef, StackDef, ], @@ -149,33 +234,38 @@ const library = createLibrary({ const promptOptions = { additionalRules: [ "Always use Stack as the root component.", + "The FIRST line must be exactly a root assignment: root = Stack(...).", + "Do not output any statement before root = Stack(...).", "Use Stack only as root. Never place Stack inside Card children.", "Output openui-lang code only. No markdown fences, no explanations, no think blocks.", "All visible text must be English.", + "Start directly with openui-lang code. Do not add preface text.", + "Keep syntax valid while streaming: no TODO bullets, no pseudo-code, no planning sections.", + "Match the requested UI type; do not force dashboard layouts for every request.", "Use references for readability and better streaming behavior.", - "For dashboards, include at least four KpiTile components and at least two charts.", - "Prefer Card with subtitle and highlight for richer hierarchy.", - "Use Badge, MetricList, and Timeline when relevant instead of plain text-only layouts.", + "Prefer Card subtitle/highlight only when it improves hierarchy.", + "Use Badge, MetricList, Timeline, Chart, and KpiTile when relevant.", + "Use InputField, TextAreaField, SelectField, ToggleField, and Divider for form or settings UIs.", + 'Use state bindings in forms when needed (e.g. InputField("Email", "name@example.com", $email, "email")).', + "Button action supports both simple action type strings and structured action objects.", "Use positional arguments only.", + "Prefer inline composition in Stack/Card to reduce forward-reference errors.", "Never output reasoning or markdown.", ], examples: [ - `User: Build a SaaS KPI dashboard - -root = Stack([title, healthy, m1, m2, m3, m4, mainCard, action1, action2]) -title = TextContent("Q2 Growth Pulse", "strong") -healthy = Badge("Healthy", "success") -m1 = KpiTile("MRR", "$128.4k", "+8.2% MoM", "up") -m2 = KpiTile("New Users", "1,248", "+12.6% WoW", "up") -m3 = KpiTile("Churn", "2.4%", "-0.3pp", "up") -m4 = KpiTile("NPS", "47", "+2", "up") -trend = Chart("Revenue Trend", "line", ["Jan", "Feb", "Mar", "Apr", "May"], [92, 98, 107, 119, 128], "$k") -mix = Chart("Traffic Sources", "pie", ["Organic", "Paid", "Referral", "Social"], [44, 28, 18, 10], "%") -ops = MetricList("Ops Snapshot", [{"label":"Avg response", "value":"4.9h"}, {"label":"Backlog", "value":"31"}]) -flow = Timeline("Delivery Steps", [{"title":"Design", "detail":"Completed", "status":"done"}, {"title":"Dev", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Planned", "status":"next"}]) -mainCard = Card("Executive Dashboard", [trend, mix, ops, flow], "Weekly status and channel mix", "glass", "Live") -action1 = Button("Show risk drivers", "continue-conversation", "primary") -action2 = Button("Compare with previous quarter", "continue-conversation", "ghost")`, + `User: Build a weekly product status view + +root = Stack([title, summary, panel, action]) +title = TextContent("Product Delivery Overview", "strong") +summary = TextContent("Current sprint highlights, blockers, and next actions.", "muted") +state = Badge("On Track", "success") +velocity = Chart("Story Points", "bar", ["W1", "W2", "W3", "W4"], [28, 31, 35, 33], "pts") +burn = Chart("Defect Trend", "line", ["W1", "W2", "W3", "W4"], [14, 11, 9, 7], "count") +metrics = MetricList("Team Metrics", [{"label":"Cycle time", "value":"3.2d"}, {"label":"PR review", "value":"6.8h"}, {"label":"Escaped bugs", "value":"2"}]) +steps = Timeline("Release Steps", [{"title":"Scope lock", "detail":"Done", "status":"done"}, {"title":"UAT", "detail":"In progress", "status":"active"}, {"title":"Launch", "detail":"Scheduled Friday", "status":"next"}]) +panel = Card("Delivery Health", [state, velocity, burn, metrics, steps], "Sprint 18 status", "glass", "Updated") +action = Button("Generate rollback checklist", "continue-conversation", "primary") +`, ], }; diff --git a/examples/solid-chat/src/App.tsx b/examples/solid-chat/src/App.tsx index d113922b5..0e49955f4 100644 --- a/examples/solid-chat/src/App.tsx +++ b/examples/solid-chat/src/App.tsx @@ -1,16 +1,82 @@ import { Field } from "@ark-ui/solid/field"; -import { BuiltinActionType, Renderer, type ActionEvent } from "@openuidev/solid-lang"; -import { createSignal, For, Show } from "solid-js"; +import { + BuiltinActionType, + createParser, + Renderer, + type ActionEvent, + type ParseResult, +} from "@openuidev/solid-lang"; +import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"; import { library } from "./lib/library"; -type ChatMessage = - | { role: "user"; text: string } - | { role: "assistant"; response: string; streaming: boolean; started: boolean }; +type UserMessage = { role: "user"; text: string }; +type AssistantMessage = { + role: "assistant"; + raw: string; + response: string; + thinking: string; + reasoning: string; + thinkOpen: boolean; + sawThinkTag: boolean; + streaming: boolean; + started: boolean; +}; +type ChatMessage = UserMessage | AssistantMessage; + +const parser = createParser(library.toJSONSchema()); export default function App() { const [messages, setMessages] = createSignal([]); const [input, setInput] = createSignal(""); const [isLoading, setIsLoading] = createSignal(false); + const [showRawPanel, setShowRawPanel] = createSignal(false); + const [rawTab, setRawTab] = createSignal<"raw" | "parsed" | "thinking">("raw"); + const [isCompact, setIsCompact] = createSignal(false); + let rawPaneRef: HTMLDivElement | undefined; + let previewPaneRef: HTMLDivElement | undefined; + + const promptStarters = [ + "Weather dashboard for Bandung", + "Pricing comparison cards for 4 plans", + "Kanban board for launch tasks", + "Login form with validation hints", + "Customer support operations view", + ]; + + onMount(() => { + const onResize = () => setIsCompact(window.innerWidth < 980); + onResize(); + window.addEventListener("resize", onResize); + onCleanup(() => window.removeEventListener("resize", onResize)); + }); + + const latestAssistant = createMemo(() => { + const list = messages(); + for (let i = list.length - 1; i >= 0; i -= 1) { + const msg = list[i]; + if (msg?.role === "assistant") return msg; + } + return undefined; + }); + + const parsedResult = createMemo(() => { + const assistant = latestAssistant(); + if (!assistant?.response) return null; + try { + return parser.parse(assistant.response); + } catch { + return null; + } + }); + + createEffect(() => { + const assistant = latestAssistant(); + if (!assistant || !assistant.streaming) return; + requestAnimationFrame(() => { + if (rawPaneRef) rawPaneRef.scrollTo({ top: rawPaneRef.scrollHeight }); + if (previewPaneRef) previewPaneRef.scrollTo({ top: previewPaneRef.scrollHeight }); + }); + }); async function sendMessage(text: string) { const trimmed = text.trim(); @@ -21,17 +87,57 @@ export default function App() { setInput(""); setMessages((prev) => [ ...prev, - { role: "assistant", response: "", streaming: true, started: false }, + { + role: "assistant", + raw: "", + response: "", + thinking: "", + reasoning: "", + thinkOpen: false, + sawThinkTag: false, + streaming: true, + started: false, + }, ]); + const rootAssignRegex = /\broot\s*=\s*[A-Za-z_][A-Za-z0-9_]*\s*\(/; const openUiStartRegex = /[A-Za-z_][A-Za-z0-9_]*\s*=\s*[A-Za-z_][A-Za-z0-9_]*\s*\(/; - const sanitizeModelText = (input: string) => - input + const sanitizeModelText = (value: string) => + value .replace(/[\s\S]*?<\/think>/gi, "") + .replace(/<\/?think>/gi, "") .replace(/```openui-lang/gi, "") .replace(/```/g, ""); + const joinThinking = (reasoning: string, prelude: string) => + [reasoning.trim(), prelude.trim()].filter((part) => part.length > 0).join("\n\n"); + + const extractLiveThinkFromRaw = (rawText: string) => { + const openTag = ""; + const closeTag = ""; + const lastOpen = rawText.lastIndexOf(openTag); + const lastClose = rawText.lastIndexOf(closeTag); + if (lastOpen < 0 || lastOpen < lastClose) return { thinkOpen: false, thinking: "" }; + const thinking = rawText.slice(lastOpen + openTag.length).trim(); + return { thinkOpen: true, thinking }; + }; + + const extractOpenUi = (rawText: string) => { + const sanitizedRaw = sanitizeModelText(rawText); + const rootIndex = sanitizedRaw.search(rootAssignRegex); + const anyAssignIndex = sanitizedRaw.search(openUiStartRegex); + const startIndex = + anyAssignIndex >= 0 && rootIndex >= 0 + ? Math.min(anyAssignIndex, rootIndex) + : anyAssignIndex >= 0 + ? anyAssignIndex + : rootIndex; + const prelude = startIndex >= 0 ? sanitizedRaw.slice(0, startIndex) : sanitizedRaw; + const response = startIndex >= 0 ? sanitizedRaw.slice(startIndex) : ""; + return { prelude, response, started: response.trim().length > 0 }; + }; + const appendAssistantDelta = (delta: string) => { if (!delta) return; setMessages((prev) => { @@ -39,11 +145,24 @@ export default function App() { for (let i = next.length - 1; i >= 0; i -= 1) { const msg = next[i]; if (msg?.role === "assistant" && msg.streaming) { - const response = msg.response + delta; + const raw = msg.raw + delta; + const extracted = extractOpenUi(raw); + const sawThinkTag = + msg.sawThinkTag || raw.includes("") || raw.includes(""); + const liveThink = extractLiveThinkFromRaw(raw); + const thinking = sawThinkTag + ? liveThink.thinkOpen + ? liveThink.thinking + : "" + : joinThinking(msg.reasoning, extracted.prelude); next[i] = { ...msg, - response, - started: openUiStartRegex.test(sanitizeModelText(response)), + raw, + response: extracted.response, + thinking, + thinkOpen: sawThinkTag ? liveThink.thinkOpen : msg.reasoning.trim().length > 0, + sawThinkTag, + started: msg.started || extracted.started, }; break; } @@ -52,22 +171,57 @@ export default function App() { }); }; - const finishAssistantStream = (fallbackError?: string) => { + const appendThinkingDelta = (delta: string) => { + if (!delta) return; setMessages((prev) => { const next = [...prev]; for (let i = next.length - 1; i >= 0; i -= 1) { const msg = next[i]; if (msg?.role === "assistant" && msg.streaming) { - const raw = sanitizeModelText(msg.response || ""); - const assignIndex = raw.search( - /[A-Za-z_][A-Za-z0-9_]*\s*=\s*[A-Za-z_][A-Za-z0-9_]*\s*\(/, - ); - const cleaned = assignIndex >= 0 ? raw.slice(assignIndex).trim() : ""; + const reasoning = msg.reasoning + delta; + const extracted = extractOpenUi(msg.raw || ""); + const currentRaw = msg.raw || ""; + const sawThinkTag = + msg.sawThinkTag || currentRaw.includes("") || currentRaw.includes(""); + const liveThink = extractLiveThinkFromRaw(currentRaw); + const thinking = sawThinkTag + ? liveThink.thinkOpen + ? liveThink.thinking + : "" + : joinThinking(reasoning, extracted.prelude); + next[i] = { + ...msg, + reasoning, + thinking, + thinkOpen: sawThinkTag ? liveThink.thinkOpen : reasoning.trim().length > 0, + sawThinkTag, + }; + break; + } + } + return next; + }); + }; + const finishAssistantStream = (fallbackError?: string) => { + setMessages((prev) => { + const next = [...prev]; + for (let i = next.length - 1; i >= 0; i -= 1) { + const msg = next[i]; + if (msg?.role === "assistant" && msg.streaming) { + const extracted = extractOpenUi(msg.response || msg.raw || ""); + const cleaned = extracted.response.trim(); const response = cleaned || `t1 = TextContent("Error: ${(fallbackError || "Empty response").replaceAll('"', '\\"')}")\ncard = Card("LLM Stream Error", [t1])\nroot = Stack([card])`; - next[i] = { ...msg, response, streaming: false }; + next[i] = { + ...msg, + response, + streaming: false, + thinking: "", + thinkOpen: false, + started: msg.started || cleaned.length > 0, + }; break; } } @@ -111,8 +265,9 @@ export default function App() { if (!payload || payload === "[DONE]") continue; try { - const parsed = JSON.parse(payload) as { delta?: string }; + const parsed = JSON.parse(payload) as { delta?: string; thinkingDelta?: string }; if (parsed.delta) appendAssistantDelta(parsed.delta); + if (parsed.thinkingDelta) appendThinkingDelta(parsed.thinkingDelta); } catch { appendAssistantDelta(payload); } @@ -135,153 +290,423 @@ export default function App() { } } + const panelColumns = () => { + if (isCompact()) return "minmax(0, 1fr)"; + if (!showRawPanel()) return "minmax(0, 1fr)"; + return "minmax(320px, 0.95fr) minmax(0, 1.4fr)"; + }; + + const paneHeight = () => { + if (isCompact()) return "56vh"; + return "520px"; + }; + + const statusLabel = createMemo(() => { + const assistant = latestAssistant(); + if (!assistant) return "Ready"; + if (assistant.streaming && !assistant.started) return "Streaming"; + if (assistant.streaming && assistant.started) return "Rendering"; + return "Ready"; + }); + + const statusColor = createMemo(() => { + if (statusLabel() === "Streaming" || statusLabel() === "Rendering") return "#2563eb"; + return "#16a34a"; + }); + return (
-

- OpenUI Solid Dashboard Chat +

+ What do you want to build?

-

- Modern generative dashboard example using Ark UI and ECharts. -

+ +
{ + event.preventDefault(); + void sendMessage(input()); + }} + style={{ + display: "grid", + gap: "10px", + padding: "12px", + margin: "0 auto", + "max-width": "760px", + border: "1px solid rgba(148,163,184,0.28)", + "border-radius": "14px", + background: "#ffffff", + }} + > + + + setInput(event.currentTarget.value) + } + placeholder="Describe the UI you want to generate..." + style={{ + width: "100%", + padding: "12px", + border: "1px solid #dbe2ea", + "border-radius": "10px", + background: "#ffffff", + "box-sizing": "border-box", + }} + /> + + +
+ + + +
+
+ +
+ + {(starter) => ( + + )} + +
-
- - {(message) => ( -
- -
- {(message as { role: "user"; text: string }).text} -
+
+ +
+
+ + + +
+ +
+ Generated output will appear here.
+ } + > + {(assistant) => ( +
+                    {rawTab() === "raw"
+                      ? assistant().raw || "Waiting for model stream..."
+                      : rawTab() === "thinking"
+                        ? assistant().thinkOpen
+                          ? assistant().thinking || "Thinking..."
+                          : "No active thinking stream."
+                        : parsedResult()
+                          ? JSON.stringify(parsedResult(), null, 2)
+                          : "Parser has no stable result yet."}
+                  
+ )} +
+ +
- -
- +
+ PREVIEW + + {statusLabel()} + +
+ +
+ Rendered UI will appear here.
} + > + {(assistant) => ( + 0 || !assistant().streaming} + fallback={ +
+
+
Generating UI...
+
+ } + > + 0}> +
+ - Generating UI... -
- } - > - + Thinking + +
+                        {assistant().thinking}
+                      
+ -
-
-
- )} - + +
+ )} + +
+
-
{ - event.preventDefault(); - void sendMessage(input()); - }} - style={{ - display: "flex", - gap: "10px", - padding: "10px", - border: "1px solid rgba(148,163,184,0.28)", - "border-radius": "14px", - background: "#f8fafc", - }} - > - - - setInput(event.currentTarget.value) - } - placeholder="Ask anything..." - style={{ - width: "100%", - padding: "11px 12px", - border: "1px solid #cbd5e1", - "border-radius": "12px", - background: "#ffffff", - }} - /> - - -
- - +
); } + +function rawTabStyle(isActive: boolean) { + return { + padding: "5px 10px", + "font-size": "11px", + "font-weight": 700, + "letter-spacing": "0.04em", + border: isActive ? "1px solid #334155" : "1px solid transparent", + background: isActive ? "rgba(51,65,85,0.6)" : "transparent", + color: isActive ? "#f8fafc" : "#64748b", + "border-radius": "8px", + cursor: "pointer", + } as const; +} diff --git a/examples/solid-chat/src/components/Button.tsx b/examples/solid-chat/src/components/Button.tsx index 195c6c185..898e65486 100644 --- a/examples/solid-chat/src/components/Button.tsx +++ b/examples/solid-chat/src/components/Button.tsx @@ -1,14 +1,43 @@ -import { useTriggerAction } from "@openuidev/solid-lang"; +import { evaluate } from "@openuidev/lang-core"; +import { + BuiltinActionType, + useFormName, + useGetFieldValue, + useTriggerAction, +} from "@openuidev/solid-lang"; import { Send } from "lucide-solid"; interface ButtonProps { label: string; - action?: string; + action?: unknown; variant?: "primary" | "secondary" | "ghost"; } +interface ActionPlanStep { + type?: string; + message?: string; + context?: string; + url?: string; +} + +interface ActionPlanValue { + steps?: ActionPlanStep[]; +} + +function isActionPlan(value: unknown): value is ActionPlanValue { + return !!value && typeof value === "object" && Array.isArray((value as ActionPlanValue).steps); +} + +function normalizeActionType(type: string): string { + if (type === "continue-conversation") return BuiltinActionType.ContinueConversation; + if (type === "open-url") return BuiltinActionType.OpenUrl; + return type; +} + export function Button(props: { props: ButtonProps }) { const triggerAction = useTriggerAction(); + const formName = useFormName(); + const getFieldValue = useGetFieldValue(); const variant = props.props.variant || "secondary"; const variantStyles: Record< @@ -21,16 +50,60 @@ export function Button(props: { props: ButtonProps }) { }; const style = variantStyles[variant]; + const resolveActionPlan = () => { + const action = props.props.action; + if (!action || typeof action !== "object") return null; + + try { + const evaluated = evaluate(action as any, { + getState: (name) => getFieldValue(formName?.(), name), + resolveRef: () => null, + }); + return isActionPlan(evaluated) ? evaluated : null; + } catch { + return null; + } + }; + + const handleClick = () => { + const label = props.props.label; + const form = formName?.(); + + if (typeof props.props.action === "string") { + triggerAction(label, form, { type: normalizeActionType(props.props.action) }); + return; + } + + const plan = resolveActionPlan(); + if (!plan?.steps?.length) { + triggerAction(label, form); + return; + } + + for (const step of plan.steps) { + if (step.type === BuiltinActionType.ContinueConversation) { + triggerAction(step.message || label, form, { + type: BuiltinActionType.ContinueConversation, + params: step.context ? { context: step.context } : undefined, + }); + return; + } + if (step.type === BuiltinActionType.OpenUrl && step.url) { + triggerAction(label, form, { + type: BuiltinActionType.OpenUrl, + params: { url: step.url }, + }); + return; + } + } + + triggerAction(label, form); + }; + return (