mirror of
https://github.com/vercel-labs/json-render.git
synced 2026-10-03 04:18:15 +08:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7103ce519 | ||
|
|
43ad534482 | ||
|
|
ea97aff3e0 | ||
|
|
9af3f999e0 | ||
|
|
06b8745da7 | ||
|
|
ddae61805e |
@@ -4,6 +4,146 @@ export const metadata = { title: "Changelog" }
|
||||
|
||||
Notable changes and updates to json-render.
|
||||
|
||||
## v0.6.0
|
||||
|
||||
February 2026
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
json-render now supports two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets the AI respond conversationally with embedded UI specs, ideal for chatbots and copilot experiences.
|
||||
|
||||
```typescript
|
||||
// Generate mode (default) — AI outputs only JSONL
|
||||
const prompt = catalog.prompt();
|
||||
|
||||
// Chat mode — AI outputs text + JSONL inline
|
||||
const chatPrompt = catalog.prompt({ mode: "chat" });
|
||||
```
|
||||
|
||||
On the server, `pipeJsonRender()` separates text from JSONL patches in a mixed stream:
|
||||
|
||||
```typescript
|
||||
import { pipeJsonRender } from "@json-render/core";
|
||||
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
|
||||
|
||||
const stream = createUIMessageStream({
|
||||
execute: async ({ writer }) => {
|
||||
writer.merge(pipeJsonRender(result.toUIMessageStream()));
|
||||
},
|
||||
});
|
||||
return createUIMessageStreamResponse({ stream });
|
||||
```
|
||||
|
||||
On the client, `useJsonRenderMessage` extracts the spec and text from message parts:
|
||||
|
||||
```tsx
|
||||
import { useJsonRenderMessage } from "@json-render/react";
|
||||
|
||||
function ChatMessage({ message }) {
|
||||
const { spec, text, hasSpec } = useJsonRenderMessage(message.parts);
|
||||
return (
|
||||
<div>
|
||||
{text && <Markdown>{text}</Markdown>}
|
||||
{hasSpec && <Renderer spec={spec} registry={registry} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state without custom `valuePath` props.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "Input",
|
||||
"props": { "label": "Email", "value": { "$bindState": "/form/email" } }
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { useBoundProp } from "@json-render/react";
|
||||
|
||||
Input: ({ props, bindings }) => {
|
||||
const [value, setValue] = useBoundProp<string>(props.value, bindings?.value);
|
||||
return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
|
||||
}
|
||||
```
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
All dynamic expressions now use structured `$state`, `$item`, and `$index` objects instead of string token rewriting. This is simpler, more explicit, and works for both props and visibility conditions.
|
||||
|
||||
**Props:**
|
||||
|
||||
```json
|
||||
{ "title": { "$state": "/user/name" } }
|
||||
{ "label": { "$item": "title" } }
|
||||
{ "position": { "$index": true } }
|
||||
```
|
||||
|
||||
**Visibility:**
|
||||
|
||||
```json
|
||||
{ "$state": "/isAdmin" }
|
||||
{ "$state": "/role", "eq": "admin" }
|
||||
[{ "$state": "/isAdmin" }, { "$state": "/feature" }]
|
||||
{ "$or": [{ "$state": "/roleA" }, { "$state": "/roleB" }] }
|
||||
{ "$item": "isActive" }
|
||||
{ "$index": true, "gt": 0 }
|
||||
```
|
||||
|
||||
Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`.
|
||||
|
||||
### New: React Chat Hooks
|
||||
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem`
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming inline UI generation.
|
||||
|
||||
### Improved: Renderer Performance
|
||||
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance with repeat lists
|
||||
- `emit` is always defined (never `undefined`)
|
||||
- Repeat scope passes the actual item object, eliminating string token rewriting
|
||||
|
||||
### Improved: Utilities
|
||||
|
||||
- `applySpecPatch()` — typed wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- `{ $path }` and `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }` in props
|
||||
- Visibility: `{ path }` -> `{ $state }`, `{ and/or/not }` -> `{ $and/$or }` with `not` as operator flag
|
||||
- `DynamicValue`: `{ path: string }` -> `{ $state: string }`
|
||||
- `repeat.path` -> `repeat.statePath`
|
||||
- Action params: `path` -> `statePath` in setState action
|
||||
- `actionHandlers` -> `handlers` on `JSONUIProvider` / `ActionProvider`
|
||||
- `AuthState` and `{ auth }` visibility conditions removed (model auth as regular state)
|
||||
- Legacy catalog API removed: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`
|
||||
- React exports removed: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- Codegen: `traverseTree` -> `traverseSpec`
|
||||
|
||||
See the [Migration Guide](/docs/migration) for detailed upgrade instructions.
|
||||
|
||||
---
|
||||
|
||||
## v0.5.0
|
||||
|
||||
February 2026
|
||||
|
||||
@@ -2,7 +2,7 @@ export const metadata = { title: "Migration Guide" }
|
||||
|
||||
# Migration Guide
|
||||
|
||||
This guide covers breaking changes introduced in v0.5.0 and how to update your code.
|
||||
This guide covers breaking changes introduced in v0.6.0 and how to update your code.
|
||||
|
||||
## State Provider
|
||||
|
||||
|
||||
@@ -781,9 +781,10 @@ export const { registry, executeAction } = defineRegistry(playgroundCatalog, {
|
||||
const setValue = isBound ? setBoundValue : setLocalValue;
|
||||
|
||||
const hasValidation = !!(bindings?.value && props.checks?.length);
|
||||
const { errors, validate } = useFieldValidation(bindings?.value ?? "", {
|
||||
checks: props.checks ?? [],
|
||||
});
|
||||
const { errors, validate } = useFieldValidation(
|
||||
bindings?.value ?? "",
|
||||
hasValidation ? { checks: props.checks ?? [] } : undefined,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
@@ -822,9 +823,10 @@ export const { registry, executeAction } = defineRegistry(playgroundCatalog, {
|
||||
const setValue = isBound ? setBoundValue : setLocalValue;
|
||||
|
||||
const hasValidation = !!(bindings?.value && props.checks?.length);
|
||||
const { errors, validate } = useFieldValidation(bindings?.value ?? "", {
|
||||
checks: props.checks ?? [],
|
||||
});
|
||||
const { errors, validate } = useFieldValidation(
|
||||
bindings?.value ?? "",
|
||||
hasValidation ? { checks: props.checks ?? [] } : undefined,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
@@ -864,9 +866,10 @@ export const { registry, executeAction } = defineRegistry(playgroundCatalog, {
|
||||
);
|
||||
|
||||
const hasValidation = !!(bindings?.value && props.checks?.length);
|
||||
const { errors, validate } = useFieldValidation(bindings?.value ?? "", {
|
||||
checks: props.checks ?? [],
|
||||
});
|
||||
const { errors, validate } = useFieldValidation(
|
||||
bindings?.value ?? "",
|
||||
hasValidation ? { checks: props.checks ?? [] } : undefined,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -1,5 +1,88 @@
|
||||
# @json-render/codegen
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ea97aff]
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 06b8745: Chat mode (inline GenUI), AI SDK integration, two-way binding, and expression-based visibility/props.
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
Two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets AI respond conversationally with embedded UI specs — ideal for chatbots and copilot experiences.
|
||||
- `catalog.prompt({ mode: "chat" })` generates a chat-aware system prompt
|
||||
- `pipeJsonRender()` server-side transform separates text from JSONL patches in a mixed stream
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: React Chat Hooks
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem` expressions
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state.
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
Replaced string token rewriting with structured expression objects:
|
||||
- Props: `{ $state: "/path" }`, `{ $item: "field" }`, `{ $index: true }`
|
||||
- Visibility: `{ $state: "/path", eq: "value" }`, `{ $item: "active" }`, `{ $index: true, gt: 0 }`
|
||||
- Logic: `{ $and: [...] }`, `{ $or: [...] }`, and implicit AND via arrays
|
||||
- Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`
|
||||
|
||||
### New: Utilities
|
||||
- `applySpecPatch()` — typed convenience wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat `{ root, elements }` format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming UI generation.
|
||||
|
||||
### Improved: Renderer
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance in repeat lists
|
||||
- `emit` is always defined (never `undefined`) — no more optional chaining needed
|
||||
- Action params are resolved through `resolveActionParam` supporting `$item`, `$index`, `$state`
|
||||
- Repeat scope now passes the actual item object instead of requiring token rewriting
|
||||
|
||||
### Breaking Changes
|
||||
- **Expressions renamed**: `{ $path }` / `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }`
|
||||
- **Visibility conditions**: `{ path }` → `{ $state }`, `{ and/or/not }` → `{ $and/$or }` with `not` as operator flag
|
||||
- **DynamicValue**: `{ path: string }` → `{ $state: string }`
|
||||
- **Repeat field**: `repeat.path` → `repeat.statePath`
|
||||
- **Action params**: `path` → `statePath` in setState action params
|
||||
- **Provider props**: `actionHandlers` → `handlers` on `JSONUIProvider`/`ActionProvider`
|
||||
- **Auth removed**: `AuthState` type and `{ auth }` visibility conditions removed — model auth as regular state
|
||||
- **Legacy catalog removed**: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`, `ComponentDefinition`, `CatalogConfig`, `SystemPromptOptions` removed
|
||||
- **React exports removed**: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- **Codegen**: `traverseTree` → `traverseSpec`, `SpecVisitor` → `TreeVisitor`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [06b8745]
|
||||
- @json-render/core@0.6.0
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/codegen",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Utilities for generating code from json-render UI trees",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,78 @@
|
||||
# @json-render/core
|
||||
|
||||
## 0.6.1
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ea97aff: Fix infinite re-render loop caused by multiple unbound form inputs (Input, Textarea, Select) all registering field validation at the same empty path with different `checks` configs, causing them to overwrite each other endlessly. Stabilize context values in ActionProvider, ValidationProvider, and useUIStream by using refs for state/callbacks, preventing unnecessary re-render cascades on every state update.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 06b8745: Chat mode (inline GenUI), AI SDK integration, two-way binding, and expression-based visibility/props.
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
Two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets AI respond conversationally with embedded UI specs — ideal for chatbots and copilot experiences.
|
||||
- `catalog.prompt({ mode: "chat" })` generates a chat-aware system prompt
|
||||
- `pipeJsonRender()` server-side transform separates text from JSONL patches in a mixed stream
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: React Chat Hooks
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem` expressions
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state.
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
Replaced string token rewriting with structured expression objects:
|
||||
- Props: `{ $state: "/path" }`, `{ $item: "field" }`, `{ $index: true }`
|
||||
- Visibility: `{ $state: "/path", eq: "value" }`, `{ $item: "active" }`, `{ $index: true, gt: 0 }`
|
||||
- Logic: `{ $and: [...] }`, `{ $or: [...] }`, and implicit AND via arrays
|
||||
- Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`
|
||||
|
||||
### New: Utilities
|
||||
- `applySpecPatch()` — typed convenience wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat `{ root, elements }` format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming UI generation.
|
||||
|
||||
### Improved: Renderer
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance in repeat lists
|
||||
- `emit` is always defined (never `undefined`) — no more optional chaining needed
|
||||
- Action params are resolved through `resolveActionParam` supporting `$item`, `$index`, `$state`
|
||||
- Repeat scope now passes the actual item object instead of requiring token rewriting
|
||||
|
||||
### Breaking Changes
|
||||
- **Expressions renamed**: `{ $path }` / `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }`
|
||||
- **Visibility conditions**: `{ path }` → `{ $state }`, `{ and/or/not }` → `{ $and/$or }` with `not` as operator flag
|
||||
- **DynamicValue**: `{ path: string }` → `{ $state: string }`
|
||||
- **Repeat field**: `repeat.path` → `repeat.statePath`
|
||||
- **Action params**: `path` → `statePath` in setState action params
|
||||
- **Provider props**: `actionHandlers` → `handlers` on `JSONUIProvider`/`ActionProvider`
|
||||
- **Auth removed**: `AuthState` type and `{ auth }` visibility conditions removed — model auth as regular state
|
||||
- **Legacy catalog removed**: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`, `ComponentDefinition`, `CatalogConfig`, `SystemPromptOptions` removed
|
||||
- **React exports removed**: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- **Codegen**: `traverseTree` → `traverseSpec`, `SpecVisitor` → `TreeVisitor`
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/core",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,88 @@
|
||||
# @json-render/react-native
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ea97aff]
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 06b8745: Chat mode (inline GenUI), AI SDK integration, two-way binding, and expression-based visibility/props.
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
Two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets AI respond conversationally with embedded UI specs — ideal for chatbots and copilot experiences.
|
||||
- `catalog.prompt({ mode: "chat" })` generates a chat-aware system prompt
|
||||
- `pipeJsonRender()` server-side transform separates text from JSONL patches in a mixed stream
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: React Chat Hooks
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem` expressions
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state.
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
Replaced string token rewriting with structured expression objects:
|
||||
- Props: `{ $state: "/path" }`, `{ $item: "field" }`, `{ $index: true }`
|
||||
- Visibility: `{ $state: "/path", eq: "value" }`, `{ $item: "active" }`, `{ $index: true, gt: 0 }`
|
||||
- Logic: `{ $and: [...] }`, `{ $or: [...] }`, and implicit AND via arrays
|
||||
- Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`
|
||||
|
||||
### New: Utilities
|
||||
- `applySpecPatch()` — typed convenience wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat `{ root, elements }` format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming UI generation.
|
||||
|
||||
### Improved: Renderer
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance in repeat lists
|
||||
- `emit` is always defined (never `undefined`) — no more optional chaining needed
|
||||
- Action params are resolved through `resolveActionParam` supporting `$item`, `$index`, `$state`
|
||||
- Repeat scope now passes the actual item object instead of requiring token rewriting
|
||||
|
||||
### Breaking Changes
|
||||
- **Expressions renamed**: `{ $path }` / `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }`
|
||||
- **Visibility conditions**: `{ path }` → `{ $state }`, `{ and/or/not }` → `{ $and/$or }` with `not` as operator flag
|
||||
- **DynamicValue**: `{ path: string }` → `{ $state: string }`
|
||||
- **Repeat field**: `repeat.path` → `repeat.statePath`
|
||||
- **Action params**: `path` → `statePath` in setState action params
|
||||
- **Provider props**: `actionHandlers` → `handlers` on `JSONUIProvider`/`ActionProvider`
|
||||
- **Auth removed**: `AuthState` type and `{ auth }` visibility conditions removed — model auth as regular state
|
||||
- **Legacy catalog removed**: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`, `ComponentDefinition`, `CatalogConfig`, `SystemPromptOptions` removed
|
||||
- **React exports removed**: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- **Codegen**: `traverseTree` → `traverseSpec`, `SpecVisitor` → `TreeVisitor`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [06b8745]
|
||||
- @json-render/core@0.6.0
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react-native",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,90 @@
|
||||
# @json-render/react
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 43ad534: Fix infinite re-render loop caused by multiple unbound form inputs (Input, Textarea, Select) all registering field validation at the same empty path with different `checks` configs, causing them to overwrite each other endlessly. Stabilize context values in ActionProvider, ValidationProvider, and useUIStream by using refs for state/callbacks, preventing unnecessary re-render cascades on every state update.
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ea97aff: Fix infinite re-render loop caused by multiple unbound form inputs (Input, Textarea, Select) all registering field validation at the same empty path with different `checks` configs, causing them to overwrite each other endlessly. Stabilize context values in ActionProvider, ValidationProvider, and useUIStream by using refs for state/callbacks, preventing unnecessary re-render cascades on every state update.
|
||||
- Updated dependencies [ea97aff]
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 06b8745: Chat mode (inline GenUI), AI SDK integration, two-way binding, and expression-based visibility/props.
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
Two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets AI respond conversationally with embedded UI specs — ideal for chatbots and copilot experiences.
|
||||
- `catalog.prompt({ mode: "chat" })` generates a chat-aware system prompt
|
||||
- `pipeJsonRender()` server-side transform separates text from JSONL patches in a mixed stream
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: React Chat Hooks
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem` expressions
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state.
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
Replaced string token rewriting with structured expression objects:
|
||||
- Props: `{ $state: "/path" }`, `{ $item: "field" }`, `{ $index: true }`
|
||||
- Visibility: `{ $state: "/path", eq: "value" }`, `{ $item: "active" }`, `{ $index: true, gt: 0 }`
|
||||
- Logic: `{ $and: [...] }`, `{ $or: [...] }`, and implicit AND via arrays
|
||||
- Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`
|
||||
|
||||
### New: Utilities
|
||||
- `applySpecPatch()` — typed convenience wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat `{ root, elements }` format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming UI generation.
|
||||
|
||||
### Improved: Renderer
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance in repeat lists
|
||||
- `emit` is always defined (never `undefined`) — no more optional chaining needed
|
||||
- Action params are resolved through `resolveActionParam` supporting `$item`, `$index`, `$state`
|
||||
- Repeat scope now passes the actual item object instead of requiring token rewriting
|
||||
|
||||
### Breaking Changes
|
||||
- **Expressions renamed**: `{ $path }` / `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }`
|
||||
- **Visibility conditions**: `{ path }` → `{ $state }`, `{ and/or/not }` → `{ $and/$or }` with `not` as operator flag
|
||||
- **DynamicValue**: `{ path: string }` → `{ $state: string }`
|
||||
- **Repeat field**: `repeat.path` → `repeat.statePath`
|
||||
- **Action params**: `path` → `statePath` in setState action params
|
||||
- **Provider props**: `actionHandlers` → `handlers` on `JSONUIProvider`/`ActionProvider`
|
||||
- **Auth removed**: `AuthState` type and `{ auth }` visibility conditions removed — model auth as regular state
|
||||
- **Legacy catalog removed**: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`, `ComponentDefinition`, `CatalogConfig`, `SystemPromptOptions` removed
|
||||
- **React exports removed**: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- **Codegen**: `traverseTree` → `traverseSpec`, `SpecVisitor` → `TreeVisitor`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [06b8745]
|
||||
- @json-render/core@0.6.0
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React renderer for @json-render/core. JSON becomes React components.",
|
||||
"keywords": [
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import {
|
||||
@@ -137,6 +138,11 @@ export function ActionProvider({
|
||||
children,
|
||||
}: ActionProviderProps) {
|
||||
const { state, get, set } = useStateStore();
|
||||
// Keep a ref to the latest state so `execute` doesn't change on every
|
||||
// state update — preventing the entire action context from churning.
|
||||
const stateRef = useRef(state);
|
||||
stateRef.current = state;
|
||||
|
||||
const [handlers, setHandlers] =
|
||||
useState<Record<string, ActionHandler>>(initialHandlers);
|
||||
const [loadingActions, setLoadingActions] = useState<Set<string>>(new Set());
|
||||
@@ -152,7 +158,7 @@ export function ActionProvider({
|
||||
|
||||
const execute = useCallback(
|
||||
async (binding: ActionBinding) => {
|
||||
const resolved = resolveAction(binding, state);
|
||||
const resolved = resolveAction(binding, stateRef.current);
|
||||
|
||||
// Built-in: setState updates the StateProvider state directly
|
||||
if (resolved.action === "setState" && resolved.params) {
|
||||
@@ -299,7 +305,7 @@ export function ActionProvider({
|
||||
});
|
||||
}
|
||||
},
|
||||
[state, handlers, get, set, navigate],
|
||||
[handlers, get, set, navigate],
|
||||
);
|
||||
|
||||
const confirm = useCallback(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import {
|
||||
@@ -130,6 +131,11 @@ export function ValidationProvider({
|
||||
children,
|
||||
}: ValidationProviderProps) {
|
||||
const { state } = useStateStore();
|
||||
// Keep a ref to the latest state so `validate` doesn't change on every
|
||||
// state update — preventing the entire validation context from churning.
|
||||
const stateRef = useRef(state);
|
||||
stateRef.current = state;
|
||||
|
||||
const [fieldStates, setFieldStates] = useState<
|
||||
Record<string, FieldValidationState>
|
||||
>({});
|
||||
@@ -155,8 +161,9 @@ export function ValidationProvider({
|
||||
const validate = useCallback(
|
||||
(path: string, config: ValidationConfig): ValidationResult => {
|
||||
// Walk the nested state object using JSON Pointer segments
|
||||
const currentState = stateRef.current;
|
||||
const segments = path.split("/").filter(Boolean);
|
||||
let value: unknown = state;
|
||||
let value: unknown = currentState;
|
||||
for (const seg of segments) {
|
||||
if (value != null && typeof value === "object") {
|
||||
value = (value as Record<string, unknown>)[seg];
|
||||
@@ -167,7 +174,7 @@ export function ValidationProvider({
|
||||
}
|
||||
const result = runValidation(config, {
|
||||
value,
|
||||
stateModel: state,
|
||||
stateModel: currentState,
|
||||
customFunctions,
|
||||
});
|
||||
|
||||
@@ -182,7 +189,7 @@ export function ValidationProvider({
|
||||
|
||||
return result;
|
||||
},
|
||||
[state, customFunctions],
|
||||
[customFunctions],
|
||||
);
|
||||
|
||||
const touch = useCallback((path: string) => {
|
||||
@@ -280,7 +287,7 @@ export function useFieldValidation(
|
||||
|
||||
// Register field on mount
|
||||
React.useEffect(() => {
|
||||
if (config) {
|
||||
if (path && config) {
|
||||
registerField(path, config);
|
||||
}
|
||||
}, [path, config, registerField]);
|
||||
|
||||
@@ -250,6 +250,13 @@ export function useUIStream({
|
||||
const [rawLines, setRawLines] = useState<string[]>([]);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
// Keep refs to callbacks so `send` doesn't recreate when consumers
|
||||
// pass inline arrow functions.
|
||||
const onCompleteRef = useRef(onComplete);
|
||||
onCompleteRef.current = onComplete;
|
||||
const onErrorRef = useRef(onError);
|
||||
onErrorRef.current = onError;
|
||||
|
||||
const clear = useCallback(() => {
|
||||
setSpec(null);
|
||||
setError(null);
|
||||
@@ -350,19 +357,19 @@ export function useUIStream({
|
||||
}
|
||||
}
|
||||
|
||||
onComplete?.(currentSpec);
|
||||
onCompleteRef.current?.(currentSpec);
|
||||
} catch (err) {
|
||||
if ((err as Error).name === "AbortError") {
|
||||
return;
|
||||
}
|
||||
const error = err instanceof Error ? err : new Error(String(err));
|
||||
setError(error);
|
||||
onError?.(error);
|
||||
onErrorRef.current?.(error);
|
||||
} finally {
|
||||
setIsStreaming(false);
|
||||
}
|
||||
},
|
||||
[api, onComplete, onError],
|
||||
[api],
|
||||
);
|
||||
|
||||
// Cleanup on unmount
|
||||
|
||||
@@ -1,5 +1,88 @@
|
||||
# @json-render/remotion
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ea97aff]
|
||||
- @json-render/core@0.6.1
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 06b8745: Chat mode (inline GenUI), AI SDK integration, two-way binding, and expression-based visibility/props.
|
||||
|
||||
### New: Chat Mode (Inline GenUI)
|
||||
|
||||
Two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets AI respond conversationally with embedded UI specs — ideal for chatbots and copilot experiences.
|
||||
- `catalog.prompt({ mode: "chat" })` generates a chat-aware system prompt
|
||||
- `pipeJsonRender()` server-side transform separates text from JSONL patches in a mixed stream
|
||||
- `createJsonRenderTransform()` low-level TransformStream for custom pipelines
|
||||
|
||||
### New: AI SDK Integration
|
||||
|
||||
First-class Vercel AI SDK support with typed data parts and stream utilities.
|
||||
- `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
|
||||
- `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
|
||||
- `createMixedStreamParser()` for parsing mixed text + JSONL streams
|
||||
|
||||
### New: React Chat Hooks
|
||||
- `useChatUI()` — full chat hook with message history, streaming, and spec extraction
|
||||
- `useJsonRenderMessage()` — extract spec + text from a message's parts array
|
||||
- `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
|
||||
- `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem` expressions
|
||||
|
||||
### New: Two-Way Binding
|
||||
|
||||
Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state.
|
||||
|
||||
### New: Expression-Based Props and Visibility
|
||||
|
||||
Replaced string token rewriting with structured expression objects:
|
||||
- Props: `{ $state: "/path" }`, `{ $item: "field" }`, `{ $index: true }`
|
||||
- Visibility: `{ $state: "/path", eq: "value" }`, `{ $item: "active" }`, `{ $index: true, gt: 0 }`
|
||||
- Logic: `{ $and: [...] }`, `{ $or: [...] }`, and implicit AND via arrays
|
||||
- Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`
|
||||
|
||||
### New: Utilities
|
||||
- `applySpecPatch()` — typed convenience wrapper for applying a single patch to a Spec
|
||||
- `nestedToFlat()` — convert nested tree specs to flat `{ root, elements }` format
|
||||
- `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
|
||||
|
||||
### New: Chat Example
|
||||
|
||||
Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming UI generation.
|
||||
|
||||
### Improved: Renderer
|
||||
- `ElementRenderer` is now `React.memo`'d for better performance in repeat lists
|
||||
- `emit` is always defined (never `undefined`) — no more optional chaining needed
|
||||
- Action params are resolved through `resolveActionParam` supporting `$item`, `$index`, `$state`
|
||||
- Repeat scope now passes the actual item object instead of requiring token rewriting
|
||||
|
||||
### Breaking Changes
|
||||
- **Expressions renamed**: `{ $path }` / `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }`
|
||||
- **Visibility conditions**: `{ path }` → `{ $state }`, `{ and/or/not }` → `{ $and/$or }` with `not` as operator flag
|
||||
- **DynamicValue**: `{ path: string }` → `{ $state: string }`
|
||||
- **Repeat field**: `repeat.path` → `repeat.statePath`
|
||||
- **Action params**: `path` → `statePath` in setState action params
|
||||
- **Provider props**: `actionHandlers` → `handlers` on `JSONUIProvider`/`ActionProvider`
|
||||
- **Auth removed**: `AuthState` type and `{ auth }` visibility conditions removed — model auth as regular state
|
||||
- **Legacy catalog removed**: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`, `ComponentDefinition`, `CatalogConfig`, `SystemPromptOptions` removed
|
||||
- **React exports removed**: `createRendererFromCatalog`, `rewriteRepeatTokens`
|
||||
- **Codegen**: `traverseTree` → `traverseSpec`, `SpecVisitor` → `TreeVisitor`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [06b8745]
|
||||
- @json-render/core@0.6.0
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/remotion",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Remotion renderer for @json-render/core. JSON becomes video compositions.",
|
||||
"keywords": [
|
||||
|
||||
Reference in New Issue
Block a user