Pārlūkot izejas kodu

update website docs (#119)

Chris Tate 4 mēneši atpakaļ
vecāks
revīzija
b7d5a75bfa

+ 35 - 0
apps/web/app/(main)/docs/api/core/page.mdx

@@ -141,6 +141,25 @@ const catalog = defineCatalog(schema, {
 });
 ```
 
+### SchemaOptions
+
+When creating schemas with `defineSchema`, you can pass options:
+
+```typescript
+interface SchemaOptions {
+  promptTemplate?: PromptTemplate;  // Custom AI prompt generator
+  defaultRules?: string[];          // Default rules injected before custom rules in prompts
+  builtInActions?: BuiltInAction[]; // Actions always available at runtime, auto-injected into prompts
+}
+
+interface BuiltInAction {
+  name: string;        // Action name (e.g. "setState")
+  description: string; // Human-readable description for the LLM
+}
+```
+
+Built-in actions are injected into prompts as `[built-in]` and are handled by the runtime (e.g. `ActionProvider`) without requiring handlers in `defineRegistry`. The React schema declares `setState`, `pushState`, and `removeState` as built-in.
+
 ### defineSchema
 
 Create custom schemas for different output formats (e.g., page-based, block-based).
@@ -333,6 +352,21 @@ const flat = nestedToFlat({
 // { root: "el-0", elements: { "el-0": ..., "el-1": ... } }
 ```
 
+### createJsonRenderTransform
+
+Low-level `TransformStream` that separates text from JSONL patches in a mixed AI stream. Lines that parse as JSONL patches are emitted as `data-spec` parts; everything else passes through as text.
+
+The transform properly splits text blocks around spec data by emitting `text-end`/`text-start` pairs, ensuring the AI SDK creates separate text parts and preserving correct interleaving of prose and UI in `message.parts`.
+
+```typescript
+import { createJsonRenderTransform } from '@json-render/core';
+
+const transform = createJsonRenderTransform();
+// Use with ReadableStream.pipeThrough(transform) for custom pipelines
+```
+
+Most users should use `pipeJsonRender()` instead, which wraps this transform for the common AI SDK use case.
+
 ### createMixedStreamParser
 
 Parse a mixed stream of text and JSONL patches (used for Chat + GenUI mode):
@@ -556,6 +590,7 @@ interface ActionBinding {
   };
   onSuccess?: { set: Record<string, unknown> };
   onError?: { set: Record<string, unknown> };
+  preventDefault?: boolean;  // Prevent default browser behavior (e.g. navigation on links)
 }
 ```
 

+ 42 - 2
apps/web/app/(main)/docs/api/react/page.mdx

@@ -46,7 +46,9 @@ type ValidationFunction = (value: unknown, args?: object) => boolean | Promise<b
 
 ## defineRegistry
 
-Create a type-safe component registry from a catalog. Components receive `props`, `children`, `emit`, and `loading` with catalog-inferred types.
+Create a type-safe component registry from a catalog. Components receive `props`, `children`, `emit`, `on`, and `loading` with catalog-inferred types.
+
+When the catalog declares actions, the `actions` field is required. When the catalog has no actions (e.g. `actions: {}`), the field is optional.
 
 ```tsx
 import { defineRegistry } from '@json-render/react';
@@ -87,10 +89,48 @@ type Registry = Record<string, React.ComponentType<ComponentRenderProps>>;
 interface ComponentContext<P> {
   props: P;                                // Typed props from catalog
   children?: React.ReactNode;              // Rendered children (for slot components)
-  emit: (event: string) => void;           // Emit a named event
+  emit: (event: string) => void;           // Emit a named event (always defined)
+  on: (event: string) => EventHandle;      // Get event handle with metadata
   loading?: boolean;
   bindings?: Record<string, string>;       // State paths from $bindState/$bindItem expressions
 }
+
+interface EventHandle {
+  emit: () => void;              // Fire the event
+  shouldPreventDefault: boolean; // Whether any binding requested preventDefault
+  bound: boolean;                // Whether any handler is bound
+}
+```
+
+Use `emit("press")` for simple event firing. Use `on("click")` when you need to check metadata like `shouldPreventDefault`:
+
+```tsx
+Link: ({ props, on }) => {
+  const click = on("click");
+  return (
+    <a
+      href={props.href}
+      onClick={(e) => {
+        if (click.shouldPreventDefault) e.preventDefault();
+        click.emit();
+      }}
+    >
+      {props.label}
+    </a>
+  );
+},
+```
+
+### BaseComponentProps
+
+Catalog-agnostic base type for building reusable component libraries (e.g. `@json-render/shadcn`) that are not tied to a specific catalog:
+
+```typescript
+import type { BaseComponentProps } from "@json-render/react";
+
+const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
+  <div>{props.title}{children}</div>
+);
 ```
 
 ## Hooks

+ 175 - 0
apps/web/app/(main)/docs/api/shadcn/page.mdx

@@ -0,0 +1,175 @@
+export const metadata = { title: "@json-render/shadcn API" }
+
+# @json-render/shadcn
+
+Pre-built [shadcn/ui](https://ui.shadcn.com/) components for json-render. 36 components built on Radix UI + Tailwind CSS, ready to use with `defineCatalog` and `defineRegistry`.
+
+## Installation
+
+```bash
+npm install @json-render/shadcn @json-render/core @json-render/react zod
+```
+
+Your app must have Tailwind CSS configured.
+
+## Entry Points
+
+| Entry Point | Exports | Use For |
+|-------------|---------|---------|
+| `@json-render/shadcn` | `shadcnComponents` | React implementations |
+| `@json-render/shadcn/catalog` | `shadcnComponentDefinitions` | Catalog schemas (no React dependency, safe for server) |
+
+## Usage
+
+Pick the components you need from the standard definitions:
+
+```typescript
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react/schema";
+import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
+import { defineRegistry } from "@json-render/react";
+import { shadcnComponents } from "@json-render/shadcn";
+
+// Catalog: pick definitions
+const catalog = defineCatalog(schema, {
+  components: {
+    Card: shadcnComponentDefinitions.Card,
+    Stack: shadcnComponentDefinitions.Stack,
+    Heading: shadcnComponentDefinitions.Heading,
+    Button: shadcnComponentDefinitions.Button,
+    Input: shadcnComponentDefinitions.Input,
+  },
+  actions: {},
+});
+
+// Registry: pick matching implementations
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: shadcnComponents.Card,
+    Stack: shadcnComponents.Stack,
+    Heading: shadcnComponents.Heading,
+    Button: shadcnComponents.Button,
+    Input: shadcnComponents.Input,
+  },
+});
+```
+
+State actions (`setState`, `pushState`, `removeState`) are built into the React schema and handled by `ActionProvider` automatically. You don't need to declare them in your catalog.
+
+## Extending with Custom Components
+
+Add custom components alongside standard ones:
+
+```typescript
+import { z } from "zod";
+
+const catalog = defineCatalog(schema, {
+  components: {
+    // Standard
+    Card: shadcnComponentDefinitions.Card,
+    Stack: shadcnComponentDefinitions.Stack,
+    Button: shadcnComponentDefinitions.Button,
+
+    // Custom
+    Metric: {
+      props: z.object({
+        label: z.string(),
+        value: z.string(),
+        trend: z.enum(["up", "down", "neutral"]).nullable(),
+      }),
+      description: "KPI metric display",
+    },
+  },
+  actions: {},
+});
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: shadcnComponents.Card,
+    Stack: shadcnComponents.Stack,
+    Button: shadcnComponents.Button,
+    Metric: ({ props }) => (
+      <div>
+        <span>{props.label}</span>
+        <span>{props.value}</span>
+      </div>
+    ),
+  },
+});
+```
+
+## Available Components
+
+### Layout
+
+| Component | Description |
+|-----------|-------------|
+| `Card` | Container card with optional title, description, maxWidth, centered |
+| `Stack` | Flex container with direction, gap, align, justify |
+| `Grid` | Grid layout with columns (1-6) and gap |
+| `Separator` | Visual separator line with orientation |
+
+### Navigation
+
+| Component | Description |
+|-----------|-------------|
+| `Tabs` | Tabbed navigation with tabs array, defaultValue, value |
+| `Accordion` | Collapsible sections with items array and type (single/multiple) |
+| `Collapsible` | Single collapsible section with title and defaultOpen |
+| `Pagination` | Page navigation with totalPages and page |
+
+### Overlay
+
+| Component | Description |
+|-----------|-------------|
+| `Dialog` | Modal dialog with title, description, openPath |
+| `Drawer` | Bottom drawer with title, description, openPath |
+| `Tooltip` | Hover tooltip with content and text |
+| `Popover` | Click-triggered popover with trigger and content |
+| `DropdownMenu` | Dropdown menu with label and items array |
+
+### Content
+
+| Component | Description |
+|-----------|-------------|
+| `Heading` | Heading text with level (h1-h4) |
+| `Text` | Paragraph with variant (body, caption, muted, lead, code) |
+| `Image` | Image with alt, width, height |
+| `Avatar` | User avatar with src, name, size |
+| `Badge` | Status badge with text and variant |
+| `Alert` | Alert banner with title, message, type |
+| `Carousel` | Horizontally scrollable carousel with items |
+| `Table` | Data table with columns and rows |
+
+### Feedback
+
+| Component | Description |
+|-----------|-------------|
+| `Progress` | Progress bar with value, max, label |
+| `Skeleton` | Loading placeholder with width, height, rounded |
+| `Spinner` | Loading spinner with size and label |
+
+### Input
+
+| Component | Description |
+|-----------|-------------|
+| `Button` | Clickable button with label, variant, disabled |
+| `Link` | Anchor link with label and href |
+| `Input` | Text input with label, name, type, placeholder, value, checks |
+| `Textarea` | Multi-line text input with label, name, placeholder, rows, value, checks |
+| `Select` | Dropdown select with label, name, options, value, checks |
+| `Checkbox` | Checkbox with label, name, checked |
+| `Radio` | Radio button group with label, name, options, value |
+| `Switch` | Toggle switch with label, name, checked |
+| `Slider` | Range slider with label, min, max, step, value |
+| `Toggle` | Toggle button with label, pressed, variant |
+| `ToggleGroup` | Group of toggle buttons with items, type, value |
+| `ButtonGroup` | Group of buttons with buttons array and selected |
+
+## Notes
+
+- The `/catalog` entry point has no React dependency -- use it for server-side prompt generation
+- Components use Tailwind CSS classes -- your app must have Tailwind configured
+- Component implementations use bundled shadcn/ui primitives (not your app's `components/ui/`)
+- Form inputs support `checks` for validation (type + message pairs)
+- Events: inputs emit `change`/`submit`/`focus`/`blur`; buttons emit `press`; selects emit `change`/`select`

+ 91 - 0
apps/web/app/(main)/docs/changelog/page.mdx

@@ -4,6 +4,97 @@ export const metadata = { title: "Changelog" }
 
 Notable changes and updates to json-render.
 
+## v0.7.0
+
+February 2026
+
+### New: `@json-render/shadcn`
+
+Pre-built [shadcn/ui](https://ui.shadcn.com/) component library for json-render. 36 components built on Radix UI + Tailwind CSS, ready to use with `defineCatalog` and `defineRegistry`.
+
+```bash
+npm install @json-render/shadcn
+```
+
+```typescript
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react/schema";
+import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
+import { defineRegistry } from "@json-render/react";
+import { shadcnComponents } from "@json-render/shadcn";
+
+const catalog = defineCatalog(schema, {
+  components: {
+    Card: shadcnComponentDefinitions.Card,
+    Button: shadcnComponentDefinitions.Button,
+    Input: shadcnComponentDefinitions.Input,
+  },
+  actions: {},
+});
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: shadcnComponents.Card,
+    Button: shadcnComponents.Button,
+    Input: shadcnComponents.Input,
+  },
+});
+```
+
+Components include: layout (Card, Stack, Grid, Separator), navigation (Tabs, Accordion, Collapsible, Pagination), overlay (Dialog, Drawer, Tooltip, Popover, DropdownMenu), content (Heading, Text, Image, Avatar, Badge, Alert, Carousel, Table), feedback (Progress, Skeleton, Spinner), and input (Button, Link, Input, Textarea, Select, Checkbox, Radio, Switch, Slider, Toggle, ToggleGroup, ButtonGroup).
+
+See the [API reference](/docs/api/shadcn) for full details.
+
+### New: Event Handles (`on()`)
+
+Components now receive an `on(event)` function in addition to `emit(event)`. The `on()` function returns an `EventHandle` with metadata:
+
+- `emit()` -- fire the event
+- `shouldPreventDefault` -- whether any action binding requested `preventDefault`
+- `bound` -- whether any handler is bound to this event
+
+```tsx
+Link: ({ props, on }) => {
+  const click = on("click");
+  return (
+    <a href={props.href} onClick={(e) => {
+      if (click.shouldPreventDefault) e.preventDefault();
+      click.emit();
+    }}>{props.label}</a>
+  );
+},
+```
+
+### New: `BaseComponentProps`
+
+Catalog-agnostic base type for component render functions. Use when building reusable component libraries (like `@json-render/shadcn`) that are not tied to a specific catalog.
+
+```typescript
+import type { BaseComponentProps } from "@json-render/react";
+
+const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
+  <div>{props.title}{children}</div>
+);
+```
+
+### New: Built-in Actions in Schema
+
+Schemas can now declare `builtInActions` -- actions that are always available at runtime and automatically injected into prompts. The React schema declares `setState`, `pushState`, and `removeState` as built-in, so they appear in prompts without needing to be listed in catalog `actions`.
+
+### New: `preventDefault` on `ActionBinding`
+
+Action bindings now support a `preventDefault` boolean field, allowing the LLM to request that default browser behavior (e.g. navigation on links) be prevented.
+
+### Improved: Stream Transform Text Block Splitting
+
+`createJsonRenderTransform()` now properly splits text blocks around spec data by emitting `text-end`/`text-start` pairs. This ensures the AI SDK creates separate text parts, preserving correct interleaving of prose and UI in `message.parts`.
+
+### Improved: `defineRegistry` Actions Requirement
+
+`defineRegistry` now conditionally requires the `actions` field only when the catalog declares actions. Catalogs with no actions (e.g. `actions: {}`) no longer need to pass an empty actions object.
+
+---
+
 ## v0.6.0
 
 February 2026

+ 8 - 0
apps/web/app/(main)/docs/installation/page.mdx

@@ -8,6 +8,14 @@ Install the core package plus your renderer of choice.
 
 <PackageInstall packages="@json-render/core @json-render/react" />
 
+## For React UI with shadcn/ui
+
+Pre-built components for fast prototyping and production use:
+
+<PackageInstall packages="@json-render/core @json-render/react @json-render/shadcn" />
+
+Requires Tailwind CSS in your project. See the [@json-render/shadcn API reference](/docs/api/shadcn) for usage.
+
 ## For React Native
 
 <PackageInstall packages="@json-render/core @json-render/react-native" />

+ 42 - 0
apps/web/app/(main)/docs/quick-start/page.mdx

@@ -163,9 +163,51 @@ export default function Page() {
 }
 ```
 
+## Quick Start with shadcn/ui
+
+If you want to skip defining components from scratch, use `@json-render/shadcn` for 36 pre-built components:
+
+```typescript
+// lib/catalog.ts
+import { defineCatalog } from '@json-render/core';
+import { schema } from '@json-render/react/schema';
+import { shadcnComponentDefinitions } from '@json-render/shadcn/catalog';
+
+export const catalog = defineCatalog(schema, {
+  components: {
+    Card: shadcnComponentDefinitions.Card,
+    Stack: shadcnComponentDefinitions.Stack,
+    Heading: shadcnComponentDefinitions.Heading,
+    Button: shadcnComponentDefinitions.Button,
+    Input: shadcnComponentDefinitions.Input,
+  },
+  actions: {},
+});
+```
+
+```tsx
+// lib/registry.tsx
+import { defineRegistry } from '@json-render/react';
+import { shadcnComponents } from '@json-render/shadcn';
+import { catalog } from './catalog';
+
+export const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: shadcnComponents.Card,
+    Stack: shadcnComponents.Stack,
+    Heading: shadcnComponents.Heading,
+    Button: shadcnComponents.Button,
+    Input: shadcnComponents.Input,
+  },
+});
+```
+
+See the [@json-render/shadcn API reference](/docs/api/shadcn) for the full component list.
+
 ## Next steps
 
 - Learn about [catalogs](/docs/catalog) in depth
 - Explore [data binding](/docs/data-binding) for dynamic values
 - Add [action handlers](/docs/registry#action-handlers) for interactivity
 - Implement [conditional visibility](/docs/visibility)
+- Use [pre-built shadcn/ui components](/docs/api/shadcn) for fast prototyping

+ 27 - 1
apps/web/app/(main)/docs/registry/page.mdx

@@ -69,14 +69,40 @@ Each component receives a `ComponentContext` object:
 interface ComponentContext {
   props: T;                                // Type-safe props from your catalog
   children?: React.ReactNode;              // Rendered children (for slot components)
-  emit: (event: string) => void;           // Emit a named event (e.g. "press")
+  emit: (event: string) => void;           // Emit a named event (always defined)
+  on: (event: string) => EventHandle;      // Get event handle with metadata
   loading?: boolean;                       // Whether the renderer is in a loading state
   bindings?: Record<string, string>;       // State paths from $bindState/$bindItem expressions
 }
+
+interface EventHandle {
+  emit: () => void;              // Fire the event
+  shouldPreventDefault: boolean; // Whether any binding requested preventDefault
+  bound: boolean;                // Whether any handler is bound
+}
 ```
 
 Props are automatically inferred from your catalog, so `props.title` is typed as `string` if your catalog defines it that way.
 
+Use `emit("press")` for simple event firing. Use `on("click")` when you need to inspect event metadata:
+
+```tsx
+Link: ({ props, on }) => {
+  const click = on("click");
+  return (
+    <a
+      href={props.href}
+      onClick={(e) => {
+        if (click.shouldPreventDefault) e.preventDefault();
+        click.emit();
+      }}
+    >
+      {props.label}
+    </a>
+  );
+},
+```
+
 #### Using `bindings` for two-way binding
 
 When a spec uses `{ "$bindState": "/path" }` or `{ "$bindItem": "field" }` on a prop, the renderer resolves the **value** into `props` and provides the **write-back path** in `bindings`. Use the `useBoundProp` hook to wire both together:

+ 1 - 0
apps/web/lib/docs-navigation.ts

@@ -86,6 +86,7 @@ export const docsNavigation: NavSection[] = [
     items: [
       { title: "@json-render/core", href: "/docs/api/core" },
       { title: "@json-render/react", href: "/docs/api/react" },
+      { title: "@json-render/shadcn", href: "/docs/api/shadcn" },
       { title: "@json-render/react-native", href: "/docs/api/react-native" },
       { title: "@json-render/remotion", href: "/docs/api/remotion" },
       { title: "@json-render/codegen", href: "/docs/api/codegen" },