diff --git a/.husky/pre-commit b/.husky/pre-commit index 80a5efd..cf79dfe 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -4,12 +4,12 @@ # # Add homebrew to PATH for non-interactive shells -export PATH="/Users/s/Library/pnpm:/opt/homebrew/bin:$PATH" +export PATH="/opt/homebrew/bin:$HOME/.local/bin:$PATH" echo "πŸ” Running Gitleaks to check for secrets..." # Check if gitleaks is installed -if ! command -v gitleaks &> /dev/null; then +if ! command -v gitleaks > /dev/null 2>&1; then echo "" echo "❌ ERROR: Gitleaks is not installed!" echo "" diff --git a/apps/docs/alpha-docs/BETA-FEATURES.md b/apps/docs/alpha-docs/BETA-FEATURES.md index e39eb55..80d997f 100644 --- a/apps/docs/alpha-docs/BETA-FEATURES.md +++ b/apps/docs/alpha-docs/BETA-FEATURES.md @@ -189,9 +189,9 @@ Comprehensive reference for all `csdk-*` CSS classes applied to every chat UI el ### 17. Generative UI β€” Experimental -The AI can render structured UI components (cards, tables, charts, stat tiles) inline inside the chat, generated from tool call results. Ships with four built-in renderers plus a hook for custom ones. +The AI can render rich HTML components inline inside the chat using Tailwind CSS and Chart.js, generated from tool call results. Ships with a single `HtmlRenderer` in a sandboxed iframe. -- **Renderers:** `CardRenderer` Β· `TableRenderer` Β· `StatRenderer` Β· `HtmlRenderer` +- **Renderer:** `HtmlRenderer` (sandboxed iframe with Tailwind CSS + Chart.js) - **API:** `import { generativeUITool, useGenerativeUI } from "@yourgpt/copilot-sdk/experimental"` β€” register `generativeUITool` as a tool, use `useGenerativeUI()` to render results - **Package:** `@yourgpt/copilot-sdk/experimental` - **Docs page:** `content/docs/generative-ui.mdx` (page existed on main; full demo + renderers added in beta) diff --git a/apps/docs/content/docs/generative-ui.mdx b/apps/docs/content/docs/generative-ui.mdx index b277df2..3ee5fa9 100644 --- a/apps/docs/content/docs/generative-ui.mdx +++ b/apps/docs/content/docs/generative-ui.mdx @@ -1,341 +1,312 @@ --- title: Generative UI -description: Render rich React components from AI tool results β€” per-tool custom renderers or AI-driven built-in components +description: Let the AI render rich visual UI β€” dashboards, charts, tables, cards β€” directly in the chat icon: AiMagic --- import { Callout } from 'fumadocs-ui/components/callout'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; -Instead of showing raw JSON or plain text from tool calls, render interactive UI directly inside the chat β€” from your own branded React components per tool, to fully AI-generated dashboards, charts, and layouts running in a sandboxed iframe. +Render rich, interactive UI directly inside the chat β€” from AI-generated dashboards and charts to your own branded React components per tool. --- ## Two Approaches -| | `toolRenderers` | `useGenerativeUI` (experimental) | +| | AI-Generated HTML | Tool Renderers | |---|---|---| -| **What it does** | Your React component renders per tool result | AI writes full HTML + Tailwind + Chart.js, runs in a sandboxed iframe β€” or picks a typed renderer (table, stat, card, chart) | -| **Who decides the UI** | You β€” one renderer per tool | The AI β€” generates or selects based on the data | -| **Setup** | Pass `toolRenderers` to `` | One `useGenerativeUI()` call + backend `generativeUITool()` | -| **Best for** | Domain-specific, branded components | Dashboards, charts, tables, any data layout you haven't pre-built | -| **Customization** | Full control | Override any built-in renderer | +| **What it does** | AI writes HTML + Tailwind + Chart.js, rendered in a sandboxed iframe | Your React component renders per tool result | +| **Who decides the UI** | The AI β€” generates any layout it wants | You β€” one renderer per tool | +| **Best for** | Dashboards, charts, tables, cards, any visual you haven't pre-built | Domain-specific, branded components with full interactivity | +| **Streaming** | Progressive β€” HTML builds up as AI generates | Shows loading β†’ final states | +| **Interactivity** | Via `copilot.action()` bridge | Native React (onClick, state, etc.) | + +Both approaches work together. Use AI-generated HTML for freeform visuals, and tool renderers for structured data with custom components. --- -## Approach 1 β€” `toolRenderers` +## AI-Generated HTML (Experimental) -Map tool names to React components. Each component receives the tool's args and result as props. + +`@yourgpt/copilot-sdk/experimental` β€” APIs may change without a semver major bump. + -### Basic example +The AI writes HTML with Tailwind CSS and Chart.js directly in the chat response. The SDK detects it, extracts the HTML, and renders it in a sandboxed iframe β€” progressively as the AI generates it. -```tsx -import { CopilotChat } from "@yourgpt/copilot-sdk/ui"; +This follows the same pattern as Claude Artifacts β€” content tags in the text stream, parsed by the frontend, rendered in an isolated iframe. -function WeatherCard({ data, status }) { - if (status === "executing") { - return
Loading weather...
; - } - return ( -
-

{data.city}

-

{data.temp}Β°F

-

{data.conditions}

-
- ); -} - - +``` +User: "Show me a dashboard of key web metrics" + ↓ +AI streams:
...
+ ↓ +SDK detects tags β†’ extracts HTML β†’ renders in iframe + ↓ +User sees: [Live dashboard with stats, charts, tables β€” streaming progressively] ``` -### ToolRendererProps +### Setup + + + -Every renderer receives these props: +Add the generative UI system prompt to your runtime: ```typescript -interface ToolRendererProps { - status: "pending" | "executing" | "completed" | "error" | "failed" | "rejected"; - args: Record; // arguments passed to the tool - data?: unknown; // result (when completed) - error?: string; // error message (when failed) - executionId: string; - toolName: string; -} +import { createRuntime } from "@yourgpt/llm-sdk"; +import { generativeUISystemPrompt } from "@yourgpt/copilot-sdk/experimental"; + +const runtime = createRuntime({ + provider, + model, + systemPrompt: generativeUISystemPrompt(), +}); ``` -### Handling all states +The system prompt instructs the AI to wrap visual content in `` tags using Tailwind CSS classes. Chart.js is available for charts. -```tsx -function ChartCard({ status, data, error, args }: ToolRendererProps) { - if (status === "pending" || status === "executing") { - return ( -
-
-

- Generating {args.metric} chart... -

-
- ); - } + + - if (status === "error" || status === "failed") { - return ( -
-

Failed to load chart

-

{error}

-
- ); - } +Call `useGenerativeUI()` and pass `wrapMessage` to your chat: - if (status === "rejected") { - return ( -
-

Chart request was declined

-
- ); - } +```tsx +import { useGenerativeUI } from "@yourgpt/copilot-sdk/experimental"; +import { CopilotChat } from "@yourgpt/copilot-sdk/ui"; - return ( -
-

{data.title}

- - - - - - - -
- ); +function App() { + const { wrapMessage } = useGenerativeUI(); + + return ; } ``` -### Interactive components +That's it. The SDK handles `` detection, iframe rendering, auto-height, streaming, and script deferral. -Renderers can be fully interactive and call back into the chat: +
+ -```tsx -function ProductCard({ data }: ToolRendererProps) { - const [quantity, setQuantity] = useState(1); - const { sendMessage } = useCopilot(); +### What the AI generates - return ( -
- {data.name} -

{data.name}

-

${data.price}

-
- setQuantity(Number(e.target.value))} - className="w-16 border rounded px-2 py-1" - /> - -
-
- ); -} +The AI writes HTML with Tailwind classes wrapped in `` tags: + +```html + +
+

Monthly Revenue

+

$84,200

+ +12% +
+
``` -### Control AI response verbosity +The iframe automatically has **Tailwind CSS** and **Chart.js** pre-loaded. The AI can build anything: tables, stat cards, charts, pricing pages, data grids. -Return `_aiResponseMode: "brief"` from your tool handler to prevent the AI from describing what the UI already shows: +### Chart.js support -```tsx -handler: async ({ timeRange }) => { - const data = await fetchDashboardData(timeRange); - return { - success: true, - data, - _aiResponseMode: "brief", - _aiContext: `Dashboard for ${timeRange}`, - }; -}, -``` +The AI can create charts using Chart.js with inline scripts: - -Use `_aiResponseMode: "brief"` when your UI component is self-explanatory. The AI gives a short acknowledgment instead of narrating the data. - +```html + +
+ + +
+
+``` ---- +Scripts are deferred during streaming and only execute once the full HTML arrives. -## Approach 2 β€” AI-Generated UI (Experimental) +### Interactive actions (bridge API) - -`@yourgpt/copilot-sdk/experimental` β€” APIs may change without a semver major bump. - +The AI-generated HTML can communicate back to your app via the `copilot` bridge: -The AI calls a single `render_ui` tool and generates the UI itself. The standout capability is `type: "html"` β€” the AI writes full HTML with Tailwind CSS and Chart.js, rendered in a sandboxed iframe. No pre-built component needed. For structured data it can also pick typed renderers (`table`, `stat`, `card`, `chart`) automatically. +```tsx +// Frontend β€” register action handlers +const { wrapMessage } = useGenerativeUI({ + actions: { + addToCart: (data) => cartStore.add(data.itemId), + navigate: (data) => router.push(data.url), + }, +}); +``` +```typescript +// Backend β€” tell the AI what actions are available +systemPrompt: generativeUISystemPrompt({ + actions: { + addToCart: "Add an item to the shopping cart. Params: { itemId: string, qty: number }", + navigate: "Navigate to a URL. Params: { url: string }", + }, +}), ``` -User: "Show Q1 revenue by region" - ↓ -AI calls: render_ui({ type: "chart", chartType: "bar", labels: ["NA","EU","APAC"], datasets: [...] }) - ↓ -UI renders: [Bar chart] -User: "Build an analytics dashboard" - ↓ -AI calls: render_ui({ type: "html", html: "
...
", height: "600px" }) - ↓ -UI renders: [Full dashboard in sandboxed iframe with Tailwind + Chart.js] +The AI can then write interactive buttons: + +```html + + ``` -### Setup +- `copilot.action(name, data)` β€” calls your registered handler, returns a result +- `copilot.sendMessage(text)` β€” sends a message in the chat as the user - - +### Custom design guidelines -Register `generativeUITool()` in your route. The key becomes the tool name. +Pass additional styling instructions to the system prompt: ```typescript -import { generativeUITool } from "@yourgpt/copilot-sdk/experimental"; -import { streamText } from "@yourgpt/llm-sdk"; - -export async function POST(req: Request) { - const { messages } = await req.json(); - - const result = await streamText({ - model: openai("gpt-4o"), - system: "Use render_ui for any data, charts, or structured results.", - messages, - tools: { - render_ui: generativeUITool(), - }, - }); - - return result.toDataStreamResponse(); -} +generativeUISystemPrompt({ + designGuidelines: ` + - Use the brand color #6366f1 for primary elements + - Always include a header with the company logo + - Tables should have zebra striping + `, +}) ``` - - +### Using `GenUIFrame` directly -Call `useGenerativeUI()` once in your component tree β€” it registers the renderer automatically. +For advanced use cases, render the iframe component directly: ```tsx -import { useGenerativeUI } from "@yourgpt/copilot-sdk/experimental"; -import { CopilotChat } from "@yourgpt/copilot-sdk/ui"; - -function App() { - useGenerativeUI({ - chartRenderer: MyChartComponent, // required for chart type - }); +import { GenUIFrame } from "@yourgpt/copilot-sdk/experimental"; - return ; -} + sendMessage(msg)} + onAction={(name, data) => handleAction(name, data)} +/> ``` - - +--- -### Built-in component types +## Tool Renderers -| Type | When the AI uses it | Renderer | -|------|-------------------|----------| -| `html` | Dashboards, custom layouts, anything freeform | `HtmlRenderer` β€” sandboxed `