fix(site): add MCP tools for query examples and jump examples (#3623)

This commit is contained in:
申君健 2025-07-30 10:20:15 +08:00 committed by GitHub
parent 41b9fbaade
commit 96cd780f26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 88 additions and 13 deletions

View File

@ -10,7 +10,7 @@
<div class="right-panel" :class="{ collapsed: !showTinyRobot }">
<tiny-robot-chat />
</div>
<IconAi @click="handleShowTinyRobot" class="style-settings-icon"></IconAi>
<IconAi v-show="!showTinyRobot" @click="handleShowTinyRobot" class="style-settings-icon"></IconAi>
<tiny-dialog-box
v-model:visible="boxVisibility"
:close-on-click-modal="false"
@ -136,7 +136,7 @@ const handleShowTinyRobot = () => {
padding: 34px 0 0;
}
}
.right-panel {
.right-panel:not(.collapsed) {
:deep(.tr-container) {
z-index: 9999;
}

View File

@ -3,7 +3,7 @@
<tr-container v-model:show="showTinyRobot" v-model:fullscreen="fullscreen">
<tr-bubble-provider :message-renderers="messageRenderers">
<div v-if="showMessages.length === 0">
<div class="robot-top-msg" v-if="showMessages.length === 0">
<tr-welcome title="智能助手" description="您好我是Opentiny AI智能助手" :icon="welcomeIcon">
<template #footer>
<div class="welcome-footer"></div>
@ -17,7 +17,8 @@
@item-click="handlePromptItemClick"
></tr-prompts>
</div>
<tr-bubble-list v-else :items="showMessages" :roles="roles" auto-scroll> </tr-bubble-list>
<tr-bubble-list v-else class="robot-top-msg markdown-body" :items="showMessages" :roles="roles" auto-scroll>
</tr-bubble-list>
</tr-bubble-provider>
<template #footer>
@ -172,6 +173,11 @@ watch(() => messages.value[messages.value.length - 1]?.content, scrollToBottom)
height: 600px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
}
/** 聊天顶部要撑开空间 */
.robot-top-msg {
flex: 1;
}
</style>
<style>

View File

@ -22,8 +22,13 @@ export const useTinyRobot = () => {
const promptItems = [
{
label: '智能操作网页',
description: '帮我选中最贵的手机商品',
label: '快速跳到文档',
description: '帮我切换到国际化指南',
icon: h('span', { style: { fontSize: '18px' } }, '🕹')
},
{
label: '快速跳到组件',
description: '帮我切换到 Select 组件',
icon: h('span', { style: { fontSize: '18px' } }, '🕹')
}
]
@ -54,14 +59,24 @@ export const useTinyRobot = () => {
const suggestionPillItems = [
{
id: '1',
text: '帮我选中最贵的手机商品',
icon: h('span', { style: { fontSize: '18px' } }, '🕹')
text: 'Select',
icon: h('span', { style: { fontSize: '18px' } }, '🧩')
},
{
id: '1',
text: '表格',
icon: h('span', { style: { fontSize: '18px' } }, '🧩')
},
{
id: '1',
text: '树组件',
icon: h('span', { style: { fontSize: '18px' } }, '🧩')
}
]
function handleSuggestionPillItemClick(item: SuggestionItem) {
if (item.id === '1') {
let templateText = `请对 [目标组件] ,执行 [操作]`
let templateText = `帮我跳转到 [目标组件]`
let currentInitialValue = { 目标组件: item.text, : '' }
if (senderRef.value) {

View File

@ -2,6 +2,9 @@ import { genMenus } from '../menus'
import { z } from 'zod'
import { useRouter } from 'vue-router'
// 组件页面的右上导航的数据回调函数
export const cmpAnchorDataCallback = { value: null }
export const createGlobalMcpTool = (server) => {
const router = useRouter()
server.registerResource(
@ -26,11 +29,13 @@ export const createGlobalMcpTool = (server) => {
'swtich-router',
{
title: 'router',
description: '可以帮用户跳转页面比如跳转到组件文档页面和API文档页面',
description: '可以帮用户跳转到文档页面组件示例的总页面或组件API文档页面或组件库的概览页面',
inputSchema: {
key: z.string().describe('跳转页面路径'),
type: z.enum(['components', 'docs', 'overview', 'features']).describe('跳转页面类型'),
isOpenApi: z.boolean().describe('是否打开API文档')
type: z
.enum(['components', 'docs', 'overview', 'features'])
.describe('跳转页面类型,比如:组件的页面,文档的页面,组件的概览页面'),
isOpenApi: z.boolean().describe('跳转到组件页面时是否打开API文档')
}
},
async ({ key, type, isOpenApi }) => {
@ -51,6 +56,49 @@ export const createGlobalMcpTool = (server) => {
}
)
server.registerTool(
'get-component-demos',
{
title: '查询全部示例的信息',
description:
'查询当前组件的全部示例信息demos信息。返回值是一个数组其中每一项的 demoId 属性是示例的键通过键可以跳转到该示例。desc属性是示例的详细描述。',
inputSchema: {}
},
async () => {
// 通知组件页面返回右侧导航的数据
if (cmpAnchorDataCallback.value != null) {
const links = cmpAnchorDataCallback.value()
return {
content: [{ type: 'text', text: JSON.stringify(links) }]
}
} else {
return {
content: [{ type: 'text', text: '找不到示例' }]
}
}
}
)
server.registerTool(
'jump-to-demo',
{
title: '跳转到组件的示例demo',
description: '根据参数demoId, 跳转到指定的示例demo。',
inputSchema: {
demoId: z.string().describe('示例的id,唯一标识。')
}
},
async ({ demoId }) => {
// 通知组件页面返回右侧导航的数据
location.hash = '#' + demoId
return {
content: [{ type: 'text', text: '跳转示例成功' }]
}
}
)
// 长任务示例
server.registerTool(
'long-task',

View File

@ -91,7 +91,7 @@
</template>
<script setup lang="ts">
import { reactive, computed, watch, onMounted, nextTick, ref } from 'vue'
import { reactive, computed, watch, onMounted, nextTick, ref, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { TinyTabs, TinyTabItem } from '@opentiny/vue'
import { debounce } from '@opentiny/utils'
@ -106,6 +106,7 @@ import ApiDocs from '../../components/api-docs.vue'
import McpDocs from '../../components/mcp-docs.vue'
import useTasksFinish from '../../composable/useTasksFinish'
import { appData } from '../../tools/appData'
import { cmpAnchorDataCallback } from '../../tools/globalMcpTool'
const props = defineProps({ loadData: {}, appMode: {}, demoKey: {} })
@ -442,6 +443,11 @@ const handleAnchorClick = (e, data) => {
}
}
cmpAnchorDataCallback.value = () => state.currJson.demos
onUnmounted(() => {
cmpAnchorDataCallback.value = null
})
defineExpose({ loadPage })
</script>