mcpField
M
multi-surface-render

multi-surface-render

github.com/yonatangross开发29991 次安装今天更新
代码仓库

关于 multi-surface-render

这是一份 json-render 使用指南,介绍如何定义一套目录和界面规范,并将其渲染到网页、移动端、终端、文档、邮件、图片、视频等平台。适合需要用共享界面数据生成多种输出的开发者。

主要功能

  1. 01共享目录与界面规范
  2. 02按平台配置渲染器注册表
  3. 03支持 React、Vue、Svelte 和移动端
  4. 04输出 PDF、邮件、图片和视频
  5. 05支持终端与 Next.js 渲染
  6. 06提供渲染目标选择指引

适用场景

  1. 01将同一界面规范用于网页和 PDF
  2. 02根据共享界面数据生成 HTML 邮件
  3. 03生成社交图片或演示视频
  4. 04根据规范渲染命令行界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yonatangross/orchestkit --skill multi-surface-render

安装后在 Claude Code 中输入/multi-surface-render确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

test-cases.json
references/
ork-delta.md
upstream-email.md
upstream-image.md
upstream-pdf.md
upstream-remotion.md
rules/
_sections.md
_template.md
pdf-email-renderer.md
react-renderer.md
registry-mapping.md
target-selection.md
video-image-renderer.md

详细介绍

上游版本参考:@json-render/core 0.21.0(2026-09-30)。

使用 json-render 进行多端渲染

定义一次,随处渲染。单个 json-render 目录和规范即可生成 React Web UI、PDF 报告、HTML 邮件、Remotion 演示视频和 OG 图片——每个端都有自己的注册表,将目录类型映射到平台原生组件。

快速参考

类别规则影响使用场景
目标选择1高为你的用例选择渲染器
React 渲染器1中Web 应用、SPA、仪表板
PDF 和邮件渲染器1高报告、文档、通知
视频和图片渲染器1中演示视频、OG 图片、社交卡片
注册表映射1高平台特定的组件实现

共计:5 个类别中的 5 条规则

多端渲染的工作方式

  1. 一个目录 — 跨所有端共享的 Zod 类型组件定义
  2. 一个规范 — 描述 UI 结构的扁平树 JSON/YAML
  3. 多个注册表 — 每个端都将目录类型映射到各自的组件实现
  4. 多个渲染器 — 每个包都使用自己的注册表渲染规范

目录是契约。规范是数据。注册表是平台特定的实现。

快速开始 — 同一目录,不同渲染器

共享目录(所有端共用)
import { defineCatalog } from '@json-render/core'
import { schema } from '@json-render/react/schema'
import { z } from 'zod'

export const catalog = defineCatalog(schema, {
  components: {
    Heading: {
      props: z.object({
        text: z.string(),
        level: z.enum(['h1', 'h2', 'h3']),
      }),
      children: false,
    },
    Paragraph: {
      props: z.object({ text: z.string() }),
      children: false,
    },
    StatCard: {
      props: z.object({
        label: z.string(),
        value: z.string(),
        trend: z.enum(['up', 'down', 'flat']).optional(),
      }),
      children: false,
    },
  },
})
渲染为 Web(React)
import { Renderer } from '@json-render/react'
import { webRegistry } from './registries/web'

// webRegistry comes from `defineRegistry(catalog, { components })`.
// RendererProps is { spec, registry, loading?, fallback? } — no catalog prop.
export const Dashboard = ({ spec }) => (
  <Renderer spec={spec} registry={webRegistry} />
)
渲染为 PDF
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { pdfRegistry } from './registries/pdf'

// Buffer for HTTP response. PDF options are { registry?, state?, handlers? }.
// includeStandard is an EMAIL option, not a PDF one (see references/upstream-pdf.md).
const buffer = await renderToBuffer(spec, { registry: pdfRegistry })

// Direct file output — renderToFile(spec, filePath, options?)
await renderToFile(spec, './output/report.pdf', { registry: pdfRegistry })
渲染为邮件
import { renderToHtml } from '@json-render/react-email'
import { emailRegistry } from './registries/email'

const html = await renderToHtml(spec, { registry: emailRegistry })
await sendEmail({ to: user.email, subject: 'Weekly Report', html })
渲染为 OG 图片(Satori)
import { renderToSvg, renderToPng } from '@json-render/image'
import { imageRegistry } from './registries/image'

const png = await renderToPng(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
渲染为视频(Remotion)
// Verified 2026-07-31 against @json-render/[email protected]: the export is
// `Renderer` and its props are { spec, components }. fps and durationInFrames
// belong on Remotion's own Composition, not on this renderer.
import { Renderer } from '@json-render/remotion'
import { remotionComponents } from './registries/remotion'

export const DemoVideo = () => (
  <Renderer spec={spec} components={remotionComponents} />
)
渲染到终端(Ink,0.15+)
import { render } from 'ink'
import { Renderer } from '@json-render/ink'
import { catalog } from './catalog'
import { inkRegistry } from './registries/ink'

render(<Renderer spec={spec} catalog={catalog} registry={inkRegistry} />)

适用于 /ork:* 命令行仪表板和流式 Agent 聊天界面——内置 20 多个 Ink 原生组件(Box、Text、Spinner、Table、Markdown、Progress 等)。

渲染为 Next.js 应用(0.16+)
// createNextApp lives on the /server subpath, not the package root.
import { createNextApp } from '@json-render/next/server'

const { getPageData, generateMetadata, generateStaticParams } = createNextApp({
  spec,                        // NextAppSpec: routes keyed by Next.js URL patterns
  loaders: { getPost },        // server-side data loaders referenced by route.loader
})

它不会在磁盘上搭建项目。createNextApp 会返回服务端组件,你可以从全捕获路由重新导出这些组件,而页面本身通过 PageRenderer 渲染:

// app/[[...slug]]/page.tsx
export { generateMetadata, generateStaticParams }

export default async function Page({ params }) {
  const data = await getPageData(params)
  if (!data) notFound()
  return <PageRenderer {...data} registry={webRegistry} />
}

规范描述的是路由树(页面、布局、元数据、加载和错误状态),而不只是组件树。

决策矩阵 — 何时使用各目标

目标包使用场景输出
React@json-render/reactWeb 应用、SPAJSX
Next.js@json-render/next (0.16+)完整应用:路由、布局、SSR、元数据Next.js 应用
Vue@json-render/vueVue 项目Vue 组件
Svelte@json-render/svelteSvelte 项目Svelte 组件
Svelte+shadcn@json-render/shadcn-svelte (0.16+)包含 36 个组件的 Svelte 5 目录Svelte + Tailwind
React Native@json-render/react-native移动应用(25 个以上组件)原生视图
终端@json-render/ink (0.15+)CLI UI、TUI、流式聊天Ink(终端)
PDF@json-render/react-pdf报告、文档PDF 缓冲区/文件
邮件@json-render/react-email通知、摘要HTML 字符串
Remotion@json-render/remotion演示视频、营销MP4/WebM
图像@json-render/imageOG 图像、社交卡片SVG/PNG(Satori)
YAML@json-render/yaml (0.14+)Token 优化、流式解析器YAML 字符串
MCP@json-render/mcpClaude/Cursor/ChatGPT 对话沙盒 iframe
3D@json-render/react-three-fiber3D 场景(19 个组件,已于 2026-07-31 验证;组件列表位于上游)Three.js 画布
代码生成@json-render/codegen根据规范生成源代码TypeScript/JSX

所有 @json-render/* 渲染器均已针对 0.20.0(@json-render/core)验证。 从 0.19.0 到 0.20.0,导出内容从 84 个符号增加到 87 个,没有移除任何符号,因此此处记录的每个 API 仍可正常解析。0.20.0 新增了命名插槽(UIElement 上的 slots?: Record<string, string[]>,目录通过 slots: ["default", "header", ...] 声明),通过相对于条目的 repeat.statePath(格式为 {"$item": "employees"})实现嵌套重复,以及条目范围内的可见性, 因此在同一元素上组合 repeat 和 visible: {"$item": ...} 会筛选条目,而不会导致失败。有一项 破坏性变更,本技能中的规则均未使用:ActionExecutionContext.executeAction 现在接收 ActionBinding,而非裸动作名称,这只会影响手写的自定义渲染器桥接。

加载 rules/target-selection.md,了解详细的选择标准和权衡。

上游覆盖范围(无需重述)

此技能封装了 @json-render/*。供应商文档通过获取引入,而非在此重复。此处保留的内容 是本项目的增补:references/ork-delta.md 以及五条规则。

主题来源
完整的渲染器签名和选项对象(renderToBuffer / renderToFile / renderToStream、renderToHtml / renderToPlainText、renderToSvg / renderToPng、Remotion 导出项)references/upstream-pdf.md、upstream-email.md、upstream-image.md、upstream-remotion.md(原样引入;使用 bash scripts/sync-vercel-skills.sh 重新同步)
各目标的标准组件列表(Document、Page、Table、电子邮件的 Section / Row / Column、Remotion 转场和效果)同样的四个引入的 references/upstream-*.md 文件
<Renderer> 属性、defineRegistry、useUIStreamhttps://github.com/vercel-labs/json-render/tree/main/packages/react。0.19 属性形状修正(没有 catalog 属性,也没有顶层 onError)是本项目的发现,保留在 rules/react-renderer.md 中
电子邮件客户端限制:600px 容器、表格布局、内联样式、绝对图片 URLreferences/upstream-email.md(“Email Best Practices”)
Satori CSS 支持矩阵https://github.com/vercel/satori。此技能设计图片注册表时采用的可用子集保留在 rules/video-image-renderer.md 中
react-pdf 样式属性支持(flexbox 集合,不支持 grid)https://react-pdf.org/styling
Remotion 渲染成本和云端渲染https://www.remotion.dev/docs/lambda
各软件包的功能与输出矩阵本项目的目标选择保留在上方的决策矩阵和 rules/target-selection.md 中;各软件包详情见 https://github.com/vercel-labs/json-render

编写渲染器代码前,请阅读 references/ork-delta.md:其中包含 API 漂移规则、Remotion 和 PDF 延迟预算,以及 PDF / React Native 注册表的布局上限。

PDF 渲染器 — 报告和文档

@json-render/react-pdf 软件包在底层使用 react-pdf 将规格渲染为 PDF。提供三种输出模式:缓冲区、文件和流。

import { renderToBuffer, renderToFile, renderToStream } from '@json-render/react-pdf'

// In-memory buffer (for HTTP responses, S3 upload)
// PDF options are { registry?, state?, handlers? }, no catalog field
const buffer = await renderToBuffer(spec, { registry: pdfRegistry })
res.setHeader('Content-Type', 'application/pdf')
res.send(buffer)

// Direct file write — renderToFile(spec, filePath, options?)
await renderToFile(spec, './output/report.pdf', { registry: pdfRegistry })

// Streaming (for large documents)
const stream = await renderToStream(spec, { registry: pdfRegistry })
stream.pipe(res)

加载 rules/pdf-email-renderer.md,了解 PDF 注册表模式和电子邮件渲染。

图片渲染器 — OG 图片和社交卡片

@json-render/image 软件包使用 Satori 将规格转换为 SVG,然后可选择转换为 PNG。专为在服务端生成社交媒体图片而设计。

import { renderToSvg, renderToPng } from '@json-render/image'

// SVG output (smaller, scalable)
const svg = await renderToSvg(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

// PNG output (universal compatibility)
const png = await renderToPng(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

加载 rules/video-image-renderer.md,了解 Satori 限制和 Remotion 合成模式。

注册表映射 — 同一目录,不同平台组件

每个界面都需要自己的注册表。注册表将目录类型映射到特定平台的组件实现,同时目录和规格保持一致。

// Web registry — uses HTML elements
const webRegistry = {
  Heading: ({ text, level }) => {
    const Tag = level // h1, h2, h3
    return <Tag className="font-bold">{text}</Tag>
  },
  StatCard: ({ label, value, trend }) => (
    <div className="rounded border p-4">
      <span className="text-sm text-gray-500">{label}</span>
      <strong className="text-2xl">{value}</strong>
    </div>
  ),
}

// PDF registry — uses react-pdf primitives
import { Text, View } from '@react-pdf/renderer'
const pdfRegistry = {
  Heading: ({ text, level }) => (
    <Text style={{ fontSize: level === 'h1' ? 24 : level === 'h2' ? 18 : 14 }}>
      {text}
    </Text>
  ),
  StatCard: ({ label, value }) => (
    <View style={{ border: '1pt solid #ccc', padding: 8 }}>
      <Text style={{ fontSize: 10, color: '#666' }}>{label}</Text>
      <Text style={{ fontSize: 18, fontWeight: 'bold' }}>{value}</Text>
    </View>
  ),
}

加载 rules/registry-mapping.md,了解注册表创建模式和类型安全。

规则详情

目标选择

选择合适渲染器目标的决策标准。

规则文件关键模式
目标选择rules/target-selection.md用例映射、输出格式限制
React 渲染器

使用 <Renderer> 组件进行 Web 渲染。

规则文件关键模式
React 渲染器rules/react-renderer.md<Renderer> 组件、流式传输、错误边界
PDF 和电子邮件渲染器

在服务端渲染为 PDF 缓冲区 / 文件和 HTML 电子邮件字符串。

规则文件关键模式
PDF 和电子邮件rules/pdf-email-renderer.mdrenderToBuffer、renderToFile、renderToHtml
视频和图片渲染器

Remotion 合成和 Satori 图片生成。

规则文件关键模式
视频和图片rules/video-image-renderer.md渲染器(Remotion)、renderToPng、renderToSvg
Registry Mapping

为共享目录创建平台专属注册表。

RuleFileKey Pattern
注册表映射rules/registry-mapping.md每个平台的注册表、类型安全映射

关键决策

DecisionRecommendation
PDF 库使用 @json-render/react-pdf(react-pdf),而不是 Puppeteer 截图
邮件渲染使用 @json-render/react-email(react-email),而不是 MJML 或自定义 HTML
OG 图片使用 @json-render/image(Satori),而不是 Puppeteer 或 canvas
视频使用 @json-render/remotion(Remotion),而不是 FFmpeg 脚本
每个平台一个注册表始终使用独立的注册表;切勿为所有界面共用一个注册表
目录共享通过导入,在所有注册表之间共享同一个目录定义

常见错误

  1. 为每个界面构建单独的组件树——这违背了初衷;应共享目录和规范
  2. 使用 Puppeteer 截取 React 页面来生成 PDF——速度慢且脆弱;应使用原生 react-pdf 渲染
  3. 使用一个庞大的注册表覆盖所有平台——这不可行,因为 PDF 使用 <View>/<Text>,而 Web 使用 <div>/<span>
  4. 忽略 Satori 的限制——不支持 CSS grid,flexbox 支持有限;设计图片注册表时应考虑这些限制
  5. 为每个界面重复定义目录——一个目录,多个注册表;目录就是契约

相关技能

  • ork:json-render-catalog — 使用 Zod 和 shadcn 组件定义目录的模式
  • ork:demo-producer — 使用 Remotion 的视频制作流水线
  • ork:mcp-visual-output — 在 Claude/Cursor 中通过 MCP 渲染规范

评论

支持 Markdown