Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"@json-render/mcp",
"@json-render/svelte",
"@json-render/solid",
"@json-render/react-three-fiber"
"@json-render/react-three-fiber",
"@json-render/yaml"
]
],
"linked": [],
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ function Dashboard({ spec }) {
| `@json-render/jotai` | Jotai adapter for `StateStore` |
| `@json-render/xstate` | XState Store (atom) adapter for `StateStore` |
| `@json-render/mcp` | MCP Apps integration for Claude, ChatGPT, Cursor, VS Code |
| `@json-render/yaml` | YAML wire format with streaming parser, edit modes, AI SDK transform |

## Renderers

Expand Down
96 changes: 93 additions & 3 deletions apps/web/app/(main)/docs/api/core/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ interface PromptOptions {
system?: string; // Custom system message intro
customRules?: string[]; // Additional rules to append
mode?: "standalone" | "inline" | "generate" | "chat"; // Output mode (default: "standalone")
editModes?: EditMode[]; // Edit modes to document in prompt (default: ["patch"])
}

interface SpecValidationResult<T> {
Expand Down Expand Up @@ -480,9 +481,10 @@ function buildUserPrompt(options: UserPromptOptions): string

interface UserPromptOptions {
prompt: string; // The user's text prompt
currentSpec?: Spec | null; // Existing spec to refine (triggers patch-only mode)
currentSpec?: Spec | null; // Existing spec to refine (triggers edit mode)
state?: Record<string, unknown> | null; // Runtime state context to include
maxPromptLength?: number; // Max length for user text (truncates before wrapping)
editModes?: EditMode[]; // Edit modes for refinement (default: ["patch"])
}
```

Expand All @@ -492,14 +494,15 @@ interface UserPromptOptions {
const userPrompt = buildUserPrompt({ prompt: "create a todo app" });
```

### Refinement (patch-only mode)
### Refinement (edit modes)

When `currentSpec` is provided, the prompt instructs the AI to output only the patches needed for the change, not recreate the entire spec:
When `currentSpec` is provided, the prompt instructs the AI to use the specified edit modes instead of recreating the entire spec. Available modes: `"patch"` (RFC 6902), `"merge"` (RFC 7396), and `"diff"` (unified diff).

```typescript
const userPrompt = buildUserPrompt({
prompt: "add a dark mode toggle",
currentSpec: existingSpec,
editModes: ["patch", "merge"],
});
```

Expand All @@ -514,6 +517,93 @@ const userPrompt = buildUserPrompt({
});
```

## Edit Modes

Universal edit mode utilities for modifying existing specs. Used by `buildUserPrompt` internally and available for direct use.

```typescript
import {
buildEditInstructions,
buildEditUserPrompt,
isNonEmptySpec,
type EditMode,
type EditConfig,
} from '@json-render/core';

type EditMode = "patch" | "merge" | "diff";
```

### buildEditInstructions

Generate the prompt section describing available edit modes. Supports both JSON and YAML formats.

```typescript
function buildEditInstructions(config: EditConfig, format: "json" | "yaml"): string

const instructions = buildEditInstructions({ modes: ["patch", "merge"] }, "json");
```

### buildEditUserPrompt

Build a user prompt for editing an existing spec. Includes the current spec (with line numbers when diff mode is enabled) and mode-specific instructions.

```typescript
function buildEditUserPrompt(options: BuildEditUserPromptOptions): string

interface BuildEditUserPromptOptions {
prompt: string;
currentSpec?: Spec | null;
config?: EditConfig;
format: "json" | "yaml";
maxPromptLength?: number;
serializer?: (spec: Spec) => string;
}
```

### isNonEmptySpec

Check whether a value is a non-empty spec (has a root string and at least one element).

```typescript
function isNonEmptySpec(spec: unknown): spec is Spec
```

## Deep Merge and Diff

Format-agnostic utilities for merging and diffing spec objects.

### deepMergeSpec

Deep-merge with RFC 7396 semantics: `null` deletes, arrays replace, objects recurse. Neither input is mutated.

```typescript
import { deepMergeSpec } from '@json-render/core';

function deepMergeSpec(
base: Record<string, unknown>,
patch: Record<string, unknown>
): Record<string, unknown>

const merged = deepMergeSpec(currentSpec, { elements: { main: { props: { title: "New" } } } });
```

### diffToPatches

Generate RFC 6902 JSON Patch operations that transform one object into another. Arrays are compared shallowly and replaced atomically; plain objects recurse.

```typescript
import { diffToPatches } from '@json-render/core';

function diffToPatches(
oldObj: Record<string, unknown>,
newObj: Record<string, unknown>,
basePath?: string
): JsonPatch[]

const patches = diffToPatches(oldSpec, newSpec);
// [{ op: "replace", path: "/elements/main/props/title", value: "New Title" }]
```

## evaluateVisibility

Evaluates a visibility condition against the state model.
Expand Down
232 changes: 232 additions & 0 deletions apps/web/app/(main)/docs/api/yaml/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { pageMetadata } from "@/lib/page-metadata"
export const metadata = pageMetadata("docs/api/yaml")

# @json-render/yaml

YAML wire format for json-render. Progressive rendering and surgical edits via streaming YAML.

## Prompt Generation

### yamlPrompt

Generate a YAML-format system prompt from any json-render catalog. Works with catalogs from any renderer.

```typescript
function yamlPrompt(
catalog: Catalog,
options?: YamlPromptOptions
): string
```

```typescript
import { yamlPrompt } from "@json-render/yaml";

const systemPrompt = yamlPrompt(catalog, {
mode: "standalone",
customRules: ["Always use dark theme"],
editModes: ["merge"],
});
```

### YamlPromptOptions

<table>
<thead>
<tr>
<th>Option</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>system</code></td>
<td><code>string</code></td>
<td><code>{'\"You are a UI generator that outputs YAML.\"'}</code></td>
<td>Custom system message intro</td>
</tr>
<tr>
<td><code>mode</code></td>
<td><code>{'\"standalone\" | \"inline\"'}</code></td>
<td><code>{'\"standalone\"'}</code></td>
<td>Standalone outputs only YAML; inline allows conversational responses with embedded YAML fences</td>
</tr>
<tr>
<td><code>customRules</code></td>
<td><code>{'string[]'}</code></td>
<td><code>{'[]'}</code></td>
<td>Additional rules appended to the prompt</td>
</tr>
<tr>
<td><code>editModes</code></td>
<td><code>{'EditMode[]'}</code></td>
<td><code>{'[\"merge\"]'}</code></td>
<td>Edit modes to document in the prompt (patch, merge, diff)</td>
</tr>
</tbody>
</table>

## AI SDK Transform

### createYamlTransform

Creates a `TransformStream` that intercepts AI SDK stream chunks and converts YAML spec/edit blocks into json-render patch data parts.

```typescript
function createYamlTransform(
options?: YamlTransformOptions
): TransformStream<StreamChunk, StreamChunk>
```

Recognized fence types:

- <code>{'```yaml-spec'}</code> -- Full YAML spec, parsed progressively
- <code>{'```yaml-edit'}</code> -- Partial YAML, deep-merged with current spec
- <code>{'```yaml-patch'}</code> -- RFC 6902 JSON Patch lines
- <code>{'```diff'}</code> -- Unified diff against serialized spec

### pipeYamlRender

Convenience wrapper that pipes an AI SDK stream through the YAML transform. Drop-in replacement for `pipeJsonRender` from `@json-render/core`.

```typescript
function pipeYamlRender<T>(
stream: ReadableStream<T>,
options?: YamlTransformOptions
): ReadableStream<T>
```

```typescript
import { pipeYamlRender } from "@json-render/yaml";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";

const stream = createUIMessageStream({
execute: async ({ writer }) => {
writer.merge(pipeYamlRender(result.toUIMessageStream()));
},
});
return createUIMessageStreamResponse({ stream });
```

## Streaming Parser

### createYamlStreamCompiler

Create a streaming YAML compiler that incrementally parses YAML text and emits JSON Patch operations by diffing each successful parse against the previous snapshot.

```typescript
function createYamlStreamCompiler<T>(
initial?: Partial<T>
): YamlStreamCompiler<T>
```

```typescript
import { createYamlStreamCompiler } from "@json-render/yaml";

const compiler = createYamlStreamCompiler<Spec>();

compiler.push("root: main\n");
compiler.push("elements:\n main:\n type: Card\n");

const { result, newPatches } = compiler.flush();
```

### YamlStreamCompiler

<table>
<thead>
<tr>
<th>Method</th>
<th>Returns</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>push(chunk)</code></td>
<td><code>{'{ result: T; newPatches: JsonPatch[] }'}</code></td>
<td>Push a chunk of text, returns current result and new patches</td>
</tr>
<tr>
<td><code>flush()</code></td>
<td><code>{'{ result: T; newPatches: JsonPatch[] }'}</code></td>
<td>Flush remaining buffer, return final result</td>
</tr>
<tr>
<td><code>getResult()</code></td>
<td><code>T</code></td>
<td>Get the current compiled result</td>
</tr>
<tr>
<td><code>getPatches()</code></td>
<td><code>{'JsonPatch[]'}</code></td>
<td>Get all patches applied so far</td>
</tr>
<tr>
<td><code>reset(initial?)</code></td>
<td><code>void</code></td>
<td>Reset to initial state</td>
</tr>
</tbody>
</table>

## Fence Constants

Exported string constants for fence detection in custom parsers:

<table>
<thead>
<tr>
<th>Constant</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>YAML_SPEC_FENCE</code></td>
<td><code>{'\"```yaml-spec\"'}</code></td>
</tr>
<tr>
<td><code>YAML_EDIT_FENCE</code></td>
<td><code>{'\"```yaml-edit\"'}</code></td>
</tr>
<tr>
<td><code>YAML_PATCH_FENCE</code></td>
<td><code>{'\"```yaml-patch\"'}</code></td>
</tr>
<tr>
<td><code>DIFF_FENCE</code></td>
<td><code>{'\"```diff\"'}</code></td>
</tr>
<tr>
<td><code>FENCE_CLOSE</code></td>
<td><code>{'\"```\"'}</code></td>
</tr>
</tbody>
</table>

## Re-exports from @json-render/core

### diffToPatches

Generate RFC 6902 JSON Patch operations that transform one object into another.

```typescript
function diffToPatches(
oldObj: Record<string, unknown>,
newObj: Record<string, unknown>,
basePath?: string
): JsonPatch[]
```

### deepMergeSpec

Deep-merge with RFC 7396 semantics: `null` deletes, arrays replace, objects recurse.

```typescript
function deepMergeSpec(
base: Record<string, unknown>,
patch: Record<string, unknown>
): Record<string, unknown>
```
4 changes: 2 additions & 2 deletions apps/web/app/api/docs-chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const SYSTEM_PROMPT = `You are a helpful documentation assistant for json-render

GitHub repository: https://github.com/vercel-labs/json-render
Documentation: https://json-render.dev/docs
npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-three-fiber, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate
Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, react-three-fiber, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate. See /docs/skills for details.
npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-three-fiber, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate, @json-render/yaml
Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, react-three-fiber, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate, yaml. See /docs/skills for details.

You have access to the full json-render documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/docs/ directory.

Expand Down
Loading
Loading