- 人工智能
- AI 应用
- 前端
- MCP 服务
【免费下载链接】json-render
The Generative UI framework
导读
@json-render/devtools-solid是 json-render 生成式 UI 框架(Generative UI framework)为 SolidJS 提供的 DevTools 适配器,一个开箱即用的<JsonRenderDevtools />组件。本文从安装、最小接入、全部 Props 参考讲到面板的 Spec / State / Actions / Stream / Catalog 五大标签页与 DOM 拾取器(Picker)的实际用法,并深入packages/devtools-solid/src/index.tsx等源码,说明它如何在 SolidJS 的细粒度响应式体系下完成事件采集、状态联调与生产构建零开销。读完你可以在自己的 SolidJS 应用中快速集成、调试 AI 流式生成的 UI,并按需调整停靠位置、快捷键与内存缓冲。
一、背景:框架无关的 DevTools 核心与适配器分层
json-render 的 DevTools 采用「框架无关核心 + 各框架薄适配器」的两层设计:
@json-render/devtools(packages/devtools/README.md)是纯 TypeScript 实现的框架无关核心,提供 Shadow DOM 隔离的面板 UI、事件存储(event store)、DOM 拾取器与流式 tap 工具。它自身不依赖任何 UI 框架,绝大多数用户不会直接 import 它。@json-render/devtools-solid则是 SolidJS 适配器,把核心能力封装成一个null渲染、零 UI 输出的挂载型组件,你只需放进组件树即可。
同层的适配器还包括@json-render/devtools-react、@json-render/devtools-vue、@json-render/devtools-svelte。从packages/devtools-solid/package.json可以看到,该包依赖@json-render/core、@json-render/devtools、@json-render/solid三个 workspace 包,说明它在运行时同时桥接「核心面板逻辑」与「Solid 渲染器的状态/动作上下文」。
二、安装与前提
npm install @json-render/devtools @json-render/devtools-solid- Peer dependency:
solid-js@^1.9。安装时若版本不匹配,包管理器会给出 peer 冲突提示,请确保项目中的solid-js不低于 1.9.0(package.json 中声明为solid-js: ^1.9.0)。 @json-render/devtools会被@json-render/devtools-solid自动引入,显式安装是为了让 npm 正确解析依赖树,同时你也能直接使用其暴露的底层 API(如服务端流式 tap)。- 使用时还需要
@json-render/solid(渲染器)与@json-render/core(目录、Registry、状态模型)。Solid 渲染器的完整安装方式见 packages/solid/README.md:npm install @json-render/core @json-render/solid zod,其中solid-js ^1.9.0与zod ^4.0.0为 peer 依赖。
说明:
zod用于为 Catalog 中的组件 props 与 actions 定义 schema,是定义 Catalog 的必要依赖。
三、最小接入:三步把 DevTools 放进 Solid 应用
1. 定义 Catalog(组件与动作清单)
// catalog.ts import { defineCatalog } from "@json-render/core"; import { schema } from "@json-render/solid/schema"; import { z } from "zod"; export const catalog = defineCatalog(schema, { components: { Card: { props: z.object({ title: z.string(), description: z.string().nullable(), }), description: "A card container", }, Button: { props: z.object({ label: z.string(), action: z.string(), }), description: "A clickable button", }, }, actions: { submit: { description: "Submit the form" }, cancel: { description: "Cancel and close" }, }, });2. 创建 Registry(组件实现)
// registry.tsx import { defineRegistry } from "@json-render/solid"; import { catalog } from "./catalog"; export const { registry } = defineRegistry(catalog, { components: { Card: (renderProps) => ( <div class="card"> <h3>{renderProps.element.props.title as string}</h3> {renderProps.children} </div> ), Button: (renderProps) => ( <button onClick={() => renderProps.emit("press")}> {renderProps.element.props.label as string} </button> ), }, actions: { submit: async () => {}, cancel: async () => {}, }, });3. 在 JSONUIProvider 内放置<JsonRenderDevtools />
import { JsonRenderDevtools } from "@json-render/devtools-solid"; import { JSONUIProvider, Renderer } from "@json-render/solid"; import { registry } from "./registry"; import { catalog } from "./catalog"; export function App(props: { spec: () => any }) { return ( <JSONUIProvider registry={registry}> <Renderer spec={props.spec()} registry={registry} /> <JsonRenderDevtools spec={props.spec()} catalog={catalog} messages={messages()} // AI SDK 消息数组,用于 Stream 标签页 /> </JSONUIProvider> ); }组件放置位置没有限制(只要是JSONUIProvider内即可),它本身不渲染任何 DOM。JSONUIProvider是 Solid 渲染器提供的合并 Provider,内部串联了StateProvider、VisibilityProvider、ValidationProvider与ActionProvider(见 packages/solid/README.md)。
默认行为
- 浮动开关:出现在页面右下角,点击展开/收起面板。
- 快捷键:
Ctrl/Cmd+Shift+J切换面板开关。 - 生产构建零成本:组件在 production 下 tree-shake 为
null,不会被打包进产物。
四、Props 完整参考(与源码逐一对应)
以下 Props 定义直接来自 packages/devtools-solid/src/index.tsx 的JsonRenderDevtoolsProps接口:
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
spec | Spec \| null | — | 当前要检查的 UI Spec。多渲染器场景下由面板内部按需读取最新值 |
catalog | Catalog \| null | — | 组件/动作目录,用于 Catalog 标签页与拾取器解析 |
messages | ChatLikeMessage[] | — | AI SDK 风格的流式消息数组(parts结构),用于 Stream 标签页采集补丁事件 |
initialOpen | boolean | false | 面板初始是否展开 |
position | PanelPosition | "bottom-right" | 面板停靠与开关位置,见下文「布局与停靠」 |
hotkey | string \| false | "mod+shift+j" | 切换面板的快捷键;传false关闭快捷键 |
bufferSize | number | 500 | 事件存储(ring buffer)最大保留事件数,超出丢弃最旧事件 |
reserveSpace | boolean | true | 面板展开时是否通过<body>padding 为面板预留空间 |
allowDockToggle | boolean | true | 是否显示工具栏按钮,允许用户切换底部/右侧停靠(选择持久化到 localStorage) |
onEvent | (evt: DevtoolsEvent) => void | — | 事件回调:每当有新事件入队时触发,参数为最新一条事件 |
messages的类型为宽松的聊天消息结构(parts?: Array<{ type, text?, data? }>),与@json-render/core的 Spec、Catalog 类型对齐。若你的消息对象结构略有差异,只要parts字段兼容即可被scanMessageParts处理。
五、面板功能:五大标签页与拾取器
组件挂载时通过createPanel注册以下标签页(源码第 137 行:tabs: [specTab(), stateTab(), actionsTab(), streamTab(), catalogTab()]),面板 UI 由@json-render/devtools用原生 DOM + Shadow DOM 隔离实现:
- Spec(规范树):以树形展示当前 Spec 的元素结构(
root、elements、state),支持在多个生成之间切换。多渲染器宿主(如每条助手消息产生一个 Spec 的聊天应用)可通过getSpecs提供每代 Spec 列表,Spec 标签页会显示生成切换器(见 packages/devtools/src/panel/types.ts 的SpecEntry)。 - State(状态):实时查看/修改渲染器状态。面板通过适配器适配的
StateStore读取getSnapshot,状态以 JSON Pointer 路径扁平化展示。 - Actions(动作):展示动作分发生命周期——dispatch 与 settle 成对出现,含执行耗时(
durationMs)与结果/错误。 - Stream(流):采集 AI 流式消息中的 UI 补丁事件,观察 Spec 如何逐步生成。
- Catalog(目录):展示当前注册的组件与动作清单及其 schema 说明。
另外还有拾取器(Picker):点击页面上任意由 json-render 渲染的元素,通过data-jr-key属性反查其在 Spec 中的 key,并联动高亮 Spec 树中的对应节点。拾取器与 Spec 标签页通过createSelectionBus共享选中项(选中后调用highlightElement高亮 DOM),其 DOM 几何计算逻辑(含display: contents包裹元素的边界回退计算)见 packages/devtools/src/picker.ts。
六、运行机制与源码原理
1. 生产环境守卫:isProduction()短路
组件函数体第一行即if (isProduction()) return null;。isProduction实现于 packages/devtools/src/prod-guard.ts:当process.env.NODE_ENV === "production"时返回true,且用typeof process !== "undefined"守卫了浏览器无process的场景。配合打包器的常量折叠与 tree-shaking,生产构建中整个组件(及其引用的面板、事件存储等模块)都会被剔除。该行为有单元测试覆盖(packages/devtools/src/prod-guard.test.ts),分别验证了非 production、production、NODE_ENV未定义三种情况。
2. 事件存储:带上限的环形缓冲
面板内部createEventStore({ bufferSize })创建事件存储(默认 500 条,见 packages/devtools/src/event-store.ts)。它提供push、snapshot、subscribe、clear、size五个方法;push超过缓冲上限时丢弃最旧事件,snapshot返回全新数组保证调用方可安全当作不可变数据。所有标签页都订阅同一事件存储,事件到达时同步刷新。
3. 动作采集:核心包的 Action Observer
组件在挂载时调用registerActionObserver({ onDispatch, onSettle })(来自@json-render/core,实现见 packages/core/src/action-observer.ts)。Solid 渲染器的ActionProvider会在每次动作执行前后调用notifyActionDispatch/notifyActionSettle,DevTools 借此拿到:
- dispatch 事件:动作
id、名称name、参数params、触发时间at; - settle 事件:配对
id、是否成功ok、耗时durationMs、结果result或错误信息error。
观察器以模块级 pub/sub 实现,且每个观察器回调被 try/catch 包裹,DevTools 的异常不会中断动作执行。组件卸载时通过onCleanup反注册。
4. 流式事件采集:scanMessageParts
createEffect监听props.messages,对每条消息的parts调用scanMessageParts扫描(源码第 113–119 行)。seenParts用WeakSet记录已处理对象,保证重复渲染时同一部分不会被重复计入事件流。这是 Stream 标签页数据来源,用于观察 AI 逐步 patch 出 UI 的过程。
5. 与渲染器联动:markDevtoolsActive与data-jr-key
挂载时调用markDevtoolsActive()(实现于 packages/core/src/devtools-flag.ts),这是一个模块级计数器:DevTools 挂载期间计数 > 0,Solid 渲染器检测到isDevtoolsActive()为真后,会给每个渲染的元素包装节点打上data-jr-key属性,拾取器正是靠它把点击的 DOM 节点映射回 Spec key(见 packages/devtools/src/picker.ts 中DEVTOOLS_KEY_ATTR的定义)。未挂载 DevTools 时计数器为 0,渲染器行为与之前完全一致——纯 opt-in 机制。
6. Solid 适配层:桥接 State 上下文
组件通过useStateStore()(来自@json-render/solid,实现在 packages/solid/src/contexts/state.tsx)获取StateContextValue,将其包装为StateStore接口(get/set/update/getSnapshot/subscribe)交给面板核心,实现 State 标签页与渲染器状态的实时同步。注意:在 Solid 中读取可变状态要放在 JSX 表达式、createMemo或createEffect内,Hook 返回的是 accessor(详见 packages/solid/README.md 的「Differences from @json-render/react」一节)。
7. 生命周期管理
onMount中创建面板句柄handle、订阅选中总线;onCleanup中依次执行选中订阅反注册、markDevtoolsActive释放、handle.destroy()(面板从 DOM 移除并清理)。整套生命周期完全遵循 Solid 的组件模型,无需手动管理。
七、布局与停靠:position、reserveSpace 与 allowDockToggle
position接受三种值(定义见 packages/devtools/src/panel/types.ts):
| 值 | 面板停靠 | 浮动开关位置 | 适用场景 |
|---|---|---|---|
bottom-right(默认) | 底部抽屉 | 右下角 | 常规文档流布局 |
bottom-left | 底部抽屉 | 左下角 | 宿主应用右下角已有内容 |
right | 右侧全高面板 | 右上角 | 使用100vh/position: fixed; bottom: 0的 app-shell 布局,避免与底部固定元素冲突 |
两个联动选项:
reserveSpace(默认true):面板展开时通过<body>的 padding 为面板预留空间,内容会被推开。对height: 100vh、position: fixed; bottom: 0这类 CSS 无法自适应的布局,面板实际是覆盖(overlay)效果,此时可改传false保持纯覆盖,或让固定元素使用bottom: var(--jr-devtools-offset-bottom, 0)/right: var(--jr-devtools-offset-right, 0)这两个 CSS 自定义属性自行避让。false模式下这两个变量依然会在:root上发布,供应用按需消费。allowDockToggle(默认true):显示工具栏按钮让用户实时切换底部/右侧停靠,选择持久化到 localStorage(key 为__jr_devtools_dock),且一旦设置会覆盖初始position。传false则严格锁定position,工具栏不渲染该按钮,适合布局只兼容一种停靠方式的宿主。
八、自定义事件监听与常见问题
监听事件:传onEvent即可拿到事件流的最新一条:
<JsonRenderDevtools spec={spec()} catalog={catalog} messages={messages()} onEvent={(evt) => { if (evt.kind === "action-settled" && !evt.ok) { console.warn("action failed", evt.name, evt.error); } }} />DevtoolsEvent类型(含action-dispatched、action-settled等事件类型)由@json-render/devtools定义,@json-render/devtools-solid会原样 re-export(源码末尾export type { DevtoolsEvent })。
常见问题与建议:
- 生产构建后组件消失:这是预期行为——
isProduction()返回true时组件渲染null且被 tree-shake,确保线上零体积、零副作用。 - 快捷键不生效:检查
hotkey是否被显式传为false,或是否与其他应用快捷键冲突。语法为"mod+shift+j",mod在 macOS 上是 Cmd、其余平台是 Ctrl。 - 面板遮挡底部固定元素:将
position改为"right",或把固定元素的bottom/right接上--jr-devtools-offset-bottom/--jr-devtools-offset-right变量。 - 内存占用:长会话产生海量事件时,调小
bufferSize以控制事件存储体积;默认 500 已足够日常调试。
九、License
@json-render/devtools-solid以 Apache-2.0 协议开源(见 package.json)。
总结
@json-render/devtools-solid以极低的接入成本(一个组件、三行 JSX)为 SolidJS 生成式 UI 应用带来完整的调试体验:Spec 树检查、状态实时编辑、动作耗时追踪、AI 流式生成过程回放与 DOM 拾取联动。其背后是「框架无关核心 + 适配器」的架构——@json-render/devtools提供纯 TS 面板与事件存储,@json-render/devtools-solid负责桥接 Solid 的状态上下文与动作观察器,并在生产环境自动无痕退出。无论是调试 AI 聊天 UI 的逐步生成,还是排查状态绑定与动作执行问题,这套工具链都能让你直接「看见」渲染器内部发生了什么。
- 人工智能
- AI 应用
- 前端
- MCP 服务
【免费下载链接】json-render
The Generative UI framework
相关推荐
json-render SolidJS 调试实战:@json-render/devtools-solid 检查器面板接入指南
json render SolidJS 调试实战:@json render/devtools solid 检查器面板接入指南 导读:本文围绕 skills/de
人工智能AI 应用前端MCP 服务在 Vue 3 应用中接入 json-render DevTools:@json-render/devtools-vue 完整接入与源码解析
在 Vue 3 应用中接入 json render DevTools:@json render/devtools vue 完整接入与源码解析 @json ren
人工智能AI 应用前端MCP 服务json-render Devtools 的 Svelte 5 适配器:@json-render/devtools-svelte 接入指南与源码剖析
json render Devtools 的 Svelte 5 适配器:@json render/devtools svelte 接入指南与源码剖析 @json
人工智能AI 应用前端MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考