Compare commits

...
Author SHA1 Message Date
github-actions[bot] 7628640984 chore: version packages (#243)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-23 13:30:11 -05:00
Chris Tate bf3a7ec61d prepare v0.15 release (#242) 2026-03-23 13:23:41 -05:00
Chris Tate 453484985a ink updates (#241)
* ink updates

* no emojis

* design

* fit content

* fixes

* fixes
2026-03-23 13:12:31 -05:00
89 changed files with 738 additions and 120 deletions
+10
View File
@@ -1,5 +1,15 @@
# web # web
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/codegen@0.15.0
- @json-render/react@0.15.0
- @json-render/yaml@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
+9
View File
@@ -1,5 +1,14 @@
# example-chat # example-chat
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
- @json-render/shadcn@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-chat", "name": "example-chat",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+9
View File
@@ -1,5 +1,14 @@
# example-dashboard # example-dashboard
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/codegen@0.15.0
- @json-render/react@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-dashboard", "name": "example-dashboard",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+11
View File
@@ -0,0 +1,11 @@
# example-game-engine
## 0.1.1
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
- @json-render/react-three-fiber@0.15.0
- @json-render/yaml@0.15.0
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-game-engine", "name": "example-game-engine",
"version": "0.1.0", "version": "0.1.1",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-image # example-image
## 0.1.7
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/image@0.15.0
## 0.1.6 ## 0.1.6
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-image", "name": "example-image",
"version": "0.1.6", "version": "0.1.7",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+9
View File
@@ -0,0 +1,9 @@
# example-ink-chat
## 0.1.1
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/ink@0.15.0
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-ink-chat", "name": "example-ink-chat",
"version": "0.1.0", "version": "0.1.1",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+81 -31
View File
@@ -85,29 +85,72 @@ function getStepHint(type: string, isLast: boolean): string {
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// System prompt — embeds catalog documentation so the AI knows what components // System prompt — handwritten design guidance + catalog documentation.
// are available and how to output JSONL patches. // Follows the same pattern as examples/chat/lib/agent.ts: a rich
// AGENT_INSTRUCTIONS string with catalog.prompt() appended at the end.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const SYSTEM_PROMPT = catalog.prompt({ const AGENT_INSTRUCTIONS = `You are a terminal assistant that renders polished, information-dense UIs. You call tools for real-time data, then build clean terminal dashboards.
system:
"You are a terminal chat assistant. Use tools for real-time data (web_search, get_weather, get_hacker_news, get_github_repo, get_crypto_price), then render results as rich terminal UIs.", WORKFLOW:
1. Call the appropriate tools to gather real data. Use web_search for topics not covered by the specialized tools (get_weather, get_hacker_news, get_github_repo, get_crypto_price).
2. While tools run, output a single short status line (e.g. "Looking up weather data..."). This is the ONLY text allowed outside the spec fence.
3. After tools return, output ALL content inside a \`\`\`spec fence. Never write paragraphs of prose outside the fence.
4. For simple text replies (greetings, clarifications), still use a \`\`\`spec with a Markdown component.
DESIGN PRINCIPLES:
- HIERARCHY: Every response needs clear visual structure. Start with an h1 Heading for the topic. Use h2 Headings for subsections. Use Card to group related content into shaded areas — Cards render as subtle background fills, not bordered boxes.
- LEAD WITH THE STORY: Open with a brief Markdown paragraph (2-3 sentences) that tells the user the key insight or takeaway. Don't just dump data — frame it.
- SUMMARY METRICS: After the narrative, show 2-4 Metric components for the most important numbers. Metric displays a dim label, bold value, and optional colored trend (up=green, down=red). Group them in a horizontal Box (flexDirection: row, gap: 3) so they read like a dashboard header. Use KeyValue only for simple label:value pairs that don't need emphasis.
- DETAIL SECTIONS: Below the summary, use h2 Headings to introduce each section, followed by a single focused visualization (Table, BarChart, or set of KeyValues).
- ONE REPRESENTATION PER DATA POINT: Never show the same value as both a number and a percentage and a bar. Pick the most meaningful format. Use BarChart with showValues:true OR showPercentage:true, not both.
- TABLES: Always set explicit column widths so columns don't collapse. Use headerColor:"cyan". Keep column headers short (abbreviate if needed). Right-align numeric columns.
- CHARTS: Use distinct colors per bar in BarChart. Good palette: cyan, green, yellow, magenta, blue, red. Use Sparkline for compact inline trends alongside other content.
- COLOR STRATEGY: Use color with intention, not decoration. cyan for labels and headers. green for positive values, growth, success. red for negative values, decline, errors. yellow for warnings or neutral highlights. dimColor:true for secondary/supporting text. Avoid coloring everything — contrast comes from restraint.
- TABLES: Use borderStyle:"single" on Tables for a clean outline. Do NOT put Tables inside Cards — Tables have their own border and don't need additional wrapping.
- SPACING: Use gap:1 between sections. Don't over-pad. Keep the UI compact and scannable. NEVER add padding to the root element — the app already provides outer padding.
- WIDTH: Target 80 columns. Set explicit widths on Tables (total columns should sum to ~70-75). Use wrap:"truncate-end" on Text in tight spaces.
- CALLOUTS: Use Callout for key takeaways, important notes, tips, and warnings. Set type (info/tip/warning/important) for a colored left border accent. Keep content concise — one key point per Callout.
- TIMELINES: Use Timeline for historical events, step-by-step processes, and milestones. Set status per item (completed/current/upcoming) for colored dots. Include dates when available.
- NEVER use emojis anywhere — not in text, labels, titles, table cells, Heading text, or component props. Plain text only.
DASHBOARD PATTERN (use for data-heavy responses):
Root Box (column, gap:1) >
Heading (h1, topic title)
Markdown (2-3 sentence summary with key takeaway)
Box (row, gap:3) > [Metric, Metric, Metric] (top-line metrics, no Card)
Heading (h2, section title)
Table (borderStyle:"single")
Card (title:"Section Name") > BarChart (bar charts go in a titled Card — the Card title replaces h2)
Callout (type:"tip", key takeaway or closing note)
Card wrapping rules: Wrap BarCharts in a Card with a title. Do NOT wrap Metrics or Tables in Cards — Metrics stand alone, Tables have their own border.
COMPARISON PATTERN:
Use BarChart when you want the user to see relative magnitudes at a glance.
Use Table when there are 3+ columns of mixed data types.
Never use both for the same data.
TREND PATTERN:
Use Sparkline for compact inline trend next to a KeyValue.
Use BarChart with year/period labels for detailed time-series.
INTERACTIVITY:
- You can create interactive forms, surveys, and selection interfaces. The user navigates with arrow keys, selects with Space/Enter, and types into text fields.
- ALWAYS include a submit action on interactive UIs. Add a Text or StatusLine telling the user how to submit. Wire submit events to a "submit" action — the app collects form state automatically.
- ALWAYS populate the state field with sensible defaults for all bound values.
- Use $bindState on interactive components for two-way binding. Example: { "value": { "$state": "/choice" }, "$bindState": { "value": "/choice" } }.
- Use Tabs for multi-section surveys. Use ConfirmInput for yes/no prompts.
- After receiving form data, acknowledge the user's choices meaningfully — don't just echo them back.
${catalog.prompt({
mode: "inline", mode: "inline",
customRules: [ customRules: [
"ALL text MUST go inside the spec using the Markdown component. NEVER write prose, summaries, or explanations outside the ```spec fence. The ONLY text allowed outside the fence is a single short status like 'Searching…' while tools run.", "ALL text MUST go inside the spec using the Markdown component. The ONLY text outside the fence is a short tool-status line.",
"For text-only answers (greetings, clarifications), still output a ```spec with a Markdown component.", "For text-only answers, still output a spec with a Markdown component.",
"Prefer Table for structured data (forecasts, leaderboards, language breakdowns) and KeyValue for label-value pairs (stats, metadata).", "Prefer Table for structured data and KeyValue for label-value pairs.",
"NEVER use emojis anywhere in your output. Plain text only.",
// Interactive components
"INTERACTIVE UIs: You can create interactive forms, surveys, and selection interfaces that the user actually interacts with in the terminal. The user navigates with arrow keys, selects with Space/Enter, and types into text fields.",
"When creating interactive UIs, ALWAYS include a submit action. Add an element (usually a Text or StatusLine) that tells the user to press Enter or a specific key to submit. Wire up the submit event to a 'submit' action — the app will collect the form state and send it back to you automatically.",
"For interactive specs, ALWAYS populate the state field with sensible defaults for all bound values. Example: if a Select binds to /framework, include state: { framework: 'react' }.",
'IMPORTANT: Use $bindState on interactive components so their values are written back to state. Example for Select: { "value": { "$state": "/choice" }, "$bindState": { "value": "/choice" } }.',
"For surveys/quizzes with multiple sections, use Tabs to organize them. Bind the active tab to state and use visible conditions on children.",
"For confirmation prompts, use ConfirmInput with a clear message. The confirm/deny events can trigger actions.",
"After receiving submitted form data, acknowledge the user's choices and respond to them — don't just repeat what they selected.",
], ],
}); })}`;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Types // Types
@@ -236,7 +279,6 @@ function MessageView({ message }: { message: Message }) {
return ( return (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold>AI:</Text>
{message.spec ? ( {message.spec ? (
<JSONUIProvider initialState={message.spec.state ?? {}}> <JSONUIProvider initialState={message.spec.state ?? {}}>
<DisableFocus /> <DisableFocus />
@@ -317,9 +359,6 @@ function LiveInteractiveSpec({
if (interactiveKeys.length === 0) { if (interactiveKeys.length === 0) {
return ( return (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color="green">
AI:
</Text>
<JSONUIProvider store={store} handlers={handlers}> <JSONUIProvider store={store} handlers={handlers}>
<Renderer spec={spec} /> <Renderer spec={spec} />
</JSONUIProvider> </JSONUIProvider>
@@ -331,9 +370,6 @@ function LiveInteractiveSpec({
return ( return (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold color="green">
AI:
</Text>
{interactiveKeys.length > 1 && ( {interactiveKeys.length > 1 && (
<Text dimColor> <Text dimColor>
Step {step + 1} of {interactiveKeys.length} Step {step + 1} of {interactiveKeys.length}
@@ -417,7 +453,7 @@ export function App() {
try { try {
const result = streamText({ const result = streamText({
model: gateway(process.env.AI_GATEWAY_MODEL || DEFAULT_MODEL), model: gateway(process.env.AI_GATEWAY_MODEL || DEFAULT_MODEL),
system: SYSTEM_PROMPT, system: AGENT_INSTRUCTIONS,
messages: history, messages: history,
temperature: 0.7, temperature: 0.7,
abortSignal: controller.signal, abortSignal: controller.signal,
@@ -535,11 +571,26 @@ export function App() {
return ( return (
<Box flexDirection="column" padding={1} minHeight={stdout.rows}> <Box flexDirection="column" padding={1} minHeight={stdout.rows}>
{/* Header */} {/* Header */}
<Box marginBottom={1}> <Box marginBottom={1} gap={1}>
<Text bold>json-render terminal chat</Text> <Text bold color="cyan">
<Text dimColor> (Ctrl+C to exit)</Text> json-render
</Text>
<Text dimColor>Ctrl+C to exit</Text>
</Box> </Box>
{/* Empty state — show example prompts when no conversation yet */}
{messages.length === 0 && !isStreaming && (
<Box flexDirection="column" marginBottom={1}>
<Text dimColor>Try asking:</Text>
<Box flexDirection="column" paddingLeft={2} marginTop={1} gap={0}>
<Text dimColor>{" weather in tokyo"}</Text>
<Text dimColor>{" top hacker news stories"}</Text>
<Text dimColor>{" tell me about vercel/next.js"}</Text>
<Text dimColor>{" bitcoin price"}</Text>
</Box>
</Box>
)}
{/* Message history — collapsed when interactive wizard is active */} {/* Message history — collapsed when interactive wizard is active */}
{liveSpec && !isStreaming ? ( {liveSpec && !isStreaming ? (
<> <>
@@ -568,10 +619,9 @@ export function App() {
{/* Live spec preview while streaming */} {/* Live spec preview while streaming */}
{isStreaming && streamingSpec && streamingSpec.root && ( {isStreaming && streamingSpec && streamingSpec.root && (
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column" marginBottom={1}>
<Text bold>AI:</Text>
<JSONUIProvider initialState={streamingSpec.state ?? {}}> <JSONUIProvider initialState={streamingSpec.state ?? {}}>
<DisableFocus /> <DisableFocus />
<Renderer spec={streamingSpec} /> <Renderer spec={streamingSpec} loading />
</JSONUIProvider> </JSONUIProvider>
</Box> </Box>
)} )}
+2 -1
View File
@@ -275,7 +275,8 @@ export const getGitHubRepo = tool({
const languageBreakdown = Object.entries(languages) const languageBreakdown = Object.entries(languages)
.map(([lang, bytes]) => ({ .map(([lang, bytes]) => ({
language: lang, language: lang,
percentage: Math.round((bytes / totalBytes) * 100), percentage:
totalBytes > 0 ? Math.round((bytes / totalBytes) * 100) : 0,
})) }))
.sort((a, b) => b.percentage - a.percentage) .sort((a, b) => b.percentage - a.percentage)
.slice(0, 6); .slice(0, 6);
+10
View File
@@ -1,5 +1,15 @@
# example-mcp # example-mcp
## 0.1.6
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/mcp@0.15.0
- @json-render/react@0.15.0
- @json-render/shadcn@0.15.0
## 0.1.5 ## 0.1.5
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-mcp", "name": "example-mcp",
"version": "0.1.5", "version": "0.1.6",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+9
View File
@@ -1,5 +1,14 @@
# example-no-ai # example-no-ai
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
- @json-render/shadcn@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-no-ai", "name": "example-no-ai",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-react-email # example-react-email
## 0.1.6
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react-email@0.15.0
## 0.1.5 ## 0.1.5
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-react-email", "name": "example-react-email",
"version": "0.1.5", "version": "0.1.6",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-react-native # example-react-native
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react-native@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-react-native", "name": "example-react-native",
"version": "0.1.9", "version": "0.1.10",
"private": true, "private": true,
"main": "expo-router/entry", "main": "expo-router/entry",
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-react-pdf # example-react-pdf
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react-pdf@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-react-pdf", "name": "example-react-pdf",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+9
View File
@@ -1,5 +1,14 @@
# example-react-three-fiber # example-react-three-fiber
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
- @json-render/react-three-fiber@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-react-three-fiber", "name": "example-react-three-fiber",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-remotion # example-remotion
## 0.1.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/remotion@0.15.0
## 0.1.9 ## 0.1.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-remotion", "name": "example-remotion",
"version": "0.1.9", "version": "0.1.10",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-solid # example-solid
## 0.1.4
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/solid@0.15.0
## 0.1.3 ## 0.1.3
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-solid", "name": "example-solid",
"version": "0.1.3", "version": "0.1.4",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -1,5 +1,13 @@
# com.example.json-render-demo # com.example.json-render-demo
## 0.0.11
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
## 0.0.10 ## 0.0.10
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "com.example.json-render-demo", "name": "com.example.json-render-demo",
"version": "0.0.10", "version": "0.0.11",
"description": "Test", "description": "Test",
"private": true, "private": true,
"license": "~~proprietary~~", "license": "~~proprietary~~",
@@ -1,5 +1,13 @@
# com.example.json-render-fullpage-demo # com.example.json-render-fullpage-demo
## 0.0.11
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
## 0.0.10 ## 0.0.10
### Patch Changes ### Patch Changes
@@ -1,6 +1,6 @@
{ {
"name": "com.example.json-render-fullpage-demo", "name": "com.example.json-render-fullpage-demo",
"version": "0.0.10", "version": "0.0.11",
"description": "Full-page Stripe App example (alpha)", "description": "Full-page Stripe App example (alpha)",
"private": true, "private": true,
"license": "~~proprietary~~", "license": "~~proprietary~~",
+8
View File
@@ -1,5 +1,13 @@
# svelte-chat # svelte-chat
## 0.0.7
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/svelte@0.15.0
## 0.0.6 ## 0.0.6
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "svelte-chat", "name": "svelte-chat",
"private": true, "private": true,
"version": "0.0.6", "version": "0.0.7",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
+8
View File
@@ -1,5 +1,13 @@
# example-svelte # example-svelte
## 0.1.7
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/svelte@0.15.0
## 0.1.6 ## 0.1.6
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-svelte", "name": "example-svelte",
"version": "0.1.6", "version": "0.1.7",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+11
View File
@@ -1,5 +1,16 @@
# vite-renderers # vite-renderers
## 0.1.8
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
- @json-render/solid@0.15.0
- @json-render/svelte@0.15.0
- @json-render/vue@0.15.0
## 0.1.7 ## 0.1.7
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "vite-renderers", "name": "vite-renderers",
"version": "0.1.7", "version": "0.1.8",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+8
View File
@@ -1,5 +1,13 @@
# example-vue # example-vue
## 0.1.8
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/vue@0.15.0
## 0.1.7 ## 0.1.7
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "example-vue", "name": "example-vue",
"version": "0.1.7", "version": "0.1.8",
"private": true, "private": true,
"scripts": { "scripts": {
"predev": "command -v portless >/dev/null 2>&1 || (echo '\\nportless is required but not installed. Run: npm i -g portless\\nSee: https://github.com/vercel-labs/portless\\n' && exit 1)", "predev": "command -v portless >/dev/null 2>&1 || (echo '\\nportless is required but not installed. Run: npm i -g portless\\nSee: https://github.com/vercel-labs/portless\\n' && exit 1)",
+7
View File
@@ -1,5 +1,12 @@
# @json-render/codegen # @json-render/codegen
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/codegen", "name": "@json-render/codegen",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Utilities for generating code from json-render UI trees", "description": "Utilities for generating code from json-render UI trees",
"keywords": [ "keywords": [
+12
View File
@@ -1,5 +1,17 @@
# @json-render/core # @json-render/core
## 0.15.0
### Minor Changes
- bf3a7ec: Add Ink terminal renderer for interactive terminal UIs.
### New:
- **`@json-render/ink`** -- Terminal UI renderer for json-render, built on Ink. Includes 20+ standard components (Box, Text, Heading, Card, Table, TextInput, Select, MultiSelect, Tabs, etc.), action/validation/focus contexts, two-way state binding, and streaming via `useUIStream`. Server-safe entry points at `@json-render/ink/schema`, `@json-render/ink/catalog`, and `@json-render/ink/server`.
### Improved:
- **Examples** -- new `ink-chat` terminal chat demo, `game-engine` 3D example using react-three-fiber, website examples page with live demos and search.
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/core", "name": "@json-render/core",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "JSON becomes real things. Define your catalog, register your components, let AI generate.", "description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/image # @json-render/image
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/image", "name": "@json-render/image",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Image renderer for @json-render/core. JSON becomes SVG and PNG images via Satori.", "description": "Image renderer for @json-render/core. JSON becomes SVG and PNG images via Satori.",
"keywords": [ "keywords": [
+18
View File
@@ -0,0 +1,18 @@
# @json-render/ink
## 0.15.0
### Minor Changes
- bf3a7ec: Add Ink terminal renderer for interactive terminal UIs.
### New:
- **`@json-render/ink`** -- Terminal UI renderer for json-render, built on Ink. Includes 20+ standard components (Box, Text, Heading, Card, Table, TextInput, Select, MultiSelect, Tabs, etc.), action/validation/focus contexts, two-way state binding, and streaming via `useUIStream`. Server-safe entry points at `@json-render/ink/schema`, `@json-render/ink/catalog`, and `@json-render/ink/server`.
### Improved:
- **Examples** -- new `ink-chat` terminal chat demo, `game-engine` 3D example using react-three-fiber, website examples page with live demos and search.
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/ink", "name": "@json-render/ink",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Ink terminal renderer for @json-render/core. JSON becomes terminal UIs.", "description": "Ink terminal renderer for @json-render/core. JSON becomes terminal UIs.",
"keywords": [ "keywords": [
@@ -57,7 +57,8 @@
"scripts": { "scripts": {
"build": "tsup", "build": "tsup",
"dev": "tsup --watch", "dev": "tsup --watch",
"check-types": "tsc --noEmit" "check-types": "tsc --noEmit",
"typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@json-render/core": "workspace:*", "@json-render/core": "workspace:*",
+77 -11
View File
@@ -259,11 +259,12 @@ export const standardComponentDefinitions = {
borderStyle: z borderStyle: z
.enum(["single", "double", "round", "bold", "classic"]) .enum(["single", "double", "round", "bold", "classic"])
.nullable(), .nullable(),
backgroundColor: z.string().nullable(),
headerColor: z.string().nullable(), headerColor: z.string().nullable(),
}), }),
slots: [], slots: [],
description: description:
"Tabular data display with headers and rows. Each row is a record mapping column keys to string values.", "Tabular data display with headers and rows. Each row is a record mapping column keys to string values. Set both borderStyle and backgroundColor together so borders share the same shading.",
example: { example: {
columns: [ columns: [
{ header: "Name", key: "name", width: 20 }, { header: "Name", key: "name", width: 20 },
@@ -306,7 +307,7 @@ export const standardComponentDefinitions = {
example: { example: {
title: "package.json", title: "package.json",
subtitle: "Modified 2 hours ago", subtitle: "Modified 2 hours ago",
leading: "📄", leading: "*",
trailing: "2.1 KB", trailing: "2.1 KB",
}, },
}, },
@@ -314,16 +315,13 @@ export const standardComponentDefinitions = {
Card: { Card: {
props: z.object({ props: z.object({
title: z.string().nullable(), title: z.string().nullable(),
borderStyle: z backgroundColor: z.string().nullable(),
.enum(["single", "double", "round", "bold", "classic"])
.nullable(),
borderColor: z.string().nullable(),
padding: z.number().nullable(), padding: z.number().nullable(),
}), }),
slots: ["default"], slots: ["default"],
description: description:
"Bordered container with optional title. Use for grouping related content with a visual boundary.", "Shaded container with optional title. Renders as a filled background area for grouping related content. Default background is a subtle dark shade.",
example: { title: "Details", borderStyle: "round", padding: 1 }, example: { title: "Details", padding: 1 },
}, },
KeyValue: { KeyValue: {
@@ -363,6 +361,74 @@ export const standardComponentDefinitions = {
example: { text: "Build completed successfully", status: "success" }, example: { text: "Build completed successfully", status: "success" },
}, },
Metric: {
props: z.object({
label: z.string(),
value: z.string(),
detail: z.string().nullable(),
trend: z.enum(["up", "down", "neutral"]).nullable(),
}),
slots: [],
description:
"Key metric display with prominent value and optional trend indicator. Use for important numbers that deserve visual emphasis (price, temperature, stars, market cap).",
example: {
label: "Price",
value: "$70,686",
detail: "24h change",
trend: "up",
},
},
Callout: {
props: z.object({
type: z.enum(["info", "tip", "warning", "important"]).nullable(),
title: z.string().nullable(),
content: z.string(),
}),
slots: [],
description:
"Highlighted callout block with colored left border. Use for key takeaways, tips, warnings, or important notes that should stand out from surrounding content.",
example: {
type: "tip",
title: "Key Takeaway",
content:
"Revenue peaked in FY2022 driven by iPhone 13/14 upgrade cycles.",
},
},
Timeline: {
props: z.object({
items: z.array(
z.object({
title: z.string(),
description: z.string().nullable(),
date: z.string().nullable(),
status: z.enum(["completed", "current", "upcoming"]).nullable(),
}),
),
}),
slots: [],
description:
"Vertical timeline showing ordered events, steps, or milestones. Each item has a status-colored dot, title, optional date, and optional description.",
example: {
items: [
{
title: "Project Started",
description: "Initial commit and setup",
date: "Jan 2024",
status: "completed",
},
{
title: "Beta Release",
description: "Public beta launched",
date: "Mar 2024",
status: "current",
},
{ title: "v1.0", date: "Q2 2024", status: "upcoming" },
],
},
},
// ========================================================================== // ==========================================================================
// Interactive Components // Interactive Components
// ========================================================================== // ==========================================================================
@@ -468,9 +534,9 @@ export const standardComponentDefinitions = {
"Tab bar navigation. Navigate with left/right arrow keys. Use $bindState on value to bind the active tab to state. Place child content inside and use visible conditions on children to show content for the active tab.", "Tab bar navigation. Navigate with left/right arrow keys. Use $bindState on value to bind the active tab to state. Place child content inside and use visible conditions on children to show content for the active tab.",
example: { example: {
tabs: [ tabs: [
{ label: "Overview", value: "overview", icon: "📊" }, { label: "Overview", value: "overview" },
{ label: "Logs", value: "logs", icon: "📋" }, { label: "Logs", value: "logs" },
{ label: "Settings", value: "settings", icon: "⚙" }, { label: "Settings", value: "settings" },
], ],
}, },
}, },
+166 -27
View File
@@ -325,6 +325,7 @@ function TableComponent({ element }: ComponentRenderProps) {
}>; }>;
rows?: Array<Record<string, string>>; rows?: Array<Record<string, string>>;
borderStyle?: string; borderStyle?: string;
backgroundColor?: string;
headerColor?: string; headerColor?: string;
}; };
@@ -363,7 +364,12 @@ function TableComponent({ element }: ComponentRenderProps) {
| undefined; | undefined;
return ( return (
<Box flexDirection="column" borderStyle={borderStyleProp}> <Box
flexDirection="column"
alignSelf="flex-start"
borderStyle={borderStyleProp}
backgroundColor={p.backgroundColor}
>
{/* Header */} {/* Header */}
<Box> <Box>
{columns.map((col, i) => ( {columns.map((col, i) => (
@@ -375,15 +381,19 @@ function TableComponent({ element }: ComponentRenderProps) {
{/* Separator */} {/* Separator */}
<Text dimColor>{colWidths.map((w) => "─".repeat(w)).join("─")}</Text> <Text dimColor>{colWidths.map((w) => "─".repeat(w)).join("─")}</Text>
{/* Rows */} {/* Rows */}
{rows.map((row, rowIdx) => ( {rows.map((row, rowIdx) => {
<Box key={rowIdx}> const rowKey =
{columns.map((col, i) => ( columns.map((c) => row[c.key] ?? "").join("\0") || String(rowIdx);
<Text key={col.key}> return (
{padCell(row[col.key] || "—", colWidths[i]!, col.align)} <Box key={rowKey} gap={0}>
</Text> {columns.map((col, i) => (
))} <Text key={col.key}>
</Box> {padCell(row[col.key] || "—", colWidths[i]!, col.align)}
))} </Text>
))}
</Box>
);
})}
</Box> </Box>
); );
} }
@@ -402,7 +412,7 @@ function ListComponent({ element }: ComponentRenderProps) {
return ( return (
<Box flexDirection="column" gap={p.spacing ?? 0}> <Box flexDirection="column" gap={p.spacing ?? 0}>
{items.map((item, i) => ( {items.map((item, i) => (
<Box key={i} gap={1}> <Box key={`${i}:${item}`} gap={1}>
<Text dimColor>{p.ordered ? `${i + 1}.` : bullet}</Text> <Text dimColor>{p.ordered ? `${i + 1}.` : bullet}</Text>
<Text>{item}</Text> <Text>{item}</Text>
</Box> </Box>
@@ -434,16 +444,15 @@ function ListItemComponent({ element }: ComponentRenderProps) {
function CardComponent({ element, children }: ComponentRenderProps) { function CardComponent({ element, children }: ComponentRenderProps) {
const p = element.props as { const p = element.props as {
title?: string; title?: string;
borderStyle?: "single" | "double" | "round" | "bold" | "classic"; backgroundColor?: string;
borderColor?: string;
padding?: number; padding?: number;
}; };
return ( return (
<Box <Box
flexDirection="column" flexDirection="column"
borderStyle={p.borderStyle ?? "round"} alignSelf="flex-start"
borderColor={p.borderColor} backgroundColor={p.backgroundColor ?? "#1a1a1a"}
padding={p.padding ?? 1} padding={p.padding ?? 1}
> >
{p.title ? ( {p.title ? (
@@ -537,10 +546,136 @@ function StatusLineComponent({ element }: ComponentRenderProps) {
); );
} }
// =============================================================================
// Metric, Callout, Timeline
// =============================================================================
const TREND_CONFIG: Record<string, { prefix: string; color: string }> = {
up: { prefix: "+", color: "green" },
down: { prefix: "", color: "red" },
neutral: { prefix: "~", color: "gray" },
};
function MetricComponent({ element }: ComponentRenderProps) {
const p = element.props as {
label?: string;
value?: string;
detail?: string;
trend?: "up" | "down" | "neutral";
};
const trend = p.trend ? TREND_CONFIG[p.trend] : null;
return (
<Box flexDirection="column">
<Text dimColor>{p.label ?? ""}</Text>
<Box gap={1}>
<Text bold>{p.value ?? ""}</Text>
{trend && p.detail ? (
<Text color={trend.color}>
{trend.prefix}
{p.detail}
</Text>
) : null}
</Box>
{!trend && p.detail ? <Text dimColor>{p.detail}</Text> : null}
</Box>
);
}
const CALLOUT_CONFIG: Record<string, { label: string; color: string }> = {
info: { label: "INFO", color: "blue" },
tip: { label: "TIP", color: "green" },
warning: { label: "WARNING", color: "yellow" },
important: { label: "IMPORTANT", color: "magenta" },
};
function CalloutComponent({ element }: ComponentRenderProps) {
const p = element.props as {
type?: "info" | "tip" | "warning" | "important";
title?: string;
content?: string;
};
const config = CALLOUT_CONFIG[p.type ?? "info"] ?? CALLOUT_CONFIG.info!;
return (
<Box
flexDirection="column"
borderStyle="bold"
borderLeft
borderRight={false}
borderTop={false}
borderBottom={false}
borderColor={config.color}
paddingLeft={1}
>
{p.title ? (
<Text bold color={config.color}>
{p.title}
</Text>
) : null}
<Text>{p.content ?? ""}</Text>
</Box>
);
}
const TIMELINE_DOT: Record<string, { dot: string; color: string }> = {
completed: { dot: "●", color: "green" },
current: { dot: "◆", color: "cyan" },
upcoming: { dot: "○", color: "gray" },
};
function TimelineComponent({ element }: ComponentRenderProps) {
const p = element.props as {
items?: Array<{
title?: string;
description?: string;
date?: string;
status?: "completed" | "current" | "upcoming";
}>;
};
const items = p.items ?? [];
return (
<Box flexDirection="column" gap={1}>
{items.map((item, i) => {
const cfg =
TIMELINE_DOT[item.status ?? "upcoming"] ?? TIMELINE_DOT.upcoming!;
return (
<Box key={i} flexDirection="column">
<Box gap={1}>
<Text color={cfg.color}>{cfg.dot}</Text>
<Text bold>{item.title ?? ""}</Text>
{item.date ? <Text dimColor>{item.date}</Text> : null}
</Box>
{item.description ? (
<Box paddingLeft={2}>
<Text dimColor>{item.description}</Text>
</Box>
) : null}
</Box>
);
})}
</Box>
);
}
// ============================================================================= // =============================================================================
// Interactive Components // Interactive Components
// ============================================================================= // =============================================================================
/** Count code points instead of UTF-16 code units (handles emoji/surrogate pairs). */
function codePointLength(s: string): number {
return Array.from(s).length;
}
/** Slice by code point index instead of UTF-16 offset. */
function codePointSlice(s: string, start: number, end?: number): string {
return Array.from(s).slice(start, end).join("");
}
function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) { function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
const p = element.props as { const p = element.props as {
placeholder?: string; placeholder?: string;
@@ -556,11 +691,11 @@ function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
); );
const currentValue = value ?? ""; const currentValue = value ?? "";
const [cursorPos, setCursorPos] = useState(currentValue.length); const [cursorPos, setCursorPos] = useState(codePointLength(currentValue));
// Keep cursor in bounds when value changes externally // Keep cursor in bounds when value changes externally
useEffect(() => { useEffect(() => {
setCursorPos((prev) => Math.min(prev, currentValue.length)); setCursorPos((prev) => Math.min(prev, codePointLength(currentValue)));
}, [currentValue]); }, [currentValue]);
useInput( useInput(
@@ -573,8 +708,8 @@ function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
if (key.backspace || key.delete) { if (key.backspace || key.delete) {
if (cursorPos > 0) { if (cursorPos > 0) {
const newVal = const newVal =
currentValue.slice(0, cursorPos - 1) + codePointSlice(currentValue, 0, cursorPos - 1) +
currentValue.slice(cursorPos); codePointSlice(currentValue, cursorPos);
setValue(newVal); setValue(newVal);
setCursorPos((prev) => prev - 1); setCursorPos((prev) => prev - 1);
emit("change"); emit("change");
@@ -588,7 +723,9 @@ function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
return; return;
} }
if (key.rightArrow) { if (key.rightArrow) {
setCursorPos((prev) => Math.min(currentValue.length, prev + 1)); setCursorPos((prev) =>
Math.min(codePointLength(currentValue), prev + 1),
);
return; return;
} }
@@ -598,11 +735,11 @@ function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
if (input) { if (input) {
const newVal = const newVal =
currentValue.slice(0, cursorPos) + codePointSlice(currentValue, 0, cursorPos) +
input + input +
currentValue.slice(cursorPos); codePointSlice(currentValue, cursorPos);
setValue(newVal); setValue(newVal);
setCursorPos((prev) => prev + input.length); setCursorPos((prev) => prev + codePointLength(input));
emit("change"); emit("change");
} }
}, },
@@ -611,13 +748,12 @@ function TextInputComponent({ element, emit, bindings }: ComponentRenderProps) {
const displayValue = currentValue const displayValue = currentValue
? p.mask ? p.mask
? p.mask.repeat(currentValue.length) ? p.mask.repeat(codePointLength(currentValue))
: currentValue : currentValue
: ""; : "";
// Render with cursor position indicator const before = codePointSlice(displayValue, 0, cursorPos);
const before = displayValue.slice(0, cursorPos); const after = codePointSlice(displayValue, cursorPos);
const after = displayValue.slice(cursorPos);
return ( return (
<Box gap={1}> <Box gap={1}>
@@ -1178,6 +1314,9 @@ export const standardComponents: ComponentRegistry = {
KeyValue: KeyValueComponent, KeyValue: KeyValueComponent,
Link: LinkComponent, Link: LinkComponent,
StatusLine: StatusLineComponent, StatusLine: StatusLineComponent,
Metric: MetricComponent,
Callout: CalloutComponent,
Timeline: TimelineComponent,
TextInput: TextInputComponent, TextInput: TextInputComponent,
Select: SelectComponent, Select: SelectComponent,
MultiSelect: MultiSelectComponent, MultiSelect: MultiSelectComponent,
+18 -7
View File
@@ -101,8 +101,8 @@ export interface PendingConfirmation {
export interface ActionContextValue { export interface ActionContextValue {
/** Registered action handlers */ /** Registered action handlers */
handlers: Record<string, ActionHandler>; handlers: Record<string, ActionHandler>;
/** Actions currently executing (for loading indicators) */ /** Actions currently executing (count of in-flight executions per action name) */
loadingActions: Set<string>; loadingActions: Map<string, number>;
/** Pending confirmation dialog */ /** Pending confirmation dialog */
pendingConfirmation: PendingConfirmation | null; pendingConfirmation: PendingConfirmation | null;
/** Execute an action binding */ /** Execute an action binding */
@@ -141,7 +141,9 @@ export function ActionProvider({
initialHandlersRef.current = initialHandlers; initialHandlersRef.current = initialHandlers;
const navigateRef = useRef(navigate); const navigateRef = useRef(navigate);
navigateRef.current = navigate; navigateRef.current = navigate;
const [loadingActions, setLoadingActions] = useState<Set<string>>(new Set()); const [loadingActions, setLoadingActions] = useState<Map<string, number>>(
new Map(),
);
const [pendingConfirmation, setPendingConfirmation] = const [pendingConfirmation, setPendingConfirmation] =
useState<PendingConfirmation | null>(null); useState<PendingConfirmation | null>(null);
// Ref tracks current pending confirmation so overlapping confirms can // Ref tracks current pending confirmation so overlapping confirms can
@@ -267,7 +269,11 @@ export function ActionProvider({
} }
const actionName = resolved.action; const actionName = resolved.action;
setLoadingActions((prev) => new Set(prev).add(actionName)); setLoadingActions((prev) => {
const next = new Map(prev);
next.set(actionName, (next.get(actionName) ?? 0) + 1);
return next;
});
try { try {
await executeAction({ await executeAction({
action: resolved, action: resolved,
@@ -281,8 +287,13 @@ export function ActionProvider({
}); });
} finally { } finally {
setLoadingActions((prev) => { setLoadingActions((prev) => {
const next = new Set(prev); const next = new Map(prev);
next.delete(actionName); const count = (next.get(actionName) ?? 1) - 1;
if (count <= 0) {
next.delete(actionName);
} else {
next.set(actionName, count);
}
return next; return next;
}); });
} }
@@ -343,7 +354,7 @@ export function useAction(binding: ActionBinding): {
} { } {
const { execute, loadingActions } = useActions(); const { execute, loadingActions } = useActions();
const executeAction = useCallback(() => execute(binding), [execute, binding]); const executeAction = useCallback(() => execute(binding), [execute, binding]);
const isLoading = loadingActions.has(binding.action); const isLoading = (loadingActions.get(binding.action) ?? 0) > 0;
return { execute: executeAction, isLoading }; return { execute: executeAction, isLoading };
} }
+1 -1
View File
@@ -99,7 +99,7 @@ function parsePatchLine(line: string): ParseResult {
// Recovery: strip trailing extra braces/brackets one at a time // Recovery: strip trailing extra braces/brackets one at a time
// LLMs commonly generate extra closing characters in nested JSON // LLMs commonly generate extra closing characters in nested JSON
let attempt = trimmed; let attempt = trimmed;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 8; i++) {
const last = attempt[attempt.length - 1]; const last = attempt[attempt.length - 1];
if (last === "}" || last === "]") { if (last === "}" || last === "]") {
attempt = attempt.slice(0, -1); attempt = attempt.slice(0, -1);
+10
View File
@@ -5,6 +5,7 @@ import React, {
useCallback, useCallback,
useMemo, useMemo,
} from "react"; } from "react";
import { Text } from "ink";
import type { import type {
UIElement, UIElement,
Spec, Spec,
@@ -268,6 +269,9 @@ const ElementRenderer = React.memo(function ElementRenderer({
console.warn( console.warn(
`[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`, `[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`,
); );
return (
<Text key={childKey} color="red">{`[Missing: ${childKey}]`}</Text>
);
} }
return null; return null;
} }
@@ -346,6 +350,12 @@ function RepeatChildren({
console.warn( console.warn(
`[json-render] Missing element "${childKey}" referenced as child of "${element.type}" (repeat). This element will not render.`, `[json-render] Missing element "${childKey}" referenced as child of "${element.type}" (repeat). This element will not render.`,
); );
return (
<Text
key={`${key}:${childKey}`}
color="red"
>{`[Missing: ${childKey}]`}</Text>
);
} }
return null; return null;
} }
+7
View File
@@ -1,5 +1,12 @@
# @json-render/jotai # @json-render/jotai
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/jotai", "name": "@json-render/jotai",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Jotai adapter for json-render StateStore", "description": "Jotai adapter for json-render StateStore",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/mcp # @json-render/mcp
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/mcp", "name": "@json-render/mcp",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "MCP Apps integration for @json-render/core. Serve json-render UIs as interactive MCP Apps in Claude, ChatGPT, Cursor, and VS Code.", "description": "MCP Apps integration for @json-render/core. Serve json-render UIs as interactive MCP Apps in Claude, ChatGPT, Cursor, and VS Code.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/react-email # @json-render/react-email
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/react-email", "name": "@json-render/react-email",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "React Email renderer for @json-render/core. JSON becomes HTML emails.", "description": "React Email renderer for @json-render/core. JSON becomes HTML emails.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/react-native # @json-render/react-native
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/react-native", "name": "@json-render/react-native",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.", "description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/react-pdf # @json-render/react-pdf
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/react-pdf", "name": "@json-render/react-pdf",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "React PDF renderer for @json-render/core. JSON becomes PDF documents.", "description": "React PDF renderer for @json-render/core. JSON becomes PDF documents.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @internal/react-state # @internal/react-state
## 0.8.10
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.8.9 ## 0.8.9
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@internal/react-state", "name": "@internal/react-state",
"version": "0.8.9", "version": "0.8.10",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Shared React state context for json-render renderer packages", "description": "Shared React state context for json-render renderer packages",
+8
View File
@@ -1,5 +1,13 @@
# @json-render/react-three-fiber # @json-render/react-three-fiber
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/react-three-fiber", "name": "@json-render/react-three-fiber",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "React Three Fiber renderer for @json-render/core. JSON becomes 3D scenes.", "description": "React Three Fiber renderer for @json-render/core. JSON becomes 3D scenes.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/react # @json-render/react
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/react", "name": "@json-render/react",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "React renderer for @json-render/core. JSON becomes React components.", "description": "React renderer for @json-render/core. JSON becomes React components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/redux # @json-render/redux
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/redux", "name": "@json-render/redux",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Redux adapter for json-render StateStore", "description": "Redux adapter for json-render StateStore",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/remotion # @json-render/remotion
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/remotion", "name": "@json-render/remotion",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Remotion renderer for @json-render/core. JSON becomes video compositions.", "description": "Remotion renderer for @json-render/core. JSON becomes video compositions.",
"keywords": [ "keywords": [
+8
View File
@@ -1,5 +1,13 @@
# @json-render/shadcn # @json-render/shadcn
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
- @json-render/react@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/shadcn", "name": "@json-render/shadcn",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "shadcn/ui component library for @json-render/core. JSON becomes beautiful Tailwind-styled React components.", "description": "shadcn/ui component library for @json-render/core. JSON becomes beautiful Tailwind-styled React components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/solid # @json-render/solid
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/solid", "name": "@json-render/solid",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "SolidJS renderer for @json-render/core. JSON becomes Solid components.", "description": "SolidJS renderer for @json-render/core. JSON becomes Solid components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/svelte # @json-render/svelte
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/svelte", "name": "@json-render/svelte",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Svelte 5 renderer for @json-render/core. JSON becomes Svelte components.", "description": "Svelte 5 renderer for @json-render/core. JSON becomes Svelte components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/vue # @json-render/vue
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/vue", "name": "@json-render/vue",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Vue renderer for @json-render/core. JSON becomes Vue components.", "description": "Vue renderer for @json-render/core. JSON becomes Vue components.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/xstate # @json-render/xstate
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/xstate", "name": "@json-render/xstate",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "XState Store adapter for json-render StateStore", "description": "XState Store adapter for json-render StateStore",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/yaml # @json-render/yaml
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/yaml", "name": "@json-render/yaml",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "YAML wire format for @json-render/core. Progressive rendering and surgical edits via streaming YAML.", "description": "YAML wire format for @json-render/core. Progressive rendering and surgical edits via streaming YAML.",
"keywords": [ "keywords": [
+7
View File
@@ -1,5 +1,12 @@
# @json-render/zustand # @json-render/zustand
## 0.15.0
### Patch Changes
- Updated dependencies [bf3a7ec]
- @json-render/core@0.15.0
## 0.14.1 ## 0.14.1
### Patch Changes ### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@json-render/zustand", "name": "@json-render/zustand",
"version": "0.14.1", "version": "0.15.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Zustand adapter for json-render StateStore", "description": "Zustand adapter for json-render StateStore",
"keywords": [ "keywords": [