소스 검색

update docs (#82)

Chris Tate 5 달 전
부모
커밋
3d2d1adb2d

+ 9 - 0
.changeset/react-native-events-state.md

@@ -0,0 +1,9 @@
+---
+"@json-render/core": minor
+"@json-render/react": minor
+"@json-render/react-native": minor
+"@json-render/remotion": minor
+"@json-render/codegen": minor
+---
+
+Add @json-render/react-native package, event system (emit replaces onAction), repeat/list rendering, user prompt builder, spec validation, and rename DataProvider to StateProvider.

+ 30 - 6
README.md

@@ -6,6 +6,8 @@ Let end users generate dashboards, widgets, apps, and videos from prompts — sa
 
 ```bash
 npm install @json-render/core @json-render/react
+# or for mobile
+npm install @json-render/core @json-render/react-native
 # or for video
 npm install @json-render/core @json-render/remotion
 ```
@@ -75,8 +77,8 @@ const { registry } = defineRegistry(catalog, {
         <span>{format(props.value, props.format)}</span>
       </div>
     ),
-    Button: ({ props, onAction }) => (
-      <button onClick={() => onAction?.({ name: props.action })}>
+    Button: ({ props, emit }) => (
+      <button onClick={() => emit?.("press")}>
         {props.label}
       </button>
     ),
@@ -129,6 +131,27 @@ const { registry } = defineRegistry(catalog, { components });
 <Renderer spec={spec} registry={registry} />
 ```
 
+### React Native (Mobile)
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react-native/schema";
+import {
+  standardComponentDefinitions,
+  standardActionDefinitions,
+} from "@json-render/react-native/catalog";
+import { defineRegistry, Renderer } from "@json-render/react-native";
+
+// 25+ standard components included
+const catalog = defineCatalog(schema, {
+  components: { ...standardComponentDefinitions },
+  actions: standardActionDefinitions,
+});
+
+const { registry } = defineRegistry(catalog, { components: {} });
+<Renderer spec={spec} registry={registry} />
+```
+
 ### Remotion (Video)
 
 ```tsx
@@ -229,7 +252,7 @@ Components can trigger actions, including the built-in `setState` action:
 }
 ```
 
-The `setState` action updates the data model directly, which re-evaluates visibility conditions and dynamic prop expressions.
+The `setState` action updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions.
 
 ---
 
@@ -242,9 +265,10 @@ pnpm install
 pnpm dev
 ```
 
-- http://localhost:3000 — Docs & Playground
-- http://localhost:3001 — Example Dashboard
-- http://localhost:3002 — Remotion Video Example
+- http://localhost:3000 -- Docs & Playground
+- http://localhost:3001 -- Example Dashboard
+- http://localhost:3002 -- Remotion Video Example
+- React Native example: run `npx expo start` in `examples/react-native`
 
 ## How It Works
 

+ 167 - 0
apps/web/app/(main)/docs/api/react-native/page.mdx

@@ -0,0 +1,167 @@
+export const metadata = { title: "@json-render/react-native API" }
+
+# @json-render/react-native
+
+React Native renderer with standard components, providers, and hooks.
+
+## Standard Components
+
+### Layout
+
+| Component | Props | Description |
+|-----------|-------|-------------|
+| `Container` | `padding`, `background`, `borderRadius`, `borderColor`, `flex` | Basic wrapper with styling |
+| `Row` | `gap`, `align`, `justify`, `flex`, `wrap` | Horizontal flex layout |
+| `Column` | `gap`, `align`, `justify`, `flex` | Vertical flex layout |
+| `ScrollContainer` | `direction` | Scrollable area (vertical or horizontal) |
+| `SafeArea` | `edges` | Safe area insets for notch/home indicator |
+| `Pressable` | `action`, `actionParams` | Touchable wrapper that triggers actions |
+| `Spacer` | `size`, `flex` | Fixed or flexible spacing |
+| `Divider` | `color`, `thickness` | Thin line separator |
+
+### Content
+
+| Component | Props | Description |
+|-----------|-------|-------------|
+| `Heading` | `text`, `level`, `align`, `color` | Heading text (levels 1-6) |
+| `Paragraph` | `text`, `align`, `color` | Body text |
+| `Label` | `text`, `color`, `bold` | Small label text |
+| `Image` | `uri`, `width`, `height`, `resizeMode`, `borderRadius` | Image display |
+| `Avatar` | `uri`, `size`, `fallback` | Circular avatar |
+| `Badge` | `label`, `color`, `textColor` | Status badge |
+| `Chip` | `label`, `selected`, `color` | Tag/chip |
+
+### Input
+
+| Component | Props | Description |
+|-----------|-------|-------------|
+| `Button` | `label`, `variant`, `size`, `disabled`, `action`, `actionParams` | Pressable button |
+| `TextInput` | `placeholder`, `statePath`, `secure`, `keyboardType`, `multiline` | Text input field |
+| `Switch` | `statePath`, `label` | Toggle switch |
+| `Checkbox` | `statePath`, `label` | Checkbox with label |
+| `Slider` | `statePath`, `min`, `max`, `step` | Range slider |
+| `SearchBar` | `placeholder`, `statePath` | Search input |
+
+### Feedback
+
+| Component | Props | Description |
+|-----------|-------|-------------|
+| `Spinner` | `size`, `color` | Loading indicator |
+| `ProgressBar` | `progress`, `color`, `trackColor` | Progress indicator |
+
+### Composite
+
+| Component | Props | Description |
+|-----------|-------|-------------|
+| `Card` | `title`, `subtitle`, `padding` | Card container |
+| `ListItem` | `title`, `subtitle`, `leading`, `trailing`, `action`, `actionParams` | List row |
+| `Modal` | `visible`, `title` | Bottom sheet modal |
+
+## Providers
+
+### StateProvider
+
+```tsx
+<StateProvider initialState={object}>
+  {children}
+</StateProvider>
+```
+
+### ActionProvider
+
+```tsx
+<ActionProvider handlers={Record<string, ActionHandler>}>
+  {children}
+</ActionProvider>
+```
+
+### VisibilityProvider
+
+```tsx
+<VisibilityProvider>
+  {children}
+</VisibilityProvider>
+```
+
+### ValidationProvider
+
+```tsx
+<ValidationProvider>
+  {children}
+</ValidationProvider>
+```
+
+## defineRegistry
+
+Create a type-safe component registry. Standard components are built-in -- only register custom components.
+
+```tsx
+import { defineRegistry, type Components } from '@json-render/react-native';
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Icon: ({ props }) => <Ionicons name={props.name} size={props.size ?? 24} />,
+  } as Components<typeof catalog>,
+});
+```
+
+## Hooks
+
+### useUIStream
+
+```typescript
+const {
+  spec,         // Spec | null - current UI state
+  isStreaming,  // boolean - true while streaming
+  error,        // Error | null
+  send,         // (prompt: string) => Promise<void>
+  clear,        // () => void - reset spec and error
+} = useUIStream({
+  api: string,
+  onComplete?: (spec: Spec) => void,
+  onError?: (error: Error) => void,
+});
+```
+
+### useStateStore
+
+```typescript
+const { state, get, set, update } = useStateStore();
+```
+
+### useStateValue
+
+```typescript
+const value = useStateValue(path: string);
+```
+
+### useStateBinding
+
+```typescript
+const [value, setValue] = useStateBinding(path: string);
+```
+
+### useActions
+
+```typescript
+const { execute } = useActions();
+```
+
+### useIsVisible
+
+```typescript
+const isVisible = useIsVisible(condition?: VisibilityCondition);
+```
+
+## Catalog Exports
+
+```typescript
+import { standardComponentDefinitions, standardActionDefinitions } from "@json-render/react-native/catalog";
+import { schema } from "@json-render/react-native/schema";
+```
+
+| Export | Purpose |
+|--------|---------|
+| `standardComponentDefinitions` | Catalog definitions for all 25+ standard components |
+| `standardActionDefinitions` | Catalog definitions for standard actions (setState, navigate) |
+| `schema` | React Native element tree schema |

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

@@ -49,7 +49,7 @@ type ValidatorFn = (value: unknown, args?: object) => boolean | Promise<boolean>
 
 ## defineRegistry
 
-Create a type-safe component registry from a catalog. Components receive `props`, `children`, `onAction`, and `loading` with catalog-inferred types.
+Create a type-safe component registry from a catalog. Components receive `props`, `children`, `emit`, and `loading` with catalog-inferred types.
 
 ```tsx
 import { defineRegistry } from '@json-render/react';
@@ -57,8 +57,8 @@ import { defineRegistry } from '@json-render/react';
 const { registry } = defineRegistry(catalog, {
   components: {
     Card: ({ props, children }) => <div>{props.title}{children}</div>,
-    Button: ({ props, onAction }) => (
-      <button onClick={() => onAction?.({ name: props.action })}>
+    Button: ({ props, emit }) => (
+      <button onClick={() => emit?.("press")}>
         {props.label}
       </button>
     ),
@@ -90,7 +90,7 @@ type Registry = Record<string, React.ComponentType<ComponentRenderProps>>;
 interface ComponentContext<P> {
   props: P;                    // Typed props from catalog
   children?: React.ReactNode;  // Rendered children (for slot components)
-  onAction?: (action: { name: string; params?: object }) => void;
+  emit?: (event: string) => void;  // Emit a named event
   loading?: boolean;
 }
 ```

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

@@ -4,6 +4,125 @@ export const metadata = { title: "Changelog" }
 
 Notable changes and updates to json-render.
 
+## v0.5.0
+
+February 2026
+
+### New: @json-render/react-native
+
+Full React Native renderer with 25+ standard components, data binding, visibility, actions, and dynamic props. Build AI-generated native mobile UIs with the same catalog-driven approach as web.
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react-native/schema";
+import {
+  standardComponentDefinitions,
+  standardActionDefinitions,
+} from "@json-render/react-native/catalog";
+import { defineRegistry, Renderer } from "@json-render/react-native";
+
+const catalog = defineCatalog(schema, {
+  components: { ...standardComponentDefinitions },
+  actions: standardActionDefinitions,
+});
+
+const { registry } = defineRegistry(catalog, { components: {} });
+
+<Renderer spec={spec} registry={registry} />
+```
+
+Includes standard components for layout (Container, Row, Column, ScrollContainer, SafeArea, Pressable, Spacer, Divider), content (Heading, Paragraph, Label, Image, Avatar, Badge, Chip), input (Button, TextInput, Switch, Checkbox, Slider, SearchBar), feedback (Spinner, ProgressBar), and composite (Card, ListItem, Modal).
+
+### New: Event System
+
+Components now use `emit` to fire named events instead of directly dispatching actions. The element's `on` field maps events to action bindings, decoupling component logic from action handling.
+
+```tsx
+// Component emits a named event
+Button: ({ props, emit }) => (
+  <button onClick={() => emit?.("press")}>{props.label}</button>
+),
+
+// Element spec maps events to actions
+{
+  "type": "Button",
+  "props": { "label": "Submit" },
+  "on": { "press": { "action": "submit", "params": { "formId": "main" } } }
+}
+```
+
+### New: Repeat/List Rendering
+
+Elements can now iterate over state arrays using the `repeat` field. Child elements use `$item` and `$index` tokens in `$path` expressions to reference the current item.
+
+```json
+{
+  "type": "Column",
+  "repeat": { "path": "/posts", "key": "id" },
+  "children": ["post-card"]
+}
+```
+
+```json
+{
+  "type": "Card",
+  "props": { "title": { "$path": "$item/title" } }
+}
+```
+
+### New: User Prompt Builder
+
+Build structured user prompts with optional spec refinement and state context:
+
+```typescript
+import { buildUserPrompt } from "@json-render/core";
+
+// Fresh generation
+buildUserPrompt({ prompt: "create a todo app" });
+
+// Refinement (patch-only mode)
+buildUserPrompt({ prompt: "add a toggle", currentSpec: spec });
+
+// With runtime state
+buildUserPrompt({ prompt: "show data", state: { todos: [] } });
+```
+
+### New: Spec Validation
+
+Validate spec structure and auto-fix common issues:
+
+```typescript
+import { validateSpec, autoFixSpec } from "@json-render/core";
+
+const { valid, issues } = validateSpec(spec, catalog);
+const fixed = autoFixSpec(spec);
+```
+
+### Improved: State Management
+
+`DataProvider` has been renamed to `StateProvider` with a clearer API. State is now a first-class part of specs -- elements can bind to state via `$path` expressions, and the built-in `setState` action updates state directly.
+
+### Improved: AI Prompts
+
+Schema prompts now include streaming best practices, repeat/list examples, and state patching guidance. Schemas can also define `defaultRules` that are always included in generated prompts.
+
+### Improved: Documentation
+
+- All documentation pages migrated to MDX
+- AI-powered documentation chat
+- Dynamic Open Graph images for all docs pages
+- Improved playground
+
+### Breaking Changes
+
+- `DataProvider` renamed to `StateProvider`
+- `useData` renamed to `useStateStore`, `useDataValue` to `useStateValue`, `useDataBinding` to `useStateBinding`
+- `onAction` renamed to `emit` in component context
+- `DataModel` type renamed to `StateModel`
+- `Action` type renamed to `ActionBinding` (old name still available but deprecated)
+
+---
+
 ## v0.4.0
 
 February 2026

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

@@ -8,6 +8,10 @@ Install the core package plus your renderer of choice.
 
 <PackageInstall packages="@json-render/core @json-render/react" />
 
+## For React Native
+
+<PackageInstall packages="@json-render/core @json-render/react-native" />
+
 ## For Remotion Video
 
 <PackageInstall packages="@json-render/core @json-render/remotion remotion @remotion/player" />

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

@@ -53,7 +53,7 @@ export const catalog = defineCatalog(schema, {
 
 ## 2. Define your components
 
-Use `defineRegistry` to map catalog types to React components. Each component receives type-safe `props`, `children`, and `onAction`:
+Use `defineRegistry` to map catalog types to React components. Each component receives type-safe `props`, `children`, and `emit`:
 
 ```tsx
 // lib/registry.tsx
@@ -71,10 +71,10 @@ export const { registry } = defineRegistry(catalog, {
         {children}
       </div>
     ),
-    Button: ({ props, onAction }) => (
+    Button: ({ props, emit }) => (
       <button
         className="px-4 py-2 bg-blue-500 text-white rounded"
-        onClick={() => onAction?.({ name: props.action })}
+        onClick={() => emit?.("press")}
       >
         {props.label}
       </button>

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

@@ -39,6 +39,11 @@ export const docsNavigation: NavSection[] = [
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/dashboard",
         external: true,
       },
+      {
+        title: "React Native",
+        href: "https://github.com/vercel-labs/json-render/tree/main/examples/react-native",
+        external: true,
+      },
       {
         title: "Remotion",
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/remotion",
@@ -69,6 +74,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/react-native", href: "/docs/api/react-native" },
       { title: "@json-render/remotion", href: "/docs/api/remotion" },
       { title: "@json-render/codegen", href: "/docs/api/codegen" },
     ],

+ 1 - 0
apps/web/lib/page-titles.ts

@@ -36,6 +36,7 @@ export const PAGE_TITLES: Record<string, string> = {
   // API references
   "docs/api/core": "@json-render/core API",
   "docs/api/react": "@json-render/react API",
+  "docs/api/react-native": "@json-render/react-native API",
   "docs/api/codegen": "@json-render/codegen API",
   "docs/api/remotion": "@json-render/remotion API",
 };

+ 1 - 1
packages/core/README.md

@@ -213,7 +213,7 @@ Any prop value can be a dynamic expression that resolves based on data state at
 
 ### Data Binding (`$path`)
 
-Read a value directly from the data model:
+Read a value directly from the state model:
 
 ```json
 {

+ 1 - 1
packages/react-native/README.md

@@ -158,7 +158,7 @@ The `Pressable` component wraps children and triggers an action on press. It's e
 
 ## Built-in Actions
 
-The `setState` action is handled automatically by `ActionProvider`. It updates the data model, which triggers re-evaluation of visibility conditions and dynamic prop expressions:
+The `setState` action is handled automatically by `ActionProvider`. It updates the state model, which triggers re-evaluation of visibility conditions and dynamic prop expressions:
 
 ```json
 {

+ 4 - 4
packages/react/README.md

@@ -63,8 +63,8 @@ export const { registry } = defineRegistry(catalog, {
         {children}
       </div>
     ),
-    Button: ({ props, onAction }) => (
-      <button onClick={() => onAction?.({ name: props.action })}>
+    Button: ({ props, emit }) => (
+      <button onClick={() => emit?.("press")}>
         {props.label}
       </button>
     ),
@@ -274,7 +274,7 @@ See [@json-render/core](../core/README.md) for full expression syntax.
 
 ## Built-in Actions
 
-The `setState` action is handled automatically by `ActionProvider`. It updates the data model, which triggers re-evaluation of visibility conditions and dynamic prop expressions:
+The `setState` action is handled automatically by `ActionProvider`. It updates the state model, which triggers re-evaluation of visibility conditions and dynamic prop expressions:
 
 ```json
 {
@@ -295,7 +295,7 @@ When using `defineRegistry`, components receive these props:
 interface ComponentContext<P> {
   props: P;                    // Typed props from the catalog
   children?: React.ReactNode;  // Rendered children
-  onAction?: (action: { name: string; params?: Record<string, unknown> }) => void;
+  emit?: (event: string) => void;  // Emit a named event
   loading?: boolean;           // Whether the parent is loading
 }
 ```

+ 1 - 1
skills/json-render-core/SKILL.md

@@ -81,7 +81,7 @@ const finalSpec = compiler.getResult();
 
 Any prop value can be a dynamic expression resolved at render time:
 
-- **`{ "$path": "/state/key" }`** -- reads a value from the data model
+- **`{ "$path": "/state/key" }`** -- reads a value from the state model
 - **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** -- evaluates a visibility condition and picks a branch
 
 `$cond` uses the same syntax as visibility conditions (`eq`, `neq`, `path`, `and`, `or`, `not`). `$then` and `$else` can themselves be expressions (recursive).

+ 1 - 1
skills/json-render-react-native/SKILL.md

@@ -127,7 +127,7 @@ Components receive already-resolved props -- no changes needed to component impl
 
 ## Built-in Actions
 
-The `setState` action is handled automatically by `ActionProvider` and updates the data model directly, which re-evaluates visibility conditions and dynamic prop expressions:
+The `setState` action is handled automatically by `ActionProvider` and updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions:
 
 ```json
 { "action": "setState", "actionParams": { "path": "/activeTab", "value": "home" } }

+ 24 - 5
skills/json-render-react/SKILL.md

@@ -85,7 +85,7 @@ The React schema uses an element tree format:
 | Provider | Purpose |
 |----------|---------|
 | `StateProvider` | Share state across components (JSON Pointer paths) |
-| `ActionProvider` | Handle actions dispatched from components |
+| `ActionProvider` | Handle actions dispatched via the event system |
 | `VisibilityProvider` | Enable conditional rendering based on state |
 | `ValidationProvider` | Form field validation |
 
@@ -108,9 +108,28 @@ Any prop value can be a data-driven expression resolved by the renderer before c
 
 Components receive already-resolved props -- no changes needed to component implementations.
 
+## Event System
+
+Components use `emit` to fire named events. The element's `on` field maps events to action bindings:
+
+```tsx
+// Component emits a named event
+Button: ({ props, emit }) => (
+  <button onClick={() => emit?.("press")}>{props.label}</button>
+),
+```
+
+```json
+{
+  "type": "Button",
+  "props": { "label": "Submit" },
+  "on": { "press": { "action": "submit" } }
+}
+```
+
 ## Built-in Actions
 
-The `setState` action is handled automatically by `ActionProvider` and updates the data model directly, which re-evaluates visibility conditions and dynamic prop expressions:
+The `setState` action is handled automatically by `ActionProvider` and updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions:
 
 ```json
 { "action": "setState", "actionParams": { "path": "/activeTab", "value": "home" } }
@@ -123,9 +142,9 @@ The `setState` action is handled automatically by `ActionProvider` and updates t
 | `defineRegistry` | Create a type-safe component registry from a catalog |
 | `Renderer` | Render a spec using a registry |
 | `schema` | Element tree schema |
-| `useStateStore` | Access data context |
-| `useStateValue` | Get single value from data |
-| `useStateBinding` | Two-way data binding |
+| `useStateStore` | Access state context |
+| `useStateValue` | Get single value from state |
+| `useStateBinding` | Two-way state binding |
 | `useActions` | Access actions context |
 | `useAction` | Get a single action dispatch function |
 | `useUIStream` | Stream specs from an API endpoint |