前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8655
  • 阿里发布Qwen-Image-2.1:7B开源图像生成编辑模型
  • Benchling 用 Bedrock AgentCore 为多租户 AI Agent 构建深度防御安全架构
  • 苹果Mac mini 2026款:M6/M5 Pro芯片,AI性能最高提升4倍
  • Mac Studio 2026:M5 Ultra 支持最高 512GB 统一内存,可本地运行超大模型
  • Grok 4.7登陆GitHub Copilot,面向Agent化编程
  • AI 安全是工程问题:Agent 堆栈每一层的防护实践
  • Agent 系统的瓶颈不是模型,是架构设计
  • 防御式 Agent 架构:Schema 注入与超时控制
  • 自研研究 Agent 拦截 AI 编程幻觉:文档先行策略
  • 像物理学家一样剪枝 LLM:区块移除的伊辛模型优化
  • Mac mini上的AI开发新范式:OpenClaw与Codex实战
  • Cloudflare Python Workers 正式上线,可用纯 Python 开发边缘应用
  • 浏览器直接给 ESP32 烧录 Claude 写的宏,无需 IDE 或工具链
  • Google 发布 Agent 安全风险报告:5 万美元循环消耗与凭证窃取案例
  • 多 Agent 合规流水线:用 RAG + 自修正架构对抗 WCAG 幻觉问题
  • Anthropic 发布金融领域 Claude 参考智能体套件
  • OpenAI 披露强化学习模型在上下文压缩时插入 Prompt 注入
  • Jev 决策模型实测:0.3秒完成意图分类和工具路由,$0.00004/次
  • Kimi Code Desktop 上线:图形界面 + 内置终端 + Git 状态,支持 Swarm 多 Agent 协
  • StepFun Step 5 Preview:600B 总参数 MoE 模型,1M 超长上下文,10 月开源
  • JSON-Render:通吃 React/Vue/Svelte/React Native 等 10+ 框架的生成式 UI
  • Agent-Native:让 Agent 能力同时暴露给 LLM 工具调用和 UI 操作的 TypeScript 框架
  • 清华联合无问芯穹开源具身智能体RPent,GPT-6 Astra注入机器人
  • AI Agent工具调用边界case:路由错误比想象中更脆弱
  • AI按它能读的契约编程,而非你想要的
  • MCP 远程服务器实现 AI 驱动的确定性 UI 编排
  • 阶跃Step 5 Preview实测:27B参数开源模型冲至Top2
  • 用Responses API构建有边界的GPT-6 Astra Agent
  • 用破坏不变量法审查 AI 生成代码
  • AI 编程测试冻结:保存异常指纹而非测试名
  • 程序员亲历:全公司用 Claude Code 批量生产代码,没人读代码,12 小时工作制
  • 自托管Llama部署成本全解析:GPU之外容易被忽视的隐性开销
  • Builder.io开源Agent-Native框架:让人与AI共享同一操作层
  • 让AI编程代理在中断后从断点恢复:session化执行轨迹方案
  • CI如何验证AI代理真的跑了测试而不是伪造结果
  • llm-keys-ui:让Coding Agent安全获取API密钥的插件
  • AI产品定价数学:固定费率如何悄悄亏损
  • ZCode事件:AI编程工具静默上传全部Git历史
  • Anthropic自评:R&D自动化率26%,但完全自主为0
  • 阿里Qwen-Image-2.1:70亿参数开源图像生成拳打闭源模型
  • OWASP CRS 规则引擎:给 LLM 和 MCP 加上 WAF
  • 智谱 MaaS 上线数据不留存机制,可申请开通
  • 如何界定可完成的AI工程范围
  • Cursor中直接查询技术文档的MCP方案
  • AI Agent的过度自信陷阱
  • Cursor与.NET周报:七个实战规则
  • AI Agent失效?模型可能不是根源
  • Qwen-Image-2.1开源:7B参数兼顾生图与编辑
  • 阿里开源 Qwen-Image-2.1:7B 参数兼顾透明图生成与多图编辑
  • 免费模型Trace不能支持的五个评估主张
  • AI重构前的副作用冻结术:先录 Ledger 再动格式
  • 已加载 51 / 8655
8.0
热点
AI SCORE
工具产品2026-09-21 14:30

JSON-Render:通吃 React/Vue/Svelte/React Native 等 10+ 框架的生成式 UI

GitHub Trending#Generative UI#React#开源
Editor brief · 编辑速览

通过预定义组件和 Action 约束 LLM 输出结构,解决纯 prompt 生成 UI 不可靠的问题;支持 Next.js 全栈、Remotion 视频、React Email 等多种渲染目标。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

生成式 UI 框架。

从自然语言提示生成动态、个性化的界面,同时不牺牲可靠性。预定义的组件和动作确保输出安全、可预测。

# for React
npm install @json-render/core @json-render/react
# for React with pre-built shadcn/ui components
npm install @json-render/shadcn
# or for React Native
npm install @json-render/core @json-render/react-native
# or for video
npm install @json-render/core @json-render/remotion
# or for PDF documents
npm install @json-render/core @json-render/react-pdf
# or for HTML email
npm install @json-render/core @json-render/react-email @react-email/components @react-email/render
# or for Vue
npm install @json-render/core @json-render/vue
# or for Svelte
npm install @json-render/core @json-render/svelte
# or for SolidJS
npm install @json-render/core @json-render/solid
# or for terminal UIs
npm install @json-render/core @json-render/ink ink react
# or for full Next.js apps (routes, layouts, SSR, metadata)
npm install @json-render/core @json-render/react @json-render/next
# or for 3D scenes (and gaussian splatting via the GaussianSplat component)
npm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three

json-render 是一个生成式 UI 框架:AI 从自然语言提示生成界面,但受限于你定义的组件。你设定护栏,AI 在其中生成:

  • 护栏约束 - AI 只能使用你组件目录中的组件
  • 可预测 - JSON 输出符合你的 schema,每次都一致
  • 快速 - 模型响应时流式逐步渲染
  • 跨平台 - 同一组件目录,支持 React、Vue、Svelte、Solid(Web)、React Native(移动端)
  • 自带电池 - 36 个预置 shadcn/ui 组件,开箱即用

1. 定义你的组件目录

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { z } from "zod";

const catalog = defineCatalog(schema, {
  components: {
    Card: {
      props: z.object({ title: z.string() }),
      description: "A card container",
    },
    Metric: {
      props: z.object({
        label: z.string(),
        value: z.string(),
        format: z.enum(["currency", "percent", "number"]).nullable(),
      }),
      description: "Display a metric value",
    },
    Button: {
      props: z.object({
        label: z.string(),
        action: z.string(),
      }),
      description: "Clickable button",
    },
  },
  actions: {
    export_report: { description: "Export dashboard to PDF" },
    refresh_data: { description: "Refresh all metrics" },
  },
});

2. 定义你的组件

import { defineRegistry, Renderer } from "@json-render/react";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) => (
      <div className="card">
        <h3>{props.title}</h3>
        {children}
      </div>
    ),
    Metric: ({ props }) => (
      <div className="metric">
        <span>{props.label}</span>
        <span>{format(props.value, props.format)}</span>
      </div>
    ),
    Button: ({ props, emit }) => (
      <button onClick={() => emit("press")}>{props.label}</button>
    ),
  },
});

3. 渲染 AI 生成的规格

function Dashboard({ spec }) {
  return <Renderer spec={spec} registry={registry} />;
}

就这样。AI 生成 JSON,你安全地渲染它。

import { defineRegistry, Renderer } from "@json-render/react";
import { schema } from "@json-render/react/schema";

// 扁平规格格式(根 key + 元素 map)
const spec = {
  root: "card-1",
  elements: {
    "card-1": {
      type: "Card",
      props: { title: "Hello" },
      children: ["button-1"],
    },
    "button-1": {
      type: "Button",
      props: { label: "Click me" },
      children: [],
    },
  },
};

// defineRegistry 创建一个类型安全的组件注册表
const { registry } = defineRegistry(catalog, { components });
<Renderer spec={spec} registry={registry} />;
import { h } from "vue";
import { defineRegistry, Renderer } from "@json-render/vue";
import { schema } from "@json-render/vue/schema";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) =>
      h("div", { class: "card" }, [h("h3", null, props.title), children]),
    Button: ({ props, emit }) =>
      h("button", { onClick: () => emit("press") }, props.label),
  },
});

// 在你的 Vue 组件模板中:
// <Renderer :spec="spec" :registry="registry" />
import { defineRegistry, Renderer } from "@json-render/svelte";
import { schema } from "@json-render/svelte/schema";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: ({ props, children }) => /* Svelte 5 snippet */,
    Button: ({ props, emit }) => /* Svelte 5 snippet */,
  },
});

// 在你的 Svelte 组件中:
// <Renderer spec={spec} registry={registry} />
import { defineRegistry, Renderer } from "@json-render/solid";
import { schema } from "@json-render/solid/schema";

const { registry } = defineRegistry(catalog, {
  components: {
    Card: (renderProps) => <div>{renderProps.children}</div>,
    Button: (renderProps) => (
      <button onClick={() => renderProps.emit("press")}>
        {renderProps.element.props.label as string}
      </button>
    ),
  },
});

<Renderer spec={spec} registry={registry} />;
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { defineRegistry, Renderer } from "@json-render/react";
import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
import { shadcnComponents } from "@json-render/shadcn";

// 从 36 个标准定义中挑选组件
const catalog = defineCatalog(schema, {
  components: {
    Card: shadcnComponentDefinitions.Card,
    Stack: shadcnComponentDefinitions.Stack,
    Heading: shadcnComponentDefinitions.Heading,
    Button: shadcnComponentDefinitions.Button,
  },
  actions: {},
});

// 使用匹配的实现
const { registry } = defineRegistry(catalog, {
  components: {
    Card: shadcnComponents.Card,
    Stack: shadcnComponents.Stack,
    Heading: shadcnComponents.Heading,
    Button: shadcnComponents.Button,
  },
});

<Renderer spec={spec} registry={registry} />;

React Native(移动端)

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react-native/schema";
import {
  standardComponentDefinitions,
  standardActionDefinitions,
} from "@json-render/react-native/catalog";
import { defineRegistry, Renderer } from "@json-render/react-native";

// 内置 25+ 个标准组件
const catalog = defineCatalog(schema, {
  components: { ...standardComponentDefinitions },
  actions: standardActionDefinitions,
});

const { registry } = defineRegistry(catalog, { components: {} });
<Renderer spec={spec} registry={registry} />;
import { Player } from "@remotion/player";
import {
  Renderer,
  schema,
  standardComponentDefinitions,
} from "@json-render/remotion";

// 时间线规格格式
const spec = {
  composition: {
    id: "video",
    fps: 30,
    width: 1920,
    height: 1080,
    durationInFrames: 300,
  },
  tracks: [{ id: "main", name: "Main", type: "video", enabled: true }],
  clips: [
    {
      id: "clip-1",
      trackId: "main",
      component: "TitleCard",
      props: { title: "Hello" },
      from: 0,
      durationInFrames: 90,
    },
  ],
  audio: { tracks: [] },
};

<Player
  component={Renderer}
  inputProps={{ spec }}
  durationInFrames={spec.composition.durationInFrames}
  fps={spec.composition.fps}
  compositionWidth={spec.composition.width}
  compositionHeight={spec.composition.height}
/>;

React PDF(文档)

import { renderToBuffer } from "@json-render/react-pdf";

const spec = {
  root: "doc",
  elements: {
    doc: {
      type: "Document",
      props: { title: "Invoice" },
      children: ["page-1"],
    },
    "page-1": {
      type: "Page",
      props: { size: "A4" },
      children: ["heading-1", "table-1"],
    },
    "heading-1": {
      type: "Heading",
      props: { text: "Invoice #1234", level: "h1" },
      children: [],
    },
    "table-1": {
      type: "Table",
      props: {
        columns: [
          { header: "Item", width: "60%" },
          { header: "Price", width: "40%", align: "right" },
        ],
        rows: [
          ["Widget A", "$10.00"],
          ["Widget B", "$25.00"],
        ],
      },
      children: [],
    },
  },
};

// 渲染为 buffer、stream 或文件
const buffer = await renderToBuffer(spec);
import { renderToHtml } from "@json-render/react-email";
import { schema, standardComponentDefinitions } from "@json-render/react-email";
import { defineCatalog } from "@json-render/core";

const catalog = defineCatalog(schema, {
  components: standardComponentDefinitions,
});

```javascript
const spec = {
  root: "html-1",
  elements: {
    "html-1": {
      type: "Html",
      props: { lang: "en", dir: "ltr" },
      children: ["head-1", "body-1"],
    },
    "head-1": { type: "Head", props: {}, children: [] },
    "body-1": {
      type: "Body",
      props: { style: { backgroundColor: "#f6f9fc" } },
      children: ["container-1"],
    },
    "container-1": {
      type: "Container",
      props: {
        style: { maxWidth: "600px", margin: "0 auto", padding: "20px" },
      },
      children: ["heading-1", "text-1"],
    },
    "heading-1": { type: "Heading", props: { text: "Welcome" }, children: [] },
    "text-1": {
      type: "Text",
      props: { text: "Thanks for signing up." },
      children: [],
    },
  },
};

const html = await renderToHtml(spec);
import { renderToPng } from "@json-render/image/render";

const spec = {
  root: "frame",
  elements: {
    frame: {
      type: "Frame",
      props: { width: 1200, height: 630, backgroundColor: "#1a1a2e" },
      children: ["heading"],
    },
    heading: {
      type: "Heading",
      props: { text: "Hello World", level: "h1", color: "#ffffff" },
      children: [],
    },
  },
};

// Render to PNG (requires @resvg/resvg-js)
const png = await renderToPng(spec, { fonts });

// Or render to SVG string
import { renderToSvg } from "@json-render/image/render";
const svg = await renderToSvg(spec, { fonts });
import { defineCatalog } from "@json-render/core";
import { schema, defineRegistry } from "@json-render/react";
import {
  threeComponentDefinitions,
  threeComponents,
  ThreeCanvas,
} from "@json-render/react-three-fiber";

const catalog = defineCatalog(schema, {
  components: {
    Box: threeComponentDefinitions.Box,
    Sphere: threeComponentDefinitions.Sphere,
    AmbientLight: threeComponentDefinitions.AmbientLight,
    DirectionalLight: threeComponentDefinitions.DirectionalLight,
    GaussianSplat: threeComponentDefinitions.GaussianSplat,
    OrbitControls: threeComponentDefinitions.OrbitControls,
  },
  actions: {},
});

const { registry } = defineRegistry(catalog, {
  components: {
    Box: threeComponents.Box,
    Sphere: threeComponents.Sphere,
    AmbientLight: threeComponents.AmbientLight,
    DirectionalLight: threeComponents.DirectionalLight,
    GaussianSplat: threeComponents.GaussianSplat,
    OrbitControls: threeComponents.OrbitControls,
  },
});

<ThreeCanvas
  spec={spec}
  registry={registry}
  shadows
  camera={{ position: [5, 5, 5], fov: 50 }}
  style={{ width: "100%", height: "100vh" }}
/>;
import type { NextAppSpec } from "@json-render/next";
import { createNextApp } from "@json-render/next/server";
import { NextAppProvider } from "@json-render/next";

const spec: NextAppSpec = {
  metadata: { title: { default: "My App", template: "%s | My App" } },
  layouts: {
    main: {
      root: "shell",
      elements: {
        shell: { type: "Container", props: {}, children: ["nav", "slot"] },
        nav: { type: "NavBar", props: {}, children: [] },
        slot: { type: "Slot", props: {}, children: [] },
      },
    },
  },
  routes: {
    "/": {
      layout: "main",
      metadata: { title: "Home" },
      page: {
        root: "hero",
        elements: {
          hero: { type: "Card", props: { title: "Welcome" }, children: [] },
        },
      },
    },
  },
};

// Server: creates Page, generateMetadata, generateStaticParams
const app = createNextApp({ spec });

// Client: wrap your layout with NextAppProvider
// <NextAppProvider registry={registry} handlers={handlers}>
//   {children}
// </NextAppProvider>

TanStack Start(完整应用)

import { createFileRoute, notFound } from "@tanstack/react-router";
import {
  PageRenderer,
  StartErrorBoundary,
  StartLoading,
  StartNotFound,
  type StartAppSpec,
} from "@json-render/tanstack-start";
import { createStartApp } from "@json-render/tanstack-start/server";

const spec: StartAppSpec = {
  metadata: { title: { default: "My App", template: "%s | My App" } },
  routes: {
    "/": {
      metadata: { title: "Home" },
      page: {
        root: "hero",
        elements: {
          hero: { type: "Card", props: { title: "Welcome" }, children: [] },
        },
      },
    },
  },
};

const { getPageData, getHead } = createStartApp({ spec });

export const Route = createFileRoute("/$")({
  loader: async ({ location }) => {
    const data = await getPageData({ pathname: location.pathname });
    if (!data) throw notFound();
    return data;
  },
  head: ({ match }) => getHead({ pathname: match.pathname }),
  component: () => <PageRenderer {...Route.useLoaderData()} />,
  pendingComponent: StartLoading,
  errorComponent: StartErrorBoundary,
  notFoundComponent: StartNotFound,
});

使用 <StartAppProvider spec={spec}> 包裹根路由的 outlet,以便路由兜底组件能够解析当前路由。通过其 functions prop 传递命名的 $computed 实现。

shadcn-svelte(Svelte)

import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/svelte/schema";
import { defineRegistry, Renderer } from "@json-render/svelte";
import { shadcnComponentDefinitions } from "@json-render/shadcn-svelte/catalog";
import { shadcnComponents } from "@json-render/shadcn-svelte";

const catalog = defineCatalog(schema, {
  components: {
    Card: shadcnComponentDefinitions.Card,
    Stack: shadcnComponentDefinitions.Stack,
    Heading: shadcnComponentDefinitions.Heading,
    Button: shadcnComponentDefinitions.Button,
  },
  actions: {},
});

const { registry } = defineRegistry(catalog, {
  components: {
    Card: shadcnComponents.Card,
    Stack: shadcnComponents.Stack,
    Heading: shadcnComponents.Heading,
    Button: shadcnComponents.Button,
  },
});

// In your Svelte component:
// <Renderer spec={spec} registry={registry} />

适用于任何 json-render 应用的嵌入式检查器面板。包含 Spec 树、状态编辑器、操作日志、流日志、目录浏览器和 DOM 选择器。

// React
import { JsonRenderDevtools } from "@json-render/devtools-react";

<JSONUIProvider registry={registry} handlers={handlers}>
  <Renderer spec={spec} registry={registry} />
  <JsonRenderDevtools spec={spec} catalog={catalog} messages={messages} />
</JSONUIProvider>;

浮动开关出现在右下角。快捷键:Ctrl/Cmd + Shift + J。在生产环境会摇树优化掉,不产生任何代码。

支持 React、Vue、Svelte 和 Solid — 将 @json-render/devtools-react 替换为适配你渲染器的对应包即可。

import { defineCatalog } from "@json-render/core";
import {
  schema,
  standardComponentDefinitions,
  standardActionDefinitions,
  defineRegistry,
  Renderer,
  JSONUIProvider,
} from "@json-render/ink";

const catalog = defineCatalog(schema, {
  components: { ...standardComponentDefinitions },
  actions: standardActionDefinitions,
});

const { registry } = defineRegistry(catalog, { components: {} });

const spec = {
  root: "card-1",
  elements: {
    "card-1": {
      type: "Card",
      props: { title: "Status" },
      children: ["status-1"],
    },
    "status-1": {
      type: "StatusLine",
      props: { label: "Build", status: "success" },
      children: [],
    },
  },
};

<JSONUIProvider initialState={{}}>
  <Renderer spec={spec} registry={registry} />
</JSONUIProvider>;

流式渲染(SpecStream)

流式处理 AI 响应:

import { createSpecStreamCompiler } from "@json-render/core";

const compiler = createSpecStreamCompiler<MySpec>();

// Process chunks as they arrive
const { result, newPatches } = compiler.push(chunk);
setSpec(result); // Update UI with partial result

// Get final result
const finalSpec = compiler.getResult();

从目录生成系统提示词:

const systemPrompt = catalog.prompt();
// Includes component descriptions, props schemas, available actions

条件可见性

{
  "type": "Alert",
  "props": { "message": "Error occurred" },
  "visible": [
    { "$state": "/form/hasError" },
    { "$state": "/form/errorDismissed", "not": true }
  ]
}

任何 prop 值都可以通过表达式由数据驱动:

{
  "type": "Icon",
  "props": {
    "name": {
      "$cond": { "$state": "/activeTab", "eq": "home" },
      "$then": "home",
      "$else": "home-outline"
    },
    "color": {
      "$cond": { "$state": "/activeTab", "eq": "home" },
      "$then": "#007AFF",
      "$else": "#8E8E93"
    }
  }
}

{ "$state": "/state/key" } — 从状态模型中读取值

{ "$cond": <condition>, "$then": <value>, "$else": <value> } — 计算条件并选择分支

{ "$template": "Hello, ${/user/name}!" } — 将状态值插值到字符串中

{ "$computed": "fn", "args": { ... } } — 使用已解析的参数调用注册的函数

组件可以触发操作,包括内置的 setState 操作:

{
  "type": "Pressable",
  "props": {
    "action": "setState",
    "actionParams": { "statePath": "/activeTab", "value": "home" }
  },
  "children": ["home-icon"]
}

setState action 直接更新状态模型,进而重新计算可见性条件和动态属性表达式。

通过触发 actions 来响应状态变化:

{
  "type": "Select",
  "props": {
    "value": { "$bindState": "/form/country" },
    "options": ["US", "Canada", "UK"]
  },
  "watch": {
    "/form/country": {
      "action": "loadCities",
      "params": { "country": { "$state": "/form/country" } }
    }
  }
}

watch 是元素顶层级字段(与 type/props/children 同级)。监听器在监听值发生变化时触发,而非在初始渲染时触发。

git clone https://github.com/vercel-labs/json-render
cd json-render
pnpm install
pnpm dev

http://json-render.localhost:1355 - 文档与 Playground

http://dashboard-demo.json-render.localhost:1355 - 示例 Dashboard

http://react-email-demo.json-render.localhost:1355 - React Email 示例

http://remotion-demo.json-render.localhost:1355 - Remotion 视频示例

Chat 示例:在 examples/chat 目录下运行 pnpm dev

实验性 Jev 组合:使用 experimental_composeSpec 和 experimental_createEvaluator 从 core 模块配合你自己的 catalog,或者在 /playground 中选择 Jev (Experimental)。未发布版本;构建指南见 guide。

Svelte 示例:在 examples/svelte 或 examples/svelte-chat 目录下运行 pnpm dev

Vue 示例:在 examples/vue 目录下运行 pnpm dev

Vite Renderers(React + Vue + Svelte + Solid):在 examples/vite-renderers 目录下运行 pnpm dev

React Native 示例:在 examples/react-native 目录下运行 npx expo start

Gaussian Splatting(R3F):在 examples/react-three-fiber-gsplat 目录下运行 pnpm dev

Gaussian Splatting(实验性独立 gsplat.js demo):在 examples/gsplat 目录下运行 pnpm dev

flowchart LR
    A[User Prompt] --> B[AI + Catalog]
    B --> C[JSON Spec]
    C --> D[Renderer]

B -.- E([guardrailed])
    C -.- F([predictable])
    D -.- G([streamed])

定义护栏——即 AI 智能体可使用的组件、actions 和数据绑定

用自然语言描述你想要的 Prompt

AI 生成 JSON——输出始终可预测,被约束在你的 catalog 内

快速渲染——在模型响应的同时逐步流式渲染

Original source

本文由 AI 翻译整理自 GitHub Trending,原文版权归原作者所有。

阅读英文原文
上一篇
StepFun Step 5 Preview:600B 总参数 MoE 模型,1M 超长上下文,10 月开源
下一篇
Agent-Native:让 Agent 能力同时暴露给 LLM 工具调用和 UI 操作的 TypeScript 框架