| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /**
- * 组件注册表 - 动态组件定义
- *
- * 前端维护所有可用的 json-render 组件定义
- * 发送给后端,让 AI 了解可用的 UI 组件
- *
- * ## 架构说明
- *
- * 1. 前端在这里维护所有组件定义(组件类型、描述、Schema)
- * 2. 通过 generateComponentsPrompt() 生成发送给后端的提示词
- * 3. 后端使用这些提示词构建 SYSTEM_PROMPT,无需手动修改后端代码
- * 4. 添加新组件时,只需在这里添加定义即可
- */
- 'use client';
- /**
- * 组件配置接口
- */
- interface ComponentConfig {
- /** 组件类型标识 */
- type: string;
- /** 组件描述 */
- description: string;
- /** Schema 字段说明 */
- schema: Record<string, string>;
- /** 示例 JSON */
- example?: Record<string, unknown>;
- /** 使用场景提示 */
- usageHint?: string;
- }
- /**
- * 所有可用的 json-render 组件定义
- *
- * 添加新组件:
- * 1. 在此对象中添加组件定义
- * 2. 无需修改后端代码
- */
- export const AVAILABLE_COMPONENTS: Record<string, ComponentConfig> = {
- // ============ MCP 专用组件 ============
- 'translation-result': {
- type: 'translation-result',
- description: '翻译结果卡片,展示译文和使用的术语',
- schema: {
- translated: '译文文本',
- termsUsed: '使用的术语列表(可选)'
- },
- usageHint: '调用 translate_text 工具后使用此组件展示结果',
- example: {
- type: 'translation-result',
- translated: '这是一个示例翻译',
- termsUsed: ['术语1', '术语2']
- }
- },
- 'novel-list': {
- type: 'novel-list',
- description: '小说列表卡片,展示多部小说的摘要信息',
- schema: {
- novels: '小说数组,每个小说包含 id, title, author, description, chapterCount, tags'
- },
- usageHint: '调用 get_novels 工具后使用此组件展示列表',
- example: {
- type: 'novel-list',
- novels: [
- {
- id: '1',
- title: '示例小说',
- author: '作者名',
- description: '这是一本精彩的小说',
- chapterCount: 100,
- tags: ['玄幻', '冒险']
- }
- ]
- }
- },
- 'novel-detail': {
- type: 'novel-detail',
- description: '小说详情卡片,展示单部小说的完整信息',
- schema: {
- novel: '小说对象,包含 id, title, author, category, description, status, chapterCount, wordCount, viewCount, isVip'
- },
- usageHint: '调用 get_novel_detail 工具或用户点击小说卡片后使用此组件',
- example: {
- type: 'novel-detail',
- novel: {
- id: '1',
- title: '修真世界',
- author: '方想',
- category: '玄幻',
- description: '一个热血少年的修真之路...',
- status: '已完结',
- chapterCount: 1200,
- wordCount: 3500000,
- viewCount: 500000,
- isVip: false
- }
- }
- },
- 'chapter-reader': {
- type: 'chapter-reader',
- description: '章节阅读器,展示小说章节内容',
- schema: {
- novelTitle: '小说标题',
- chapterTitle: '章节标题',
- content: '章节内容',
- chapterNumber: '当前章节号(可选)',
- totalChapters: '总章节数(可选)'
- },
- usageHint: '调用 get_chapter 工具后使用此组件展示章节内容',
- example: {
- type: 'chapter-reader',
- novelTitle: '修真世界',
- chapterTitle: '第一章:开始',
- content: '章节内容...',
- chapterNumber: 1,
- totalChapters: 1200
- }
- },
- 'suggestion-buttons': {
- type: 'suggestion-buttons',
- description: '建议操作按钮组,提供可点击的建议操作',
- schema: {
- suggestions: '建议数组,每个建议包含 label(显示文本), message(发送的消息), icon(图标,可选)'
- },
- usageHint: '在展示列表、详情后,给出操作建议时使用',
- example: {
- type: 'suggestion-buttons',
- suggestions: [
- { label: '下一页', icon: '➡️', message: '显示下一页小说' },
- { label: '返回列表', icon: '🔙', message: '返回小说列表' }
- ]
- }
- },
- 'mcp-tool-call': {
- type: 'mcp-tool-call',
- description: '工具调用过程展示,显示工具执行的详细状态',
- schema: {
- tool: '工具名称',
- status: '执行状态:pending, running, success, error',
- args: '工具参数(可选)',
- result: '执行结果(可选)',
- error: '错误信息(可选)',
- timestamp: '时间戳(可选)'
- },
- usageHint: '展示工具调用过程时使用',
- example: {
- type: 'mcp-tool-call',
- tool: 'translate_text',
- status: 'success',
- args: { text: 'Hello' },
- result: { translated: '你好' }
- }
- },
- 'login-panel': {
- type: 'login-panel',
- description: '登录面板,提供用户登录界面',
- schema: {
- server: '服务器名称',
- email: '已登录的邮箱(可选)'
- },
- usageHint: '需要用户登录 MCP 服务器时使用',
- example: {
- type: 'login-panel',
- server: 'Novel Platform User'
- }
- },
- // ============ 基础组件 ============
- 'card': {
- type: 'card',
- description: '卡片容器,用于包裹其他内容',
- schema: {
- title: '卡片标题(可选)',
- children: '子组件数组(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要将内容组织成卡片时使用',
- example: {
- type: 'card',
- title: '卡片标题',
- children: [
- { type: 'text', content: '内容', variant: 'body' }
- ]
- }
- },
- 'stack': {
- type: 'stack',
- description: '布局容器,用于排列子元素',
- schema: {
- direction: '排列方向:row(横向)或 column(纵向,默认)',
- spacing: '间距(可选)',
- align: '对齐方式:start, center, end, stretch(可选)',
- children: '子组件数组(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要布局多个元素时使用',
- example: {
- type: 'stack',
- direction: 'column',
- spacing: 2,
- children: [
- { type: 'heading', level: 'h3', text: '标题' },
- { type: 'text', content: '内容' }
- ]
- }
- },
- 'heading': {
- type: 'heading',
- description: '标题组件,支持 h1-h6 级别',
- schema: {
- level: '标题级别:h1, h2, h3, h4, h5, h6(可选)',
- text: '标题文本',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要显示标题时使用',
- example: {
- type: 'heading',
- level: 'h2',
- text: '这是标题'
- }
- },
- 'text': {
- type: 'text',
- description: '文本组件,显示正文或次要文本',
- schema: {
- content: '文本内容',
- variant: '文本样式:body(正文), muted(次要文本), code(代码文本)(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要显示段落文本时使用',
- example: {
- type: 'text',
- content: '这是正文内容',
- variant: 'body'
- }
- },
- 'button': {
- type: 'button',
- description: '按钮组件,支持点击交互',
- schema: {
- label: '按钮文本',
- variant: '按钮样式:default, primary, secondary, ghost, danger(可选)',
- action: '交互事件名,如 sendMessage(可选)',
- actionPayload: '事件负载数据(可选)',
- disabled: '是否禁用(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要用户点击执行操作时使用',
- example: {
- type: 'button',
- label: '点击我',
- variant: 'primary',
- action: 'sendMessage',
- actionPayload: { message: '要发送的消息' }
- }
- },
- 'input': {
- type: 'input',
- description: '输入框组件,用于用户输入',
- schema: {
- placeholder: '占位符文本(可选)',
- value: '输入值(可选)',
- disabled: '是否禁用(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要用户输入文本时使用',
- example: {
- type: 'input',
- placeholder: '请输入...'
- }
- },
- 'badge': {
- type: 'badge',
- description: '徽章标签,显示状态或分类',
- schema: {
- text: '标签文本',
- variant: '标签样式:default, success, warning, error, info(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要显示状态或标签时使用',
- example: {
- type: 'badge',
- text: '已完结',
- variant: 'success'
- }
- },
- 'code-block': {
- type: 'code-block',
- description: '代码块展示组件',
- schema: {
- code: '代码内容',
- language: '编程语言(可选)',
- inline: '是否行内显示(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要显示代码时使用',
- example: {
- type: 'code-block',
- code: 'console.log("Hello");',
- language: 'javascript'
- }
- },
- 'data-table': {
- type: 'data-table',
- description: '数据表格,展示结构化数据',
- schema: {
- columns: '列定义数组,每列包含 key, label, sortable',
- rows: '行数据数组,每行是一个对象',
- sortable: '是否支持排序(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要展示表格数据时使用',
- example: {
- type: 'data-table',
- columns: [
- { key: 'name', label: '名称' },
- { key: 'value', label: '值' }
- ],
- rows: [
- { name: '项目1', value: '值1' },
- { name: '项目2', value: '值2' }
- ]
- }
- },
- 'separator': {
- type: 'separator',
- description: '分隔线,用于视觉分隔',
- schema: {
- orientation: '方向:horizontal(水平,默认)或 vertical(垂直)(可选)',
- className: '自定义样式类名(可选)'
- },
- usageHint: '需要在内容间添加分隔时使用',
- example: {
- type: 'separator',
- orientation: 'horizontal'
- }
- }
- };
- /**
- * 组件类型
- */
- export type ComponentType = keyof typeof AVAILABLE_COMPONENTS;
- /**
- * 生成组件使用说明文本
- *
- * 此函数将所有组件定义转换为发送给后端的提示词格式
- * 后端使用这些提示词构建 Claude 的 SYSTEM_PROMPT
- *
- * @returns 组件说明文本(Markdown 格式)
- */
- export function generateComponentsPrompt(): string {
- const components = Object.entries(AVAILABLE_COMPONENTS);
- // 分组:MCP 专用组件 和 基础组件
- const mcpComponents = components.filter(([key]) => {
- const config = AVAILABLE_COMPONENTS[key];
- return [
- 'translation-result', 'novel-list', 'novel-detail', 'chapter-reader',
- 'suggestion-buttons', 'mcp-tool-call', 'login-panel'
- ].includes(config.type);
- });
- const baseComponents = components.filter(([key]) => {
- const config = AVAILABLE_COMPONENTS[key];
- return ![
- 'translation-result', 'novel-list', 'novel-detail', 'chapter-reader',
- 'suggestion-buttons', 'mcp-tool-call', 'login-panel'
- ].includes(config.type);
- });
- let prompt = '## 可用的 json-render 组件\n\n';
- prompt += '你可以使用以下组件来展示结构化数据。组件是 JSON 对象,放在回复的 ```json 代码块中。\n\n';
- // MCP 专用组件
- if (mcpComponents.length > 0) {
- prompt += '### MCP 专用组件\n\n';
- prompt += '这些组件用于展示 MCP 工具调用的结果:\n\n';
- mcpComponents.forEach(([key, config]) => {
- prompt += `#### ${config.type}\n`;
- prompt += `${config.description}\n`;
- if (config.usageHint) {
- prompt += `> ${config.usageHint}\n`;
- }
- prompt += '\n**Schema:**\n';
- Object.entries(config.schema).forEach(([fieldName, description]) => {
- prompt += `- \`${fieldName}\`: ${description}\n`;
- });
- prompt += '\n**示例:**\n';
- prompt += '```json\n';
- prompt += JSON.stringify(config.example || { type: key }, null, 2);
- prompt += '\n```\n\n';
- });
- }
- // 基础组件
- if (baseComponents.length > 0) {
- prompt += '### 基础组件\n\n';
- prompt += '这些组件用于构建通用 UI:\n\n';
- baseComponents.forEach(([key, config]) => {
- prompt += `#### ${config.type}\n`;
- prompt += `${config.description}\n`;
- if (config.usageHint) {
- prompt += `> ${config.usageHint}\n`;
- }
- prompt += '\n**Schema:**\n';
- Object.entries(config.schema).forEach(([fieldName, description]) => {
- prompt += `- \`${fieldName}\`: ${description}\n`;
- });
- prompt += '\n**示例:**\n';
- prompt += '```json\n';
- prompt += JSON.stringify(config.example || { type: key }, null, 2);
- prompt += '\n```\n\n';
- });
- }
- return prompt;
- }
- /**
- * 获取组件定义
- */
- export function getComponentConfig(type: string): ComponentConfig | undefined {
- return AVAILABLE_COMPONENTS[type];
- }
- /**
- * 获取所有组件类型列表
- */
- export function getComponentTypes(): string[] {
- return Object.keys(AVAILABLE_COMPONENTS);
- }
- /**
- * 检查组件类型是否有效
- */
- export function isValidComponentType(type: string): boolean {
- return type in AVAILABLE_COMPONENTS;
- }
|