feat(json-renderer): 添加从 markdown 代码块中提取 JSON 的功能

- 新增 extractJsonFromMarkdown 函数,支持以下格式:
  * ```json {...} ``` 代码块
  * ``` {...} ``` 代码块
  * 直接的 JSON 字符串
- 修改 data 处理逻辑,支持字符串类型输入并自动提取 JSON
- 支持对象和数组形式的 JSON spec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude AI
2026-03-19 00:17:59 +00:00
parent a1deafe5a0
commit 5034cfe6fa

View File

@@ -3,17 +3,60 @@
*
* 基于 Vercel Labs json-render 的生成式 UI 渲染器
* 接收 AI 生成的 spec 并渲染为 React 组件
* 支持组件交互事件(通过 ActionProvider 传递 emit 函数)
*/
'use client';
import { useState, useMemo } from 'react';
import React, { useState, useMemo, useContext } from 'react';
import { jsonRenderRegistry } from '@/lib/json-render-registry';
import type { ComponentSpec } from '@/lib/json-render-catalog';
import { ActionContext } from '@/lib/action-context'; // 导入 ActionContext
// 避免直接导入 json-render 类型,使用 any 来处理类型兼容性
type AnySpec = Record<string, any>;
/**
* 从 markdown 代码块中提取 JSON
*
* 支持以下格式:
* - ```json {...} ```
* - ``` {...} ```
* - 直接的 JSON 字符串
*/
function extractJsonFromMarkdown(text: string): any | null {
if (typeof text !== 'string') {
return null;
}
// 1. 尝试匹配 ```json ... ``` 或 ``` ... ``` 代码块
const codeBlockMatch = text.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
if (codeBlockMatch) {
try {
return JSON.parse(codeBlockMatch[1]);
} catch {
return null;
}
}
// 2. 尝试匹配数组形式的代码块
const arrayBlockMatch = text.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
if (arrayBlockMatch) {
try {
return JSON.parse(arrayBlockMatch[1]);
} catch {
return null;
}
}
// 3. 尝试直接解析整个文本
try {
return JSON.parse(text);
} catch {
return null;
}
}
interface JsonRendererProps {
// 单个组件 spec
spec?: ComponentSpec | null;
@@ -56,9 +99,25 @@ export default function JsonRenderer({
// 处理原始 data
if (data) {
// 尝试直接作为 spec
if (typeof data === 'object' && data !== null && 'type' in data) {
result.push(data as AnySpec);
let parsedData: any = null;
// 如果 data 是字符串,尝试从 markdown 中提取 JSON
if (typeof data === 'string') {
parsedData = extractJsonFromMarkdown(data);
} else if (typeof data === 'object' && data !== null && 'type' in data) {
// 直接作为 spec
parsedData = data;
}
// 如果成功解析了数据
if (parsedData) {
if (Array.isArray(parsedData)) {
// 如果是数组,添加所有元素
result.push(...parsedData);
} else if (parsedData.type) {
// 如果是单个 spec添加它
result.push(parsedData);
}
}
}
@@ -108,6 +167,9 @@ export default function JsonRenderer({
return null;
}
// 获取 ActionContext 中的 emit 函数
const actionContext = useContext(ActionContext);
// 渲染组件
return (
<div className={`json-renderer space-y-4 ${className}`}>
@@ -118,7 +180,12 @@ export default function JsonRenderer({
const CustomComponent = (jsonRenderRegistry as any)[componentType];
if (CustomComponent) {
return <CustomComponent key={index} {...componentSpec} />;
// 传递 emit 函数给组件(如果存在 ActionContext
const props = {
...componentSpec,
...(actionContext && { emit: actionContext.emit }),
};
return <CustomComponent key={index} {...props} />;
}
// Fallback