Compare commits
No commits in common. "db200f960122716d6cb91592c6401fe2c752c47b" and "23d0ce1d93c4e57fb9763853b636e526994a38c6" have entirely different histories.
db200f9601
...
23d0ce1d93
42
.github/workflows/build.yaml
vendored
@ -1,42 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build all
|
||||
run: yarn build
|
||||
|
||||
- name: Package VSIX
|
||||
run: npx vsce package --out dist/openmcp.vsix
|
||||
|
||||
- name: Upload VSIX
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openmcp-${{ matrix.os }}
|
||||
path: dist/openmcp.vsix
|
||||
|
41
.github/workflows/test.yaml
vendored
@ -1,41 +0,0 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Install xvfb (Linux only)
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y xvfb
|
||||
|
||||
- name: Run Test with XVFB (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: xvfb-run --auto-servernum yarn test
|
||||
|
||||
- name: Run Test (Windows/macOS)
|
||||
if: runner.os != 'Linux'
|
||||
run: yarn test
|
4
.gitignore
vendored
@ -15,7 +15,3 @@ resources/renderer
|
||||
resources/service
|
||||
*.traineddata
|
||||
.turbo
|
||||
stats.html
|
||||
.openmcp
|
||||
test-vsix
|
||||
resources/changelog/**
|
1
.vscode/openmcp_connection.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"items":[]}
|
7
.vscode/settings.json
vendored
@ -11,6 +11,9 @@
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
|
||||
"i18n-haru.root": "./renderer/src/i18n",
|
||||
"i18n-haru.main": "zh"
|
||||
"i18n-haru.root": "renderer/src/i18n",
|
||||
"i18n-haru.main": "zh",
|
||||
"i18n-ally.localesPaths": [
|
||||
"renderer/src/i18n"
|
||||
]
|
||||
}
|
||||
|
411
.vscode/tabs.example-servers_puppeteer.json
vendored
Normal file
387
.vscode/tabs.my-browser.json
vendored
Normal file
@ -0,0 +1,387 @@
|
||||
{
|
||||
"currentIndex": 3,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "交互测试",
|
||||
"icon": "icon-robot",
|
||||
"type": "blank",
|
||||
"componentIndex": 3,
|
||||
"storage": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "总结 https://asoca.grand-challenge.org/ ",
|
||||
"extraInfo": {
|
||||
"created": 1747379137481,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_2z36dkpt4m5jaioqjsbjabiw",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://asoca.grand-challenge.org/\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379138355,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_2z36dkpt4m5jaioqjsbjabiw",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Navigated to https://asoca.grand-challenge.org/"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379139028,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "现在我将总结 https://asoca.grand-challenge.org/ 的内容。\n\n首先,我需要浏览页面并提取相关信息。接下来,我将执行以下步骤:\n\n1. 获取页面的标题和主要描述。\n2. 提取页面中的关键信息,如项目目标、参与方式、时间安排等。\n3. 总结页面的主要内容和目的。\n\n让我开始执行这些步骤。",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_zv6y2wsmzqn9ei7y8oywt75r",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.title\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "call_uzg4bj50l2ekjaonrkj8r852",
|
||||
"index": 1,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.querySelector('meta[name=\\\"description\\\"]').content\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "call_q3jqivcv8vgfpmk4u7w9k9au",
|
||||
"index": 2,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"Array.from(document.querySelectorAll('h1, h2, h3, p')).map(el => el.textContent.trim()).join('\\\\n')\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379142638,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_zv6y2wsmzqn9ei7y8oywt75r",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"ERROR: The request could not be satisfied\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379142650,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 1,
|
||||
"tool_call_id": "call_uzg4bj50l2ekjaonrkj8r852",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Script execution failed: Cannot read properties of null (reading 'content')"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379142656,
|
||||
"state": "tool call failed",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 2,
|
||||
"tool_call_id": "call_q3jqivcv8vgfpmk4u7w9k9au",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"403 ERROR\\nThe request could not be satisfied.\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379142664,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_w7iq0mszlm46ooo06w9fvri4",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_navigate",
|
||||
"arguments": "{\"url\":\"https://asoca.grand-challenge.org/\",\"launchOptions\":{\"headless\":false,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379144069,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_w7iq0mszlm46ooo06w9fvri4",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Navigated to https://asoca.grand-challenge.org/"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379147016,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_qelzom3lpyo7skuil4b469t9",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.title\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "call_wtla8rzqnes1gffk7wieubxk",
|
||||
"index": 1,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"document.querySelector('meta[name=\\\"description\\\"]').content\"}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "call_z6wcwspc9lqk4w3sfknv30um",
|
||||
"index": 2,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "k_evaluate",
|
||||
"arguments": "{\"script\":\"Array.from(document.querySelectorAll('h1, h2, h3, p')).map(el => el.textContent.trim()).join('\\\\n')\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379148961,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 0,
|
||||
"tool_call_id": "call_qelzom3lpyo7skuil4b469t9",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379149424,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 1,
|
||||
"tool_call_id": "call_wtla8rzqnes1gffk7wieubxk",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Script execution failed: Cannot read properties of null (reading 'content')"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379149428,
|
||||
"state": "tool call failed",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"index": 2,
|
||||
"tool_call_id": "call_z6wcwspc9lqk4w3sfknv30um",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1747379149432,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "由此可见,网站 https://asoca.grand-challenge.org/ 可能有一些限制或反爬虫机制,导致无法直接获取页面内容。建议通过以下方式进一步分析:\n\n1. **检查页面加载状态**:确认页面是否完全加载。\n2. **查看网络请求**:检查是否有额外的网络请求或动态内容加载。\n3. **使用开发者工具**:手动查看页面元素和结构。\n\n如果需要进一步操作,请告诉我!",
|
||||
"extraInfo": {
|
||||
"created": 1747379151760,
|
||||
"state": "success",
|
||||
"serverName": "Huoshan DeepSeek"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"modelIndex": 7,
|
||||
"enableTools": [
|
||||
{
|
||||
"name": "k_navigate",
|
||||
"description": "Navigate to a URL",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_screenshot",
|
||||
"description": "Take a screenshot of the current page or a specific element",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_click",
|
||||
"description": "Click an element on the page",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_fill",
|
||||
"description": "Fill out an input field",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_select",
|
||||
"description": "Select an element on the page with Select tag",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_hover",
|
||||
"description": "Hover an element on the page",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"name": "k_evaluate",
|
||||
"description": "Execute JavaScript in the browser console",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"enableWebSearch": false,
|
||||
"temperature": 0.7,
|
||||
"contextLength": 20,
|
||||
"systemPrompt": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "工具",
|
||||
"icon": "icon-tool",
|
||||
"type": "tool",
|
||||
"componentIndex": 2,
|
||||
"storage": {
|
||||
"currentToolName": "k_navigate",
|
||||
"formData": {
|
||||
"url": "https://asoca.grand-challenge.org/",
|
||||
"launchOptions": {
|
||||
"headless": false
|
||||
},
|
||||
"allowDangerous": false
|
||||
},
|
||||
"lastToolCallResponse": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Navigated to https://asoca.grand-challenge.org/"
|
||||
}
|
||||
],
|
||||
"isError": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "资源",
|
||||
"icon": "icon-file",
|
||||
"type": "blank",
|
||||
"componentIndex": 0,
|
||||
"storage": {
|
||||
"formData": {},
|
||||
"currentType": "resource",
|
||||
"currentResourceName": "Browser console logs",
|
||||
"lastResourceReadResponse": {
|
||||
"contents": [
|
||||
{
|
||||
"uri": "console://logs",
|
||||
"mimeType": "text/plain",
|
||||
"text": "[warn] Error with Permissions-Policy header: Unrecognized feature: 'ambient-light-sensor'.\n[warn] Error with Permissions-Policy header: Unrecognized feature: 'document-domain'.\n[warn] Error with Permissions-Policy header: Unrecognized feature: 'ambient-light-sensor'.\n[warn] Error with Permissions-Policy header: Unrecognized feature: 'document-domain'.\n[error] Failed to load resource: the server responded with a status of 403 ()\n[error] Failed to load resource: the server responded with a status of 403 ()"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "工具",
|
||||
"icon": "icon-tool",
|
||||
"type": "blank",
|
||||
"componentIndex": 2,
|
||||
"storage": {
|
||||
"formData": {
|
||||
"script": "document.body.innerText"
|
||||
},
|
||||
"currentToolName": "k_evaluate",
|
||||
"lastToolCallResponse": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Execution result:\n\"\"\n\nConsole output:\n"
|
||||
}
|
||||
],
|
||||
"isError": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -14,7 +14,6 @@ vsc-extension-quickstart.md
|
||||
**/.vscode-test.*
|
||||
renderer/**
|
||||
service/**
|
||||
news/**
|
||||
test/**
|
||||
servers/**
|
||||
scripts/**
|
||||
@ -24,9 +23,4 @@ software/**
|
||||
|
||||
.editorconfig
|
||||
.gitattributes
|
||||
*.vsix
|
||||
.turbo
|
||||
.github
|
||||
webpack
|
||||
.openmcp
|
||||
.vscode
|
||||
*.vsix
|
77
CHANGELOG.md
@ -1,80 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
## [main] 0.1.10
|
||||
- 修复 issue #48: 修复错误的引导路径。
|
||||
- 修复非中文语言下,初始化引导界面不跟随主界面的问题。
|
||||
|
||||
## [main] 0.1.9
|
||||
- 增加 mook 功能:可以利用随机种子或者AI生成来自动化填充测试 tool 的表单数据
|
||||
- 增加工具自检功能:openmcp 的 tool 下可以点击「工具模块」 右侧的 「工具自检」进入自检模式,该模式下,用户可以自己定义工具执行的拓扑顺序,然后一次性进行自动检测。
|
||||
- 修复 issue #44: 完成链接跳转的平台适配
|
||||
- 修复 issue #36: 完成非文件夹打开下的成功启动
|
||||
- 修复 issue #45: 数组类型参数不支持
|
||||
- 修复多行对话粘贴进入对话框样式异常的问题
|
||||
|
||||
## [main] 0.1.8
|
||||
- 增加 STDIO 下的热更新,现在用户修改 mcp 代码,openmcp 会自动完成一切相关功能的热更新,无需用户手动重启。
|
||||
- 完成 mcpconfig.json 的导出功能,导出的 配置文件 可以通过 openmcp-sdk 框架完成低代码 agent 部署;也可以直接载入 Claude Desktop 等等 MCP 客户端中,实现 MCP 的快速部署和使用。
|
||||
- 修复若干 vscode 插件端 bug
|
||||
|
||||
## [main] 0.1.7
|
||||
- 新的构建系统
|
||||
- 修复无法在 trae & cursor 中使用的 bug
|
||||
|
||||
## [main] 0.1.6
|
||||
- 针对 0.1.5 无法在 Windows 启动的紧急修复。
|
||||
- 修复环境变量中添加 token 失效的问题。
|
||||
- 优化工具展示的页面布局。
|
||||
|
||||
## [main] 0.1.5
|
||||
- 修复 gemini 获取模型列表时存在 models 前缀的问题
|
||||
- 增加 web api 功能
|
||||
- 修复无法在对话框中使用 mcp resource 的 bug
|
||||
- 调试结果的工作区从 .vscode 迁移到 .openmcp,连接配置文件从 .vscode/openmcp_connection.json 迁移到 .openmcp/connection.json
|
||||
- 技术栈更新:openmcp 全链路组件切换为 esm
|
||||
- 优化引导界面文字布局。
|
||||
- 文档和软件本体支持其他国家语言。
|
||||
|
||||
## [main] 0.1.4
|
||||
- 重新实现 openai 协议的底层网络实现,从而支持 Google Gemini 全系列模型。
|
||||
- 实现 index 适配器,从而支持 Grok3 全系列模型。
|
||||
- 解决 issue#23 插件创建连接时报错“Cannot read properties of undefined (reading 'name')”
|
||||
- 在填写 apikey 和 baseurl 的情况下,现在可以一键刷新模型列表,避免用户手动输入模型列表。
|
||||
|
||||
## [main] 0.1.3
|
||||
- 解决 issue#21 点击按钮后的发送文本后不会清空当前的输入框。
|
||||
- 修复暂停按键在多轮对话后消失的问题。
|
||||
- 修复 issue#25 无法连接 streamable http 的问题。
|
||||
|
||||
## [main] 0.1.2
|
||||
- 新特性:用户发送的信息增加「重新发送」按钮。
|
||||
- 支持特性 issue#17 「关于左侧添加mcp服务器操作优化问题」:增加强制聚焦功能,用户创建mcp服务器连接的过程中不会让输入框失去焦点。
|
||||
- 更新 MCP & OpenAI 协议内容。
|
||||
- 解决 issue#21 vscode插件界面bug,在高度有限情况下无法通过滚动完全显示连接按钮。
|
||||
- 解决 issue#21 最后一个标签页关闭并恢复默认页面。
|
||||
- 解决 issue#22 工具模块UI异常,现在 openmcp 支持解析 pydantic 进行 typing 的 python mcp 了。
|
||||
- 优化对象输入框,现在对象输入框具有语法高亮和受限度的自动补全了。
|
||||
- 对于 trae 的所有默认主题进行额外支持。
|
||||
|
||||
## [main] 0.1.1
|
||||
- 修复 SSH 连接 Ubuntu 的情况下的部分 bug
|
||||
- 修复 python 项目点击 openmcp 进行连接时,初始化参数错误的问题
|
||||
- 取消 service 底层的 mcp 连接复用技术,防止无法刷新
|
||||
- 修复连接后,可能无法在欢迎界面选择调试选项的 bug
|
||||
|
||||
## [main] 0.1.0
|
||||
## [main] 0.0.9
|
||||
- 修复 0.0.8 引入的 bug:system prompt 返回的是索引而非真实内容
|
||||
- 新特性:支持同时连入多个 mcp server
|
||||
- 新特性:更新协议内容,支持 streamable http 协议,未来将逐步取代 SSE 的连接方式
|
||||
- impl issue#16:对于 uv 创建的 py 项目进行特殊支持,自动初始化项目,并将 mcp 定向到 .venv/bin/mcp 中,不再需要用户全局安装 mcp
|
||||
- 对于 npm 创建的 js/ts 项目进行特殊支持:自动初始化项目
|
||||
- 去除了 websearch 的设置,增加了 parallel_tool_calls 的设置,parallel_tool_calls 默认为 true,代表 允许模型在单轮回复中调用多个工具
|
||||
- 重构了 openmcp 连接模块的基础设施,基于新的技术设施实现了更加详细的连接模块的日志系统.
|
||||
- impl issue#15:无法复制
|
||||
- impl issue#14:增加日志清除按钮
|
||||
|
||||
## [main] 0.0.9
|
||||
- 修复 0.0.8 引入的bug:system prompt 返回的是索引而非真实内容
|
||||
- 测试新的发布管线
|
||||
|
||||
## [main] 0.0.8
|
||||
- 大模型 API 测试时更加完整的报错
|
||||
@ -135,4 +64,4 @@
|
||||
- 完成配置加载,保存,大模型设置
|
||||
- 完成标签页自动保存
|
||||
- 完成大模型对话窗口和工具调用
|
||||
- 完成对 vscode 和 trae 的支持
|
||||
- 完成对 vscode 和 trae 的支持
|
169
CLAUDE.md
@ -1,169 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Setup and Installation
|
||||
```bash
|
||||
npm run setup # Install dependencies and prepare OCR resources
|
||||
```
|
||||
|
||||
### Development
|
||||
```bash
|
||||
npm run serve # Start all services in development mode (uses Turbo)
|
||||
npm run build # Build all modules
|
||||
npm run build:all # Build all modules (alias)
|
||||
npm run build:electron # Build only Electron app
|
||||
```
|
||||
|
||||
### Service Development
|
||||
```bash
|
||||
cd service
|
||||
npm run serve # Start service with hot reload (nodemon + tsx)
|
||||
npm run build # Compile TypeScript to dist/
|
||||
npm run debug # Start with Node.js inspector
|
||||
npm run typecheck # Type checking without emit
|
||||
```
|
||||
|
||||
### Renderer Development
|
||||
```bash
|
||||
cd renderer
|
||||
npm run serve # Start Vite dev server
|
||||
npm run build # Build for production
|
||||
npm run serve:website # Start in website mode
|
||||
npm run type-check # Vue TypeScript checking
|
||||
```
|
||||
|
||||
### VSCode Extension
|
||||
```bash
|
||||
npm run vscode:prepublish # Prepare for VSCode publishing (Rollup build)
|
||||
npm run compile # Compile TypeScript
|
||||
npm run watch # Watch mode compilation
|
||||
vsce package # Create VSIX package for distribution
|
||||
vsce publish # Publish to VSCode Marketplace (requires auth)
|
||||
```
|
||||
|
||||
### Quality Assurance
|
||||
```bash
|
||||
npm run lint # ESLint for TypeScript files
|
||||
npm run pretest # Run compile and lint
|
||||
npm run test # Run tests
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Multi-Module Structure
|
||||
OpenMCP follows a **layered modular architecture** with three main deployment targets:
|
||||
|
||||
1. **VSCode Extension** (`src/extension.ts`) - IDE integration
|
||||
2. **Service Layer** (`service/`) - Node.js backend handling MCP protocol
|
||||
3. **Renderer Layer** (`renderer/`) - Vue.js frontend for UI
|
||||
|
||||
### Key Architectural Patterns
|
||||
|
||||
#### Message Bridge Communication
|
||||
The system uses a **message bridge pattern** for cross-platform communication:
|
||||
- **VSCode**: Uses `vscode.postMessage` API
|
||||
- **Electron**: Uses IPC communication
|
||||
- **Web**: Uses WebSocket connections
|
||||
- **Node.js**: Uses EventEmitter for SDK mode
|
||||
|
||||
All communication flows through `MessageBridge` class in `renderer/src/api/message-bridge.ts`.
|
||||
|
||||
#### MCP Client Management
|
||||
- **Connection Management**: `service/src/mcp/connect.service.ts` handles multiple MCP server connections
|
||||
- **Client Pooling**: `clientMap` maintains active MCP client instances with UUID-based identification
|
||||
- **Transport Abstraction**: Supports STDIO, SSE, and StreamableHTTP transports
|
||||
- **Auto-reconnection**: `McpServerConnectMonitor` handles connection monitoring
|
||||
|
||||
#### Request/Response Flow
|
||||
```
|
||||
Frontend (Vue) → MessageBridge → Service Router → MCP Controllers → MCP SDK → External MCP Servers
|
||||
```
|
||||
|
||||
### Important Service Patterns
|
||||
|
||||
#### Preprocessing Commands
|
||||
`service/src/mcp/connect.service.ts` includes **automatic environment setup**:
|
||||
- Python projects: Auto-runs `uv sync` and installs MCP CLI
|
||||
- Node.js projects: Auto-runs `npm install` if node_modules missing
|
||||
- Path resolution: Handles `~/` home directory expansion
|
||||
|
||||
#### OCR Integration
|
||||
Built-in OCR using Tesseract.js:
|
||||
- Images from MCP responses are automatically processed
|
||||
- Base64 images saved to temp files and queued for OCR
|
||||
- Results delivered via worker threads
|
||||
|
||||
### Frontend Architecture (Vue 3)
|
||||
|
||||
#### State Management
|
||||
- **Panel System**: Tab-based interface in `renderer/src/components/main-panel/`
|
||||
- **Reactive Connections**: MCP connection state managed reactively
|
||||
- **Multi-language**: Vue i18n with support for 9 languages
|
||||
|
||||
#### Core Components
|
||||
- **Chat Interface**: `main-panel/chat/` - LLM interaction with MCP tools
|
||||
- **Tool Testing**: `main-panel/tool/` - Direct MCP tool invocation
|
||||
- **Resource Browser**: `main-panel/resource/` - MCP resource exploration
|
||||
- **Prompt Manager**: `main-panel/prompt/` - System prompt templates
|
||||
|
||||
### Build System
|
||||
|
||||
#### Turbo Monorepo
|
||||
Uses Turbo for coordinated builds across modules:
|
||||
- **Dependency ordering**: Renderer builds before Electron
|
||||
- **Parallel execution**: Service and Renderer can build concurrently
|
||||
- **Task caching**: Disabled for development iterations
|
||||
|
||||
#### Rollup Configuration
|
||||
VSCode extension uses Rollup for optimal bundling:
|
||||
- **ES modules**: Output as ESM format
|
||||
- **External dependencies**: VSCode API marked as external
|
||||
- **TypeScript**: Direct compilation without webpack
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Adding New MCP Features
|
||||
1. **Service Layer**: Add controller in `service/src/mcp/`
|
||||
2. **Router Registration**: Add to `ModuleControllers` in `service/src/common/router.ts`
|
||||
3. **Frontend Integration**: Add API calls in `renderer/src/api/`
|
||||
4. **UI Components**: Create components in `renderer/src/components/`
|
||||
|
||||
### Testing MCP Servers
|
||||
1. **Connection**: Configure in connection panel (STDIO/SSE/HTTP)
|
||||
2. **Validation**: Test tools/resources in respective panels
|
||||
3. **Integration**: Verify LLM interaction in chat interface
|
||||
|
||||
### Packaging VSCode Extension
|
||||
|
||||
1. **Build Dependencies**: Run `npm run build` to build all modules
|
||||
2. **Prepare Extension**: Run `npm run vscode:prepublish` to bundle extension code
|
||||
3. **Create Package**: Run `vsce package` to generate `.vsix` file
|
||||
4. **Install Locally**: Use `code --install-extension openmcp-x.x.x.vsix` for testing
|
||||
5. **Publish**: Run `vsce publish` (requires marketplace publisher account)
|
||||
|
||||
### Platform-Specific Considerations
|
||||
|
||||
- **VSCode**: Uses webview API, limited to extension context
|
||||
- **Electron**: Full desktop app capabilities, local service spawning
|
||||
- **Web**: Requires external service, WebSocket limitations
|
||||
- **SDK**: Embedded in other Node.js applications
|
||||
|
||||
## Important Files
|
||||
|
||||
### Configuration
|
||||
|
||||
- `turbo.json` - Monorepo build orchestration
|
||||
- `rollup.config.js` - VSCode extension bundling
|
||||
- `service/package.json` - Backend dependencies and scripts
|
||||
- `renderer/package.json` - Frontend dependencies and scripts
|
||||
|
||||
### Core Architecture
|
||||
|
||||
- `src/extension.ts` - VSCode extension entry point
|
||||
- `service/src/main.ts` - Service WebSocket server
|
||||
- `service/src/common/router.ts` - Request routing system
|
||||
- `renderer/src/api/message-bridge.ts` - Cross-platform communication
|
||||
- `service/src/mcp/client.service.ts` - MCP client implementation
|
199
README.md
@ -2,78 +2,76 @@
|
||||
|
||||
<img src="./icons/openmcp.png" height="200px"/>
|
||||
|
||||
<h3>OpenMCP: All you need for MCP Development</h3>
|
||||
<h3>OpenMCP: 一体化 MCP Server 调试器</h3>
|
||||
|
||||
English | [中文](./README.zh.md)
|
||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #CB81DA; color: white; border-radius: .5em; text-decoration: none;">👉 加入 OpenMCP正式级技术组</a>
|
||||
|
||||
<a href="https://openmcp.kirigaya.cn" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;">🫱 Official Documentation</a>
|
||||
|
||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #CB81DA; color: white; border-radius: .5em; text-decoration: none;">OpenMCP QQ Group</a>
|
||||
|
||||
<a href="https://discord.gg/SKTZRf6NzU" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;">OpenMCP Discord Channel</a>
|
||||
<a href="https://discord.gg/af5cfB9a" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;"> 加入 OpenMCP Discord频道</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
## OpenMCP
|
||||
|
||||
An all-in-one vscode/trae/cursor plugin for MCP server debugging.
|
||||
一款用于 MCP 服务端调试的一体化 vscode/trae/cursor 插件。
|
||||
|
||||
[](https://www.youtube.com/watch?v=S7igsEhcLiw)
|
||||
### [👆 Full Video](https://www.youtube.com/watch?v=S7igsEhcLiw)
|
||||
<video src="https://github.com/user-attachments/assets/ab214d58-b77c-4bd3-8b6e-55552f4036ff" width="100%"></video>
|
||||
|
||||
Integrated Inspector + MCP client basic functions, combining development and testing into one.
|
||||
<video src="https://github.com/user-attachments/assets/c17a4ad7-83b4-47ff-8627-85b57ad18940" width="100%"></video>
|
||||
|
||||
|
||||
集成 Inspector + MCP 客户端基础功能,开发测试一体化。
|
||||
|
||||

|
||||
|
||||
Test mcp tools, prompts and resources with a variety of tools.
|
||||
进行资源协议、工具、Prompt 的 MCP 服务器测试。
|
||||
|
||||

|
||||
|
||||
Tested tools can be placed in the "Interactive Testing" module for large model interaction testing.
|
||||
测试完成的工具可以放入 「交互测试」 模块之间进行大模型交互测试。
|
||||
|
||||

|
||||
|
||||
Complete project-level management panel for easier MCP project management at both project and global levels.
|
||||
完整的项目级管理面板,更加方便的进行项目和全局的 mcp 项目管理。
|
||||
|
||||

|
||||
|
||||
Supports multiple large models
|
||||
支持多种大模型
|
||||
|
||||

|
||||
|
||||
Support XML mode and customized options for your tool selection.
|
||||
|
||||

|
||||
|
||||
## TODO
|
||||
|
||||
## Feature Roadmap
|
||||
## 需求规划
|
||||
|
||||
| Module | Feature | Priority | Status | Fix Priority |
|
||||
| 所在模块 | 需求内容 | 功能优先级 | 当前状态 | 修复优先级 |
|
||||
|---------|---------|--------|---------|-----------|
|
||||
| `all` | Complete basic infrastructure | `Full Version` | 100% | `Done` |
|
||||
| `render` | Support cost analysis in chat mode | `Iteration` | 100% | `Done` |
|
||||
| `ext` | Support basic MCP project management | `Iteration` | 100% | `P0` |
|
||||
| `service` | Support custom OpenAI-compatible large model integration | `Full Version` | 100% | `Done` |
|
||||
| `service` | Support custom protocol large model integration | `MVP` | 0% | `P1` |
|
||||
| `all` | Support debugging multiple MCP Servers simultaneously | `MVP` | 100% | `P0` |
|
||||
| `all` | Support online verification via large models | `Iteration` | 100% | `Done` |
|
||||
| `all` | Support saving user's server debugging work | `Iteration` | 100% | `Done` |
|
||||
| `render` | High-risk operation permission confirmation | `MVP` | 0% | `P1` |
|
||||
| `service` | Hot update for connected MCP servers | `MVP` | 0% | `P1` |
|
||||
| `service` | Cloud sync for system configuration | `MVP` | 0% | `P1` |
|
||||
| `all` | System prompt management module | `Iteration` | 100% | `Done` |
|
||||
| `service` | Tool-wise logging system | `MVP` | 0% | `P1` |
|
||||
| `service` | MCP security checks (prevent prompt injection, etc.) | `MVP` | 0% | `P1` |
|
||||
| `service` | Built-in OCR for character recognition | `Iteration` | 100% | `Done` |
|
||||
| `all` | 完成最基本的各类基础设施 | `完整版本` | 100% | `Done` |
|
||||
| `render` | chat 模式下支持进行成本分析 | `迭代版本` | 100% | `Done` |
|
||||
| `ext` | 支持基本的 MCP 项目管理 | `迭代版本` | 100% | `P0` |
|
||||
| `service` | 支持自定义支持 openai 接口协议的大模型接入 | `完整版本` | 100% | `Done` |
|
||||
| `service` | 支持自定义接口协议的大模型接入 | `MVP` | 0% | `P1` |
|
||||
| `all` | 支持同时调试多个 MCP Server | `MVP` | 0% | `P1` |
|
||||
| `all` | 支持通过大模型进行在线验证 | `迭代版本` | 100% | `Done` |
|
||||
| `all` | 支持对用户对应服务器的调试工作内容进行保存 | `迭代版本` | 100% | `Done` |
|
||||
| `render` | 高危操作权限确认 | `MVP` | 0% | `P1` |
|
||||
| `service` | 对于连接的 mcp server 进行热更新 | `MVP` | 0% | `P1` |
|
||||
| `service` | 系统配置信息云同步 | `MVP` | 0% | `P1` |
|
||||
| `all` | 系统提示词管理模块 | `迭代版本` | 100% | `Done` |
|
||||
| `service` | 工具 wise 的日志系统 | `MVP` | 0% | `P1` |
|
||||
| `service` | 自带 OCR 进行字符识别 | `迭代版本` | 100% | `Done` |
|
||||
|
||||
## Project Concept
|
||||
|
||||
OpenMCP adopts a layered modular design. By assembling different modules, it can be implemented in different modes on different platforms.
|
||||
## 项目概念
|
||||
|
||||
openmcp 采用分层模块化设计,通过组装不同的模块,可以将它实现成不同平台上的不同模式。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph OpenMCP Core Components
|
||||
subgraph OpenMCP核心组件
|
||||
renderer[Renderer]
|
||||
openmcpservice[OpenMCPService]
|
||||
end
|
||||
@ -84,92 +82,85 @@ flowchart TD
|
||||
nginx[Nginx]
|
||||
end
|
||||
|
||||
subgraph OpenMCP_Plugin["OpenMCP Plugin"]
|
||||
subgraph OpenMCP_插件["OpenMCP 插件"]
|
||||
renderer
|
||||
openmcpservice
|
||||
vscode[VSCode Plugin Code]
|
||||
vscode[VSCode 插件代码]
|
||||
end
|
||||
|
||||
subgraph OpenMCP_App["OpenMCP App"]
|
||||
renderer
|
||||
openmcpservice
|
||||
electron[Electron Code]
|
||||
electron[Electron 代码]
|
||||
end
|
||||
|
||||
subgraph QQBot["OpenMCP-based QQ Bot"]
|
||||
subgraph QQ机器人["基于 OpenMCP 的 QQ 机器人"]
|
||||
lagrange[Lagrange.OneBot]
|
||||
openmcpservice
|
||||
end
|
||||
|
||||
%% Dependencies
|
||||
OpenMCP_Web -->|Frontend Rendering| renderer
|
||||
OpenMCP_Web -->|Backend Service| openmcpservice
|
||||
OpenMCP_Web -->|Reverse Proxy| nginx
|
||||
%% 依赖关系
|
||||
OpenMCP_Web -->|前端渲染| renderer
|
||||
OpenMCP_Web -->|后端服务| openmcpservice
|
||||
OpenMCP_Web -->|反向代理| nginx
|
||||
|
||||
OpenMCP_Plugin -->|UI Interface| renderer
|
||||
OpenMCP_Plugin -->|Core Logic| openmcpservice
|
||||
OpenMCP_Plugin -->|IDE Integration| vscode
|
||||
OpenMCP_插件 -->|UI 界面| renderer
|
||||
OpenMCP_插件 -->|核心逻辑| openmcpservice
|
||||
OpenMCP_插件 -->|集成开发| vscode
|
||||
|
||||
OpenMCP_App -->|Frontend UI| renderer
|
||||
OpenMCP_App -->|Local Service| openmcpservice
|
||||
OpenMCP_App -->|Desktop Packaging| electron
|
||||
OpenMCP_App -->|前端界面| renderer
|
||||
OpenMCP_App -->|本地服务| openmcpservice
|
||||
OpenMCP_App -->|桌面封装| electron
|
||||
|
||||
QQBot -->|Protocol Adaptation| lagrange
|
||||
QQBot -->|Business Logic| openmcpservice
|
||||
QQ机器人 -->|协议适配| lagrange
|
||||
QQ机器人 -->|业务逻辑| openmcpservice
|
||||
```
|
||||
|
||||
## Development
|
||||
- renderer : Frontend UI definitions
|
||||
- service : Test components for renderer , including a simple forwarding layer
|
||||
- src : VSCode plugin definitions
|
||||
|
||||
### Renderer & Service Development
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[Dev Server]
|
||||
<--ws--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
Project setup:
|
||||
|
||||
```bash
|
||||
npm run setup
|
||||
```
|
||||
Start dev server:
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Extension Development
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[extention.ts] <--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
|
||||
Build for deployment:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
build vscode extension:
|
||||
|
||||
```bash
|
||||
npm run build:plugin
|
||||
```
|
||||
|
||||
Then just press F5, いただきます (Let's begin)
|
||||
|
||||
---
|
||||
|
||||
## CI Pipeline
|
||||
## Dev
|
||||
|
||||
✅ npm run build
|
||||
✅ npm run build:task-loop
|
||||
✅ openmcp-client UT
|
||||
✅ openmcp-sdk UT
|
||||
✅ vscode extension UT
|
||||
- `renderer`: 前端 UI 的定义
|
||||
- `service`: 测试 `renderer` 的部分,包含一个简易的转发层
|
||||
- `src`: vscode 插件端定义
|
||||
|
||||
### Renderer & Service Dev
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[Dev Server] <--ws--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
|
||||
配置项目
|
||||
|
||||
```bash
|
||||
## linux
|
||||
./configure.sh
|
||||
## windows
|
||||
./configure.ps1
|
||||
```
|
||||
|
||||
启动 dev server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
> 端口占用: 8282 (renderer) + 8081 (service)
|
||||
|
||||
### Extention Dev
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[extention.ts] <--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
|
||||
负载部署
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
and just press f5, いただきます
|
||||
|
166
README.zh.md
@ -1,166 +0,0 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="./icons/openmcp.png" height="200px"/>
|
||||
|
||||
<h3>OpenMCP: 一体化 MCP Server 调试器</h3>
|
||||
|
||||
[English](./README.md) | 中文
|
||||
|
||||
<a href="https://openmcp.kirigaya.cn" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;"> 🫱 官方文档</a>
|
||||
|
||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: #CB81DA; color: white; border-radius: .5em; text-decoration: none;">OpenMCP QQ 讨论群</a>
|
||||
|
||||
<a href="https://discord.gg/SKTZRf6NzU" target="_blank" style="display: inline-block; padding: 8px 16px; background-color: rgb(84, 176, 84); color: white; border-radius: .5em; text-decoration: none;">OpenMCP Discord 频道</a>
|
||||
|
||||
</div>
|
||||
|
||||
## OpenMCP
|
||||
|
||||
一款用于 MCP 服务端调试的一体化 vscode/trae/cursor 插件。
|
||||
|
||||
[](https://www.bilibili.com/video/BV1MFTBzpEtZ/?vd_source=3f248073d6ebdb61308992901b606f24)
|
||||
### [👆 完整视频](https://www.youtube.com/watch?v=S7igsEhcLiw)
|
||||
|
||||
集成 Inspector + MCP 客户端基础功能,开发测试一体化。
|
||||
|
||||

|
||||
|
||||
进行资源协议、工具、Prompt 的 MCP 服务器测试。
|
||||
|
||||

|
||||
|
||||
测试完成的工具可以放入 「交互测试」 模块之间进行大模型交互测试。
|
||||
|
||||

|
||||
|
||||
完整的项目级管理面板,更加方便的进行项目和全局的 mcp 项目管理。
|
||||
|
||||

|
||||
|
||||
支持多种大模型
|
||||
|
||||

|
||||
|
||||
支持 XML 模式和自定义工具选择
|
||||
|
||||

|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
## 需求规划
|
||||
|
||||
| 所在模块 | 需求内容 | 功能优先级 | 当前状态 | 修复优先级 |
|
||||
|---------|---------|--------|---------|-----------|
|
||||
| `all` | 完成最基本的各类基础设施 | `完整版本` | 100% | `Done` |
|
||||
| `render` | chat 模式下支持进行成本分析 | `迭代版本` | 100% | `Done` |
|
||||
| `ext` | 支持基本的 MCP 项目管理 | `迭代版本` | 100% | `P0` |
|
||||
| `service` | 支持自定义支持 openai 接口协议的大模型接入 | `完整版本` | 100% | `Done` |
|
||||
| `service` | 支持自定义接口协议的大模型接入 | `MVP` | 0% | `P1` |
|
||||
| `all` | 支持同时调试多个 MCP Server | `MVP` | 100% | `P0` |
|
||||
| `all` | 支持通过大模型进行在线验证 | `迭代版本` | 100% | `Done` |
|
||||
| `all` | 支持对用户对应服务器的调试工作内容进行保存 | `迭代版本` | 100% | `Done` |
|
||||
| `render` | 高危操作权限确认 | `MVP` | 0% | `P1` |
|
||||
| `service` | 对于连接的 mcp server 进行热更新 | `MVP` | 0% | `P1` |
|
||||
| `service` | 系统配置信息云同步 | `MVP` | 0% | `P1` |
|
||||
| `all` | 系统提示词管理模块 | `迭代版本` | 100% | `Done` |
|
||||
| `service` | 工具 wise 的日志系统 | `MVP` | 0% | `P1` |
|
||||
| `service` | 自带 OCR 进行字符识别 | `迭代版本` | 100% | `Done` |
|
||||
|
||||
|
||||
## 项目概念
|
||||
|
||||
openmcp 采用分层模块化设计,通过组装不同的模块,可以将它实现成不同平台上的不同模式。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph OpenMCP核心组件
|
||||
renderer[Renderer]
|
||||
openmcpservice[OpenMCPService]
|
||||
end
|
||||
|
||||
subgraph OpenMCP_Web["OpenMCP Web"]
|
||||
renderer
|
||||
openmcpservice
|
||||
nginx[Nginx]
|
||||
end
|
||||
|
||||
subgraph OpenMCP_插件["OpenMCP 插件"]
|
||||
renderer
|
||||
openmcpservice
|
||||
vscode[VSCode 插件代码]
|
||||
end
|
||||
|
||||
subgraph OpenMCP_App["OpenMCP App"]
|
||||
renderer
|
||||
openmcpservice
|
||||
electron[Electron 代码]
|
||||
end
|
||||
|
||||
subgraph QQ机器人["基于 OpenMCP 的 QQ 机器人"]
|
||||
lagrange[Lagrange.OneBot]
|
||||
openmcpservice
|
||||
end
|
||||
|
||||
%% 依赖关系
|
||||
OpenMCP_Web -->|前端渲染| renderer
|
||||
OpenMCP_Web -->|后端服务| openmcpservice
|
||||
OpenMCP_Web -->|反向代理| nginx
|
||||
|
||||
OpenMCP_插件 -->|UI 界面| renderer
|
||||
OpenMCP_插件 -->|核心逻辑| openmcpservice
|
||||
OpenMCP_插件 -->|集成开发| vscode
|
||||
|
||||
OpenMCP_App -->|前端界面| renderer
|
||||
OpenMCP_App -->|本地服务| openmcpservice
|
||||
OpenMCP_App -->|桌面封装| electron
|
||||
|
||||
QQ机器人 -->|协议适配| lagrange
|
||||
QQ机器人 -->|业务逻辑| openmcpservice
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Dev
|
||||
|
||||
- `renderer`: 前端 UI 的定义
|
||||
- `service`: 测试 `renderer` 的部分,包含一个简易的转发层
|
||||
- `src`: vscode 插件端定义
|
||||
|
||||
### Renderer & Service Dev
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[Dev Server] <--ws--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
|
||||
配置项目
|
||||
|
||||
```bash
|
||||
npm run setup
|
||||
```
|
||||
|
||||
启动 dev server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
> 端口占用: 8282 (renderer) + 8081 (service)
|
||||
|
||||
### Extention Dev
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D[renderer] <--> A[extention.ts] <--> B[service]
|
||||
B <--mcp--> m(MCP Server)
|
||||
```
|
||||
|
||||
负载部署
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
and just press f5, いただきます
|
38
build_service.ps1
Normal file
@ -0,0 +1,38 @@
|
||||
# 创建并清理资源目录
|
||||
New-Item -ItemType Directory -Path ./openmcp-sdk -Force
|
||||
Remove-Item -Recurse -Force ./openmcp-sdk/* -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Path ./openmcp-sdk -Force
|
||||
|
||||
# 获取当前工作目录的绝对路径
|
||||
$currentDir = (Get-Location).Path
|
||||
|
||||
# 并行构建 renderer 和 service
|
||||
$rendererJob = Start-Job -ScriptBlock {
|
||||
param($workDir)
|
||||
Set-Location -Path "$workDir\renderer"
|
||||
npm run build
|
||||
Move-Item -Path "./dist" -Destination "$workDir\openmcp-sdk\renderer" -Force
|
||||
} -ArgumentList $currentDir
|
||||
|
||||
$serviceJob = Start-Job -ScriptBlock {
|
||||
param($workDir)
|
||||
Set-Location -Path "$workDir\service"
|
||||
npm run build
|
||||
Move-Item -Path "./dist" -Destination "$workDir\openmcp-sdk\service" -Force
|
||||
} -ArgumentList $currentDir
|
||||
|
||||
# 等待任务完成
|
||||
$rendererJob | Wait-Job | Receive-Job
|
||||
$serviceJob | Wait-Job | Receive-Job
|
||||
|
||||
# 将 openmcp-sdk 目录复制到 software/openmcp-sdk
|
||||
New-Item -ItemType Directory -Path ./software/openmcp-sdk -Force
|
||||
Remove-Item -Recurse -Force ./software/openmcp-sdk/* -ErrorAction SilentlyContinue
|
||||
Copy-Item -Recurse -Path ./openmcp-sdk -Destination ./software/ -Force
|
||||
|
||||
$serviceJob = Start-Job -ScriptBlock {
|
||||
param($workDir)
|
||||
npm run build:task-loop
|
||||
} -ArgumentList $currentDir
|
||||
|
||||
Write-Output "finish building services in ./openmcp-sdk"
|
18
build_service.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p ./openmcp-sdk
|
||||
rm -rf ./openmcp-sdk/
|
||||
mkdir -p ./openmcp-sdk
|
||||
|
||||
(cd ./renderer && npm run build && mv ./dist ../openmcp-sdk/renderer) &
|
||||
(cd ./service && npm run build && mv ./dist ../openmcp-sdk/service) &
|
||||
|
||||
wait
|
||||
|
||||
mkdir -p ./software/openmcp-sdk
|
||||
rm -rf ./software/openmcp-sdk
|
||||
cp -r ./openmcp-sdk ./software/
|
||||
|
||||
npm run build:task-loop
|
||||
|
||||
echo "finish building services in ./openmcp-sdk"
|
19
configure.ps1
Normal file
@ -0,0 +1,19 @@
|
||||
## 安装 renderer 依赖
|
||||
#Set-Location renderer
|
||||
#npm i
|
||||
#Set-Location ..
|
||||
#
|
||||
## 安装 service 依赖并打补丁
|
||||
#Set-Location service
|
||||
#npm i
|
||||
#node patch-mcp-sdk.js
|
||||
#Set-Location ..
|
||||
|
||||
Set-Location servers
|
||||
uv sync
|
||||
Set-Location ..
|
||||
|
||||
# 安装根目录依赖
|
||||
npm i
|
||||
|
||||
npm run prepare:ocr
|
3
configure.sh
Executable file
@ -0,0 +1,3 @@
|
||||
cd servers && uv sync && cd ..
|
||||
npm i
|
||||
npm run prepare:ocr
|
7
dev.ps1
Normal file
@ -0,0 +1,7 @@
|
||||
npx concurrently `
|
||||
-n "renderer,service" `
|
||||
-p " {name} " `
|
||||
-c "black.bgBlue,black.bgGreen" `
|
||||
--kill-others `
|
||||
"cd renderer && npm run serve" `
|
||||
"cd service && npm run serve"
|
9
dev.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
npx concurrently \
|
||||
-n "renderer,service" \
|
||||
-p " {name} " \
|
||||
-c "black.bgBlue,black.bgGreen" \
|
||||
--kill-others \
|
||||
"cd renderer && npm run serve" \
|
||||
"cd service && npm run serve"
|
@ -1,20 +0,0 @@
|
||||
// esbuild.config.js
|
||||
const { build } = require('esbuild');
|
||||
|
||||
build({
|
||||
entryPoints: ['src/extension.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/extension.cjs.js',
|
||||
sourcemap: true,
|
||||
external: ['vscode'],
|
||||
target: ['node22'],
|
||||
loader: {
|
||||
'.json': 'json'
|
||||
},
|
||||
define: { 'import.meta.url': '_importMetaUrl' },
|
||||
banner: {
|
||||
js: "const _importMetaUrl=require('url').pathToFileURL(__filename)",
|
||||
},
|
||||
}).catch(() => process.exit(1));
|
Before Width: | Height: | Size: 453 KiB After Width: | Height: | Size: 543 KiB |
Before Width: | Height: | Size: 675 KiB After Width: | Height: | Size: 496 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 564 KiB After Width: | Height: | Size: 492 KiB |
Before Width: | Height: | Size: 446 KiB After Width: | Height: | Size: 435 KiB |
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 402 KiB |
Before Width: | Height: | Size: 560 KiB |
@ -1,20 +0,0 @@
|
||||
{
|
||||
"ensure-delete-connection": "Are you sure you want to delete connection {0}?",
|
||||
"confirm": "OK",
|
||||
"choose-connection-type": "Please select the connection type",
|
||||
"please-enter-connection-command": "Please enter the connection command",
|
||||
"example-mcp-run": "For example: mcp run main.py",
|
||||
"please-enter-cwd": "Please enter the working directory (cwd), optional",
|
||||
"please-enter-cwd-placeholder": "For example: /path/to/project",
|
||||
"please-enter-url": "Please enter the connection URL",
|
||||
"example-as": "For example:",
|
||||
"enter-optional-oauth": "Please enter the OAuth token, optional",
|
||||
"quick-start": "Getting Started",
|
||||
"read-document": "Read documentation",
|
||||
"report-issue": "Report a problem",
|
||||
"join-project": "Participate in the project",
|
||||
"comment-plugin": "Comment Plugin",
|
||||
"preset-env-sync": "Preset environment variables synchronized successfully",
|
||||
"preset-env-sync.fail": "Failed to sync preset environment variables",
|
||||
"error.notOpenWorkspace": "No workspace is currently open in VSCode. Please open a workspace (e.g., open a folder) first."
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"ensure-delete-connection": "接続 {0} を削除してもよろしいですか?",
|
||||
"confirm": "確定",
|
||||
"choose-connection-type": "接続タイプを選択してください",
|
||||
"please-enter-connection-command": "接続コマンドを入力してください",
|
||||
"example-mcp-run": "例: mcp run main.py",
|
||||
"please-enter-cwd": "作業ディレクトリ (cwd) を入力してください (オプション)",
|
||||
"please-enter-cwd-placeholder": "例: /path/to/project",
|
||||
"please-enter-url": "接続URLを入力してください",
|
||||
"example-as": "例えば:",
|
||||
"enter-optional-oauth": "OAuthトークンを入力してください(任意)",
|
||||
"quick-start": "はじめに",
|
||||
"read-document": "ドキュメントを読む",
|
||||
"report-issue": "問題を報告",
|
||||
"join-project": "プロジェクトに参加する",
|
||||
"comment-plugin": "コメントプラグイン",
|
||||
"preset-env-sync": "プリセット環境変数の同期が完了しました",
|
||||
"preset-env-sync.fail": "プリセット環境変数の同期に失敗しました",
|
||||
"error.notOpenWorkspace": "現在、VSCode でワークスペースが開かれていません。まずワークスペース(例:フォルダーを開く)を開いてください。"
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"ensure-delete-connection": "确定要删除连接 {0} 吗?",
|
||||
"confirm": "确定",
|
||||
"choose-connection-type": "请选择连接类型",
|
||||
"please-enter-connection-command": "请输入连接的 command",
|
||||
"example-mcp-run": "例如: mcp run main.py",
|
||||
"please-enter-cwd": "请输入工作目录 (cwd),可选",
|
||||
"please-enter-cwd-placeholder": "例如: /path/to/project",
|
||||
"please-enter-url": "请输入连接的 URL",
|
||||
"example-as": "例如:",
|
||||
"enter-optional-oauth": "请输入 OAuth 令牌,可选",
|
||||
"quick-start": "入门",
|
||||
"read-document": "阅读文档",
|
||||
"report-issue": "报告问题",
|
||||
"join-project": "参与项目",
|
||||
"comment-plugin": "评论插件",
|
||||
"preset-env-sync": "预设环境变量同步完成",
|
||||
"preset-env-sync.fail": "预设环境变量同步失败",
|
||||
"error.notOpenWorkspace": "当前VScode没有打开工作区,请先打开工作区(例如打开文件夹)"
|
||||
}
|
31
news/.gitignore
vendored
@ -1,31 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
./src/data.json
|
3
news/.vscode/extensions.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
183
news/README.md
@ -1,183 +0,0 @@
|
||||
## OpenMCP News
|
||||
|
||||
Change when user update extension
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
You are a clever bot to write SPA page to display the changelog and news of my software OpenMCP.
|
||||
|
||||
### Some basic information you should know
|
||||
|
||||
- Release is: https://github.com/LSTM-Kirigaya/openmcp-client/releases
|
||||
- OpenMCP website is: https://openmcp.kirigaya.cn/
|
||||
- Github: https://github.com/LSTM-Kirigaya/openmcp-client
|
||||
- Discord: https://discord.com/invite/SKTZRf6NzU
|
||||
- QQ: https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD
|
||||
|
||||
### Design framework
|
||||
|
||||
You should obey the following order to make the page:
|
||||
|
||||
1. 📣 What is news in xxx
|
||||
2. 🐳 Learn more features
|
||||
3. ✨ Core Features
|
||||
4. ❤️ How to sponsor
|
||||
5. 📚 Resources
|
||||
6. 🔧 Troubleshooting
|
||||
|
||||
### Some design requirements
|
||||
|
||||
- Theme color of openmcp is #B988D1, you should use it wisely and don't use light blue as default theme color, use #B988D1 instead.
|
||||
- This is a website designed to be opened in vscode, so you should make vscode oriented design, where you should use following css macro to dev css class:
|
||||
```css
|
||||
:root {
|
||||
--font-monospace-family: var(--vscode-editor-font-family);
|
||||
--font-monospace-weight: var(--vscode-editor-font-weight);
|
||||
--font-monospace-size: var(--vscode-editor-font-size);
|
||||
|
||||
--link-foreground: var(--vscode-textLink-foreground);
|
||||
--link-active: var(--vscode-textLink-activeForeground);
|
||||
|
||||
/* UI & Control */
|
||||
--input-active-background: var(--vscode-input-background);
|
||||
--input-active-border: var(--vscode-focusBorder);
|
||||
--input-active-foreground: var(--vscode-input-foreground);
|
||||
|
||||
--input-error-background: var(--vscode-inputValidation-errorBackground);
|
||||
--input-error-border: var(--vscode-inputValidation-errorBorder);
|
||||
--input-error-foreground: var(--vscode-inputValidation-errorForeground);
|
||||
|
||||
--input-foreground: var(--vscode-input-foreground);
|
||||
--input-background: var(--vscode-input-background);
|
||||
--input-border: var(--vscode-input-border);
|
||||
--input-hover: var(--vscode-input-background);
|
||||
--input-placeholder: var(--vscode-input-placeholderForeground);
|
||||
--input-radius: 0px;
|
||||
|
||||
--scrollbar-background: var(--vscode-scrollbarSlider-background);
|
||||
--scrollbar-hover: var(--vscode-scrollbarSlider-hoverBackground);
|
||||
--scrollbar-active: var(--vscode-scrollbarSlider-activeBackground);
|
||||
|
||||
/* Window */
|
||||
--title-bar: #1f1f1f;
|
||||
--title-color: #fff;
|
||||
--foreground: var(--vscode-editor-foreground);
|
||||
--background: var(--vscode-editor-background);
|
||||
--label: rgb(189, 189, 189);
|
||||
--shadow: #000;
|
||||
--border: var(--vscode-input-border);
|
||||
--window-button-hover: rgba(255,255,255,0.1);
|
||||
--window-button-active: rgba(255,255,255,0.2);
|
||||
--window-blur-background: rgba(0,0,0,0.25);
|
||||
|
||||
--window-title-foreground: var(--foreground);
|
||||
--window-background: var(--sidebar);
|
||||
--window-border: transparent;
|
||||
--window-radius: 0px;
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar: var(--vscode-sideBar-background);
|
||||
--sidebar-border: var(--vscode-sideBar-border);
|
||||
--sidebar-min-width: 280px;
|
||||
|
||||
--sidebar-item-text: var(--vscode-list-inactiveSelectionForeground);
|
||||
--sidebar-item-border: var(--vscode-input-border);
|
||||
--sidebar-item-background: var(--sidebar);
|
||||
--sidebar-item-selected: var(--vscode-list-inactiveSelectionBackground);
|
||||
--sidebar-item-hover: var(--vscode-list-hoverBackground);
|
||||
--sidebar-item-max-height: 40px;
|
||||
--sidebar-item-radix-background: var(--vscode-breadcrumb-background);
|
||||
|
||||
--sidebar-group-text: var(--vscode-sideBarSectionHeader-foreground);
|
||||
--sidebar-group-border: var(--vscode-sideBarSectionHeader-border);
|
||||
--sidebar-group-background: var(--vscode-sideBarSectionHeader-background);
|
||||
|
||||
/* Labels */
|
||||
--signalSize-background: rgba(0,0,0,0.5);
|
||||
--signalSize-border: rgba(255,255,255,0.2);
|
||||
--signalSize-color: var(--foreground);
|
||||
|
||||
/* Color Picker */
|
||||
--picker-swatch-size: 15px;
|
||||
--picker-swatch-cols: 8;
|
||||
--picker-background: var(--vscode-breadcrumbPicker-background);
|
||||
--picker-border: var(--vscode-dropdown-border);
|
||||
|
||||
/* Search */
|
||||
--search-background: var(--vscode-quickInput-background);
|
||||
--search-border: var(--border);
|
||||
--search-panel-background: transparent;
|
||||
--search-panel-border: var(--vscode-pickerGroup-border);
|
||||
--search-panel-text: var(--vscode-quickInput-foreground);
|
||||
--search-label: var(--foreground);
|
||||
--search-selected-background: var(--vscode-list-inactiveSelectionBackground);
|
||||
|
||||
/* Properties */
|
||||
--properties-background: var(--vscode-breadcrumb-background);
|
||||
--properties-border: var(--border);
|
||||
|
||||
/* Navbar */
|
||||
--navBar-background: var(--sidebar);
|
||||
--navBar-height: 32px;
|
||||
--navBar-button: transparent;
|
||||
--navBar-button-text: var(--foreground);
|
||||
--navBar-group-background: var(--background);
|
||||
--navBar-preview-background: var(--vscode-scrollbarSlider-background);
|
||||
--navBar-slider-border: var(--foreground);
|
||||
|
||||
/* Buttons */
|
||||
--button: var(--vscode-button-background);
|
||||
--button-text: var(--vscode-button-foreground);
|
||||
--button-hover: var(--vscode-button-hoverBackground);
|
||||
--button-active: var(--vscode-button-hoverBackground);
|
||||
--button-disabled: var(--vscode-activityBar-background);
|
||||
--button-disabled-text: var(--vscode-activityBar-inactiveForeground);
|
||||
|
||||
/* Grid Lines */
|
||||
--grid-dash: 2;
|
||||
--grid-space: 4;
|
||||
--grid-line: var(--vscode-editorIndentGuide-background);
|
||||
--grid-tick: var(--vscode-editorIndentGuide-activeBackground);
|
||||
|
||||
/* Cursor */
|
||||
--cursor: var(--vscode-editorCursor-foreground);
|
||||
--cursor-ghost: rgba(255, 255, 255, 0.2);
|
||||
--cursor-width: 2;
|
||||
|
||||
/* X-Axis */
|
||||
--axis-height: 38px;
|
||||
--axis-line: var(--border);
|
||||
--axis-background: var(--vscode-sideBar-background);
|
||||
--axis-foreground: var(--foreground);
|
||||
|
||||
/* Signals */
|
||||
--signal-highlight: var(--vscode-list-inactiveSelectionBackground);
|
||||
|
||||
/* Colors */
|
||||
--accent: var(--vscode-button-background);
|
||||
--accent-dim: #234175;
|
||||
--accent-bright: #24c5f7;
|
||||
--accent-hover: var(--vscode-button-hoverBackground);
|
||||
|
||||
--color-red: #ff5252;
|
||||
--color-pink: #ff4081;
|
||||
--color-purple: #e040fb;
|
||||
--color-deepPurple: #7c4dff;
|
||||
--color-indigo: #536dfe;
|
||||
--color-blue: #448aff;
|
||||
--color-lightBlue: #40c4ff;
|
||||
--color-cyan: #18ffff;
|
||||
--color-teal: #64ffda;
|
||||
--color-green: #69f0ae;
|
||||
--color-lightGreen: #b2ff59;
|
||||
--color-lime: #eeff41;
|
||||
--color-yellow: #ffff00;
|
||||
--color-amber: #ffd740;
|
||||
--color-orange: #ffab40;
|
||||
--color-deepOrange: #ff6e40;
|
||||
|
||||
/* Settings */
|
||||
--settings-action-background: var(--background);
|
||||
}
|
||||
```
|
6
news/env.d.ts
vendored
@ -1,6 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="/default-dark.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OpenMCP News Feature</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
<style>
|
||||
.openmcp-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background: url('https://openmcp.kirigaya.cn/images/favicon.svg') no-repeat center/contain;
|
||||
}
|
||||
</style>
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "news",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"vanilla-tilt": "^1.8.1",
|
||||
"vue": "^3.5.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@types/node": "^22.15.32",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"typescript": "~5.8.0",
|
||||
"vite": "^7.0.0",
|
||||
"vite-plugin-singlefile": "^2.3.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.7",
|
||||
"vue-tsc": "^2.2.10"
|
||||
}
|
||||
}
|
@ -1,772 +0,0 @@
|
||||
:root {
|
||||
--vscode-foreground: #cccccc;
|
||||
--vscode-disabledForeground: rgba(204, 204, 204, 0.5);
|
||||
--vscode-errorForeground: #f48771;
|
||||
--vscode-descriptionForeground: rgba(204, 204, 204, 0.7);
|
||||
--vscode-icon-foreground: #c5c5c5;
|
||||
--vscode-focusBorder: #007fd4;
|
||||
--vscode-textLink-foreground: #3794ff;
|
||||
--vscode-textLink-activeForeground: #3794ff;
|
||||
--vscode-textSeparator-foreground: rgba(255, 255, 255, 0.18);
|
||||
--vscode-textPreformat-foreground: #d7ba7d;
|
||||
--vscode-textPreformat-background: rgba(255, 255, 255, 0.1);
|
||||
--vscode-textBlockQuote-background: #222222;
|
||||
--vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5);
|
||||
--vscode-textCodeBlock-background: rgba(10, 10, 10, 0.4);
|
||||
--vscode-sash-hoverBorder: #007fd4;
|
||||
--vscode-badge-background: #4d4d4d;
|
||||
--vscode-badge-foreground: #ffffff;
|
||||
--vscode-scrollbar-shadow: #000000;
|
||||
--vscode-scrollbarSlider-background: rgba(121, 121, 121, 0.4);
|
||||
--vscode-scrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
|
||||
--vscode-scrollbarSlider-activeBackground: rgba(191, 191, 191, 0.4);
|
||||
--vscode-progressBar-background: #0e70c0;
|
||||
--vscode-editor-background: #1e1e1e;
|
||||
--vscode-editor-foreground: #d4d4d4;
|
||||
--vscode-editorStickyScroll-background: #1e1e1e;
|
||||
--vscode-editorStickyScrollHover-background: #2a2d2e;
|
||||
--vscode-editorStickyScroll-shadow: #000000;
|
||||
--vscode-editorWidget-background: #252526;
|
||||
--vscode-editorWidget-foreground: #cccccc;
|
||||
--vscode-editorWidget-border: #454545;
|
||||
--vscode-editorError-foreground: #f14c4c;
|
||||
--vscode-editorWarning-foreground: #cca700;
|
||||
--vscode-editorInfo-foreground: #3794ff;
|
||||
--vscode-editorHint-foreground: rgba(238, 238, 238, 0.7);
|
||||
--vscode-editorLink-activeForeground: #4e94ce;
|
||||
--vscode-editor-selectionBackground: #264f78;
|
||||
--vscode-editor-inactiveSelectionBackground: #3a3d41;
|
||||
--vscode-editor-selectionHighlightBackground: rgba(173, 214, 255, 0.15);
|
||||
--vscode-editor-findMatchBackground: #515c6a;
|
||||
--vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
|
||||
--vscode-editor-findRangeHighlightBackground: rgba(58, 61, 65, 0.4);
|
||||
--vscode-editor-hoverHighlightBackground: rgba(38, 79, 120, 0.25);
|
||||
--vscode-editorHoverWidget-background: #252526;
|
||||
--vscode-editorHoverWidget-foreground: #cccccc;
|
||||
--vscode-editorHoverWidget-border: #454545;
|
||||
--vscode-editorHoverWidget-statusBarBackground: #2c2c2d;
|
||||
--vscode-editorInlayHint-foreground: #969696;
|
||||
--vscode-editorInlayHint-background: rgba(77, 77, 77, 0.1);
|
||||
--vscode-editorInlayHint-typeForeground: #969696;
|
||||
--vscode-editorInlayHint-typeBackground: rgba(77, 77, 77, 0.1);
|
||||
--vscode-editorInlayHint-parameterForeground: #969696;
|
||||
--vscode-editorInlayHint-parameterBackground: rgba(77, 77, 77, 0.1);
|
||||
--vscode-editorLightBulb-foreground: #ffcc00;
|
||||
--vscode-editorLightBulbAutoFix-foreground: #75beff;
|
||||
--vscode-editorLightBulbAi-foreground: #ffcc00;
|
||||
--vscode-editor-snippetTabstopHighlightBackground: rgba(124, 124, 124, 0.3);
|
||||
--vscode-editor-snippetFinalTabstopHighlightBorder: #525252;
|
||||
--vscode-diffEditor-insertedTextBackground: rgba(156, 204, 44, 0.2);
|
||||
--vscode-diffEditor-removedTextBackground: rgba(255, 0, 0, 0.2);
|
||||
--vscode-diffEditor-insertedLineBackground: rgba(155, 185, 85, 0.2);
|
||||
--vscode-diffEditor-removedLineBackground: rgba(255, 0, 0, 0.2);
|
||||
--vscode-diffEditor-diagonalFill: rgba(204, 204, 204, 0.2);
|
||||
--vscode-diffEditor-unchangedRegionBackground: #252526;
|
||||
--vscode-diffEditor-unchangedRegionForeground: #cccccc;
|
||||
--vscode-diffEditor-unchangedCodeBackground: rgba(116, 116, 116, 0.16);
|
||||
--vscode-widget-shadow: rgba(0, 0, 0, 0.36);
|
||||
--vscode-widget-border: #303031;
|
||||
--vscode-toolbar-hoverBackground: rgba(90, 93, 94, 0.31);
|
||||
--vscode-toolbar-activeBackground: rgba(99, 102, 103, 0.31);
|
||||
--vscode-breadcrumb-foreground: rgba(204, 204, 204, 0.8);
|
||||
--vscode-breadcrumb-background: #1e1e1e;
|
||||
--vscode-breadcrumb-focusForeground: #e0e0e0;
|
||||
--vscode-breadcrumb-activeSelectionForeground: #e0e0e0;
|
||||
--vscode-breadcrumbPicker-background: #252526;
|
||||
--vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5);
|
||||
--vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2);
|
||||
--vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5);
|
||||
--vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2);
|
||||
--vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4);
|
||||
--vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16);
|
||||
--vscode-editorOverviewRuler-currentContentForeground: rgba(64, 200, 174, 0.5);
|
||||
--vscode-editorOverviewRuler-incomingContentForeground: rgba(64, 166, 255, 0.5);
|
||||
--vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4);
|
||||
--vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49);
|
||||
--vscode-editorOverviewRuler-selectionHighlightForeground: rgba(160, 160, 160, 0.8);
|
||||
--vscode-problemsErrorIcon-foreground: #f14c4c;
|
||||
--vscode-problemsWarningIcon-foreground: #cca700;
|
||||
--vscode-problemsInfoIcon-foreground: #3794ff;
|
||||
--vscode-minimap-findMatchHighlight: #d18616;
|
||||
--vscode-minimap-selectionOccurrenceHighlight: #676767;
|
||||
--vscode-minimap-selectionHighlight: #264f78;
|
||||
--vscode-minimap-infoHighlight: #3794ff;
|
||||
--vscode-minimap-warningHighlight: #cca700;
|
||||
--vscode-minimap-errorHighlight: rgba(255, 18, 18, 0.7);
|
||||
--vscode-minimap-foregroundOpacity: #000000;
|
||||
--vscode-minimapSlider-background: rgba(121, 121, 121, 0.2);
|
||||
--vscode-minimapSlider-hoverBackground: rgba(100, 100, 100, 0.35);
|
||||
--vscode-minimapSlider-activeBackground: rgba(191, 191, 191, 0.2);
|
||||
--vscode-charts-foreground: #cccccc;
|
||||
--vscode-charts-lines: rgba(204, 204, 204, 0.5);
|
||||
--vscode-charts-red: #f14c4c;
|
||||
--vscode-charts-blue: #3794ff;
|
||||
--vscode-charts-yellow: #cca700;
|
||||
--vscode-charts-orange: #d18616;
|
||||
--vscode-charts-green: #89d185;
|
||||
--vscode-charts-purple: #b180d7;
|
||||
--vscode-input-background: #3c3c3c;
|
||||
--vscode-input-foreground: #cccccc;
|
||||
--vscode-inputOption-activeBorder: #007acc;
|
||||
--vscode-inputOption-hoverBackground: rgba(90, 93, 94, 0.5);
|
||||
--vscode-inputOption-activeBackground: rgba(0, 127, 212, 0.4);
|
||||
--vscode-inputOption-activeForeground: #ffffff;
|
||||
--vscode-input-placeholderForeground: #a6a6a6;
|
||||
--vscode-inputValidation-infoBackground: #063b49;
|
||||
--vscode-inputValidation-infoBorder: #007acc;
|
||||
--vscode-inputValidation-warningBackground: #352a05;
|
||||
--vscode-inputValidation-warningBorder: #b89500;
|
||||
--vscode-inputValidation-errorBackground: #5a1d1d;
|
||||
--vscode-inputValidation-errorBorder: #be1100;
|
||||
--vscode-dropdown-background: #3c3c3c;
|
||||
--vscode-dropdown-foreground: #f0f0f0;
|
||||
--vscode-dropdown-border: #3c3c3c;
|
||||
--vscode-button-foreground: #ffffff;
|
||||
--vscode-button-separator: rgba(255, 255, 255, 0.4);
|
||||
--vscode-button-background: #0e639c;
|
||||
--vscode-button-hoverBackground: #1177bb;
|
||||
--vscode-button-secondaryForeground: #ffffff;
|
||||
--vscode-button-secondaryBackground: #3a3d41;
|
||||
--vscode-button-secondaryHoverBackground: #45494e;
|
||||
--vscode-radio-activeForeground: #ffffff;
|
||||
--vscode-radio-activeBackground: rgba(0, 127, 212, 0.4);
|
||||
--vscode-radio-activeBorder: #007acc;
|
||||
--vscode-radio-inactiveBorder: rgba(255, 255, 255, 0.2);
|
||||
--vscode-radio-inactiveHoverBackground: rgba(90, 93, 94, 0.5);
|
||||
--vscode-checkbox-background: #3c3c3c;
|
||||
--vscode-checkbox-selectBackground: #252526;
|
||||
--vscode-checkbox-foreground: #f0f0f0;
|
||||
--vscode-checkbox-border: #6b6b6b;
|
||||
--vscode-checkbox-selectBorder: #c5c5c5;
|
||||
--vscode-keybindingLabel-background: rgba(128, 128, 128, 0.17);
|
||||
--vscode-keybindingLabel-foreground: #cccccc;
|
||||
--vscode-keybindingLabel-border: rgba(51, 51, 51, 0.6);
|
||||
--vscode-keybindingLabel-bottomBorder: rgba(68, 68, 68, 0.6);
|
||||
--vscode-list-focusOutline: #007fd4;
|
||||
--vscode-list-activeSelectionBackground: #04395e;
|
||||
--vscode-list-activeSelectionForeground: #ffffff;
|
||||
--vscode-list-activeSelectionIconForeground: #ffffff;
|
||||
--vscode-list-inactiveSelectionBackground: #37373d;
|
||||
--vscode-list-hoverBackground: #2a2d2e;
|
||||
--vscode-list-dropBackground: #383b3d;
|
||||
--vscode-list-dropBetweenBackground: #c5c5c5;
|
||||
--vscode-list-highlightForeground: #2aaaff;
|
||||
--vscode-list-focusHighlightForeground: #2aaaff;
|
||||
--vscode-list-invalidItemForeground: #b89500;
|
||||
--vscode-list-errorForeground: #f88070;
|
||||
--vscode-list-warningForeground: #cca700;
|
||||
--vscode-listFilterWidget-background: #252526;
|
||||
--vscode-listFilterWidget-outline: rgba(0, 0, 0, 0);
|
||||
--vscode-listFilterWidget-noMatchesOutline: #be1100;
|
||||
--vscode-listFilterWidget-shadow: rgba(0, 0, 0, 0.36);
|
||||
--vscode-list-filterMatchBackground: rgba(234, 92, 0, 0.33);
|
||||
--vscode-list-deemphasizedForeground: #8c8c8c;
|
||||
--vscode-tree-indentGuidesStroke: #585858;
|
||||
--vscode-tree-inactiveIndentGuidesStroke: rgba(88, 88, 88, 0.4);
|
||||
--vscode-tree-tableColumnsBorder: rgba(204, 204, 204, 0.13);
|
||||
--vscode-tree-tableOddRowsBackground: rgba(204, 204, 204, 0.04);
|
||||
--vscode-editorActionList-background: #252526;
|
||||
--vscode-editorActionList-foreground: #cccccc;
|
||||
--vscode-editorActionList-focusForeground: #ffffff;
|
||||
--vscode-editorActionList-focusBackground: #04395e;
|
||||
--vscode-menu-border: #454545;
|
||||
--vscode-menu-foreground: #cccccc;
|
||||
--vscode-menu-background: #252526;
|
||||
--vscode-menu-selectionForeground: #ffffff;
|
||||
--vscode-menu-selectionBackground: #0078d4;
|
||||
--vscode-menu-separatorBackground: #454545;
|
||||
--vscode-quickInput-background: #252526;
|
||||
--vscode-quickInput-foreground: #cccccc;
|
||||
--vscode-quickInputTitle-background: rgba(255, 255, 255, 0.1);
|
||||
--vscode-pickerGroup-foreground: #3794ff;
|
||||
--vscode-pickerGroup-border: #3f3f46;
|
||||
--vscode-quickInputList-focusForeground: #ffffff;
|
||||
--vscode-quickInputList-focusIconForeground: #ffffff;
|
||||
--vscode-quickInputList-focusBackground: #04395e;
|
||||
--vscode-search-resultsInfoForeground: rgba(204, 204, 204, 0.65);
|
||||
--vscode-searchEditor-findMatchBackground: rgba(234, 92, 0, 0.22);
|
||||
--vscode-editor-lineHighlightBorder: #282828;
|
||||
--vscode-editor-rangeHighlightBackground: rgba(255, 255, 255, 0.04);
|
||||
--vscode-editor-symbolHighlightBackground: rgba(234, 92, 0, 0.33);
|
||||
--vscode-editorCursor-foreground: #aeafad;
|
||||
--vscode-editorMultiCursor-primary-foreground: #aeafad;
|
||||
--vscode-editorMultiCursor-secondary-foreground: #aeafad;
|
||||
--vscode-editorWhitespace-foreground: rgba(227, 228, 226, 0.16);
|
||||
--vscode-editorLineNumber-foreground: #858585;
|
||||
--vscode-editorIndentGuide-background: rgba(227, 228, 226, 0.16);
|
||||
--vscode-editorIndentGuide-activeBackground: rgba(227, 228, 226, 0.16);
|
||||
--vscode-editorIndentGuide-background1: #404040;
|
||||
--vscode-editorIndentGuide-background2: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-background3: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-background4: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-background5: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-background6: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-activeBackground1: #707070;
|
||||
--vscode-editorIndentGuide-activeBackground2: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-activeBackground3: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-activeBackground4: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-activeBackground5: rgba(0, 0, 0, 0);
|
||||
--vscode-editorIndentGuide-activeBackground6: rgba(0, 0, 0, 0);
|
||||
--vscode-editorActiveLineNumber-foreground: #c6c6c6;
|
||||
--vscode-editorLineNumber-activeForeground: #c6c6c6;
|
||||
--vscode-editorRuler-foreground: #5a5a5a;
|
||||
--vscode-editorCodeLens-foreground: #999999;
|
||||
--vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1);
|
||||
--vscode-editorBracketMatch-border: #888888;
|
||||
--vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3);
|
||||
--vscode-editorGutter-background: #1e1e1e;
|
||||
--vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.67);
|
||||
--vscode-editorGhostText-foreground: rgba(255, 255, 255, 0.34);
|
||||
--vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6);
|
||||
--vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7);
|
||||
--vscode-editorOverviewRuler-warningForeground: #cca700;
|
||||
--vscode-editorOverviewRuler-infoForeground: #3794ff;
|
||||
--vscode-editorBracketHighlight-foreground1: #ffd700;
|
||||
--vscode-editorBracketHighlight-foreground2: #da70d6;
|
||||
--vscode-editorBracketHighlight-foreground3: #179fff;
|
||||
--vscode-editorBracketHighlight-foreground4: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketHighlight-foreground5: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketHighlight-foreground6: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketHighlight-unexpectedBracket-foreground: rgba(255, 18, 18, 0.8);
|
||||
--vscode-editorBracketPairGuide-background1: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-background2: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-background3: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-background4: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-background5: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-background6: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground1: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground2: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground3: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground4: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground5: rgba(0, 0, 0, 0);
|
||||
--vscode-editorBracketPairGuide-activeBackground6: rgba(0, 0, 0, 0);
|
||||
--vscode-editorUnicodeHighlight-border: #cca700;
|
||||
--vscode-diffEditor-move-border: rgba(139, 139, 139, 0.61);
|
||||
--vscode-diffEditor-moveActive-border: #ffa500;
|
||||
--vscode-diffEditor-unchangedRegionShadow: #000000;
|
||||
--vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0;
|
||||
--vscode-symbolIcon-arrayForeground: #cccccc;
|
||||
--vscode-symbolIcon-booleanForeground: #cccccc;
|
||||
--vscode-symbolIcon-classForeground: #ee9d28;
|
||||
--vscode-symbolIcon-colorForeground: #cccccc;
|
||||
--vscode-symbolIcon-constantForeground: #cccccc;
|
||||
--vscode-symbolIcon-constructorForeground: #b180d7;
|
||||
--vscode-symbolIcon-enumeratorForeground: #ee9d28;
|
||||
--vscode-symbolIcon-enumeratorMemberForeground: #75beff;
|
||||
--vscode-symbolIcon-eventForeground: #ee9d28;
|
||||
--vscode-symbolIcon-fieldForeground: #75beff;
|
||||
--vscode-symbolIcon-fileForeground: #cccccc;
|
||||
--vscode-symbolIcon-folderForeground: #cccccc;
|
||||
--vscode-symbolIcon-functionForeground: #b180d7;
|
||||
--vscode-symbolIcon-interfaceForeground: #75beff;
|
||||
--vscode-symbolIcon-keyForeground: #cccccc;
|
||||
--vscode-symbolIcon-keywordForeground: #cccccc;
|
||||
--vscode-symbolIcon-methodForeground: #b180d7;
|
||||
--vscode-symbolIcon-moduleForeground: #cccccc;
|
||||
--vscode-symbolIcon-namespaceForeground: #cccccc;
|
||||
--vscode-symbolIcon-nullForeground: #cccccc;
|
||||
--vscode-symbolIcon-numberForeground: #cccccc;
|
||||
--vscode-symbolIcon-objectForeground: #cccccc;
|
||||
--vscode-symbolIcon-operatorForeground: #cccccc;
|
||||
--vscode-symbolIcon-packageForeground: #cccccc;
|
||||
--vscode-symbolIcon-propertyForeground: #cccccc;
|
||||
--vscode-symbolIcon-referenceForeground: #cccccc;
|
||||
--vscode-symbolIcon-snippetForeground: #cccccc;
|
||||
--vscode-symbolIcon-stringForeground: #cccccc;
|
||||
--vscode-symbolIcon-structForeground: #cccccc;
|
||||
--vscode-symbolIcon-textForeground: #cccccc;
|
||||
--vscode-symbolIcon-typeParameterForeground: #cccccc;
|
||||
--vscode-symbolIcon-unitForeground: #cccccc;
|
||||
--vscode-symbolIcon-variableForeground: #75beff;
|
||||
--vscode-actionBar-toggledBackground: #383a49;
|
||||
--vscode-peekViewTitle-background: #252526;
|
||||
--vscode-peekViewTitleLabel-foreground: #ffffff;
|
||||
--vscode-peekViewTitleDescription-foreground: rgba(204, 204, 204, 0.7);
|
||||
--vscode-peekView-border: #3794ff;
|
||||
--vscode-peekViewResult-background: #252526;
|
||||
--vscode-peekViewResult-lineForeground: #bbbbbb;
|
||||
--vscode-peekViewResult-fileForeground: #ffffff;
|
||||
--vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2);
|
||||
--vscode-peekViewResult-selectionForeground: #ffffff;
|
||||
--vscode-peekViewEditor-background: #001f33;
|
||||
--vscode-peekViewEditorGutter-background: #001f33;
|
||||
--vscode-peekViewEditorStickyScroll-background: #001f33;
|
||||
--vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3);
|
||||
--vscode-peekViewEditor-matchHighlightBackground: rgba(255, 143, 0, 0.6);
|
||||
--vscode-editor-foldBackground: rgba(38, 79, 120, 0.3);
|
||||
--vscode-editor-foldPlaceholderForeground: #808080;
|
||||
--vscode-editorGutter-foldingControlForeground: #c5c5c5;
|
||||
--vscode-editorSuggestWidget-background: #252526;
|
||||
--vscode-editorSuggestWidget-border: #454545;
|
||||
--vscode-editorSuggestWidget-foreground: #d4d4d4;
|
||||
--vscode-editorSuggestWidget-selectedForeground: #ffffff;
|
||||
--vscode-editorSuggestWidget-selectedIconForeground: #ffffff;
|
||||
--vscode-editorSuggestWidget-selectedBackground: #04395e;
|
||||
--vscode-editorSuggestWidget-highlightForeground: #2aaaff;
|
||||
--vscode-editorSuggestWidget-focusHighlightForeground: #2aaaff;
|
||||
--vscode-editorSuggestWidgetStatus-foreground: rgba(212, 212, 212, 0.5);
|
||||
--vscode-editorMarkerNavigationError-background: #f14c4c;
|
||||
--vscode-editorMarkerNavigationError-headerBackground: rgba(241, 76, 76, 0.1);
|
||||
--vscode-editorMarkerNavigationWarning-background: #cca700;
|
||||
--vscode-editorMarkerNavigationWarning-headerBackground: rgba(204, 167, 0, 0.1);
|
||||
--vscode-editorMarkerNavigationInfo-background: #3794ff;
|
||||
--vscode-editorMarkerNavigationInfo-headerBackground: rgba(55, 148, 255, 0.1);
|
||||
--vscode-editorMarkerNavigation-background: #1e1e1e;
|
||||
--vscode-editor-linkedEditingBackground: rgba(255, 0, 0, 0.3);
|
||||
--vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.72);
|
||||
--vscode-editor-wordHighlightStrongBackground: rgba(0, 73, 114, 0.72);
|
||||
--vscode-editor-wordHighlightTextBackground: rgba(87, 87, 87, 0.72);
|
||||
--vscode-editorOverviewRuler-wordHighlightForeground: rgba(160, 160, 160, 0.8);
|
||||
--vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(192, 160, 192, 0.8);
|
||||
--vscode-editorOverviewRuler-wordHighlightTextForeground: rgba(160, 160, 160, 0.8);
|
||||
--vscode-editorHoverWidget-highlightForeground: #2aaaff;
|
||||
--vscode-editor-placeholder-foreground: rgba(255, 255, 255, 0.34);
|
||||
--vscode-tab-activeBackground: #1e1e1e;
|
||||
--vscode-tab-unfocusedActiveBackground: #1e1e1e;
|
||||
--vscode-tab-inactiveBackground: #2d2d2d;
|
||||
--vscode-tab-unfocusedInactiveBackground: #2d2d2d;
|
||||
--vscode-tab-activeForeground: #ffffff;
|
||||
--vscode-tab-inactiveForeground: rgba(255, 255, 255, 0.5);
|
||||
--vscode-tab-unfocusedActiveForeground: rgba(255, 255, 255, 0.5);
|
||||
--vscode-tab-unfocusedInactiveForeground: rgba(255, 255, 255, 0.25);
|
||||
--vscode-tab-border: #252526;
|
||||
--vscode-tab-lastPinnedBorder: rgba(204, 204, 204, 0.2);
|
||||
--vscode-tab-selectedBackground: #222222;
|
||||
--vscode-tab-selectedForeground: rgba(255, 255, 255, 0.63);
|
||||
--vscode-tab-dragAndDropBorder: #ffffff;
|
||||
--vscode-tab-activeModifiedBorder: #3399cc;
|
||||
--vscode-tab-inactiveModifiedBorder: rgba(51, 153, 204, 0.5);
|
||||
--vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 153, 204, 0.5);
|
||||
--vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 153, 204, 0.25);
|
||||
--vscode-editorPane-background: #1e1e1e;
|
||||
--vscode-editorGroupHeader-tabsBackground: #252526;
|
||||
--vscode-editorGroupHeader-noTabsBackground: #1e1e1e;
|
||||
--vscode-editorGroup-border: #444444;
|
||||
--vscode-editorGroup-dropBackground: rgba(83, 89, 93, 0.5);
|
||||
--vscode-editorGroup-dropIntoPromptForeground: #cccccc;
|
||||
--vscode-editorGroup-dropIntoPromptBackground: #252526;
|
||||
--vscode-sideBySideEditor-horizontalBorder: #444444;
|
||||
--vscode-sideBySideEditor-verticalBorder: #444444;
|
||||
--vscode-panel-background: #1e1e1e;
|
||||
--vscode-panel-border: rgba(128, 128, 128, 0.35);
|
||||
--vscode-panelTitle-activeForeground: #e7e7e7;
|
||||
--vscode-panelTitle-inactiveForeground: rgba(231, 231, 231, 0.6);
|
||||
--vscode-panelTitle-activeBorder: #e7e7e7;
|
||||
--vscode-panel-dropBorder: #e7e7e7;
|
||||
--vscode-panelSection-dropBackground: rgba(83, 89, 93, 0.5);
|
||||
--vscode-panelSectionHeader-background: rgba(128, 128, 128, 0.2);
|
||||
--vscode-panelSection-border: rgba(128, 128, 128, 0.35);
|
||||
--vscode-panelStickyScroll-background: #1e1e1e;
|
||||
--vscode-panelStickyScroll-shadow: #000000;
|
||||
--vscode-banner-background: #04395e;
|
||||
--vscode-banner-foreground: #ffffff;
|
||||
--vscode-banner-iconForeground: #3794ff;
|
||||
--vscode-statusBar-foreground: #ffffff;
|
||||
--vscode-statusBar-noFolderForeground: #ffffff;
|
||||
--vscode-statusBar-background: #007acc;
|
||||
--vscode-statusBar-noFolderBackground: #68217a;
|
||||
--vscode-statusBar-focusBorder: #ffffff;
|
||||
--vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18);
|
||||
--vscode-statusBarItem-focusBorder: #ffffff;
|
||||
--vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12);
|
||||
--vscode-statusBarItem-hoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-compactHoverBackground: rgba(255, 255, 255, 0.2);
|
||||
--vscode-statusBarItem-prominentForeground: #ffffff;
|
||||
--vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5);
|
||||
--vscode-statusBarItem-prominentHoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3);
|
||||
--vscode-statusBarItem-errorBackground: #c72e0f;
|
||||
--vscode-statusBarItem-errorForeground: #ffffff;
|
||||
--vscode-statusBarItem-errorHoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-errorHoverBackground: rgba(255, 255, 255, 0.12);
|
||||
--vscode-statusBarItem-warningBackground: #7a6400;
|
||||
--vscode-statusBarItem-warningForeground: #ffffff;
|
||||
--vscode-statusBarItem-warningHoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-warningHoverBackground: rgba(255, 255, 255, 0.12);
|
||||
--vscode-activityBar-background: #333333;
|
||||
--vscode-activityBar-foreground: #ffffff;
|
||||
--vscode-activityBar-inactiveForeground: rgba(255, 255, 255, 0.4);
|
||||
--vscode-activityBar-activeBorder: #ffffff;
|
||||
--vscode-activityBar-dropBorder: #ffffff;
|
||||
--vscode-activityBarBadge-background: #007acc;
|
||||
--vscode-activityBarBadge-foreground: #ffffff;
|
||||
--vscode-activityBarTop-foreground: #e7e7e7;
|
||||
--vscode-activityBarTop-activeBorder: #e7e7e7;
|
||||
--vscode-activityBarTop-inactiveForeground: rgba(231, 231, 231, 0.6);
|
||||
--vscode-activityBarTop-dropBorder: #e7e7e7;
|
||||
--vscode-profileBadge-background: #4d4d4d;
|
||||
--vscode-profileBadge-foreground: #ffffff;
|
||||
--vscode-statusBarItem-remoteBackground: #16825d;
|
||||
--vscode-statusBarItem-remoteForeground: #ffffff;
|
||||
--vscode-statusBarItem-remoteHoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-remoteHoverBackground: rgba(255, 255, 255, 0.12);
|
||||
--vscode-statusBarItem-offlineBackground: #6c1717;
|
||||
--vscode-statusBarItem-offlineForeground: #ffffff;
|
||||
--vscode-statusBarItem-offlineHoverForeground: #ffffff;
|
||||
--vscode-statusBarItem-offlineHoverBackground: rgba(255, 255, 255, 0.12);
|
||||
--vscode-extensionBadge-remoteBackground: #007acc;
|
||||
--vscode-extensionBadge-remoteForeground: #ffffff;
|
||||
--vscode-sideBar-background: #252526;
|
||||
--vscode-sideBarTitle-background: #252526;
|
||||
--vscode-sideBarTitle-foreground: #bbbbbb;
|
||||
--vscode-sideBar-dropBackground: rgba(83, 89, 93, 0.5);
|
||||
--vscode-sideBarSectionHeader-background: rgba(0, 0, 0, 0);
|
||||
--vscode-sideBarSectionHeader-border: rgba(204, 204, 204, 0.2);
|
||||
--vscode-sideBarActivityBarTop-border: rgba(204, 204, 204, 0.2);
|
||||
--vscode-sideBarStickyScroll-background: #252526;
|
||||
--vscode-sideBarStickyScroll-shadow: #000000;
|
||||
--vscode-titleBar-activeForeground: #cccccc;
|
||||
--vscode-titleBar-inactiveForeground: rgba(204, 204, 204, 0.6);
|
||||
--vscode-titleBar-activeBackground: #3c3c3c;
|
||||
--vscode-titleBar-inactiveBackground: rgba(60, 60, 60, 0.6);
|
||||
--vscode-menubar-selectionForeground: #cccccc;
|
||||
--vscode-menubar-selectionBackground: rgba(90, 93, 94, 0.31);
|
||||
--vscode-commandCenter-foreground: #cccccc;
|
||||
--vscode-commandCenter-activeForeground: #cccccc;
|
||||
--vscode-commandCenter-inactiveForeground: rgba(204, 204, 204, 0.6);
|
||||
--vscode-commandCenter-background: rgba(255, 255, 255, 0.05);
|
||||
--vscode-commandCenter-activeBackground: rgba(255, 255, 255, 0.08);
|
||||
--vscode-commandCenter-border: rgba(204, 204, 204, 0.2);
|
||||
--vscode-commandCenter-activeBorder: rgba(204, 204, 204, 0.3);
|
||||
--vscode-commandCenter-inactiveBorder: rgba(204, 204, 204, 0.15);
|
||||
--vscode-notificationCenter-border: #303031;
|
||||
--vscode-notificationToast-border: #303031;
|
||||
--vscode-notifications-foreground: #cccccc;
|
||||
--vscode-notifications-background: #252526;
|
||||
--vscode-notificationLink-foreground: #3794ff;
|
||||
--vscode-notificationCenterHeader-background: #303031;
|
||||
--vscode-notifications-border: #303031;
|
||||
--vscode-notificationsErrorIcon-foreground: #f14c4c;
|
||||
--vscode-notificationsWarningIcon-foreground: #cca700;
|
||||
--vscode-notificationsInfoIcon-foreground: #3794ff;
|
||||
--vscode-inlineChat-foreground: #cccccc;
|
||||
--vscode-inlineChat-background: #252526;
|
||||
--vscode-inlineChat-border: #454545;
|
||||
--vscode-inlineChat-shadow: rgba(0, 0, 0, 0.36);
|
||||
--vscode-inlineChatInput-border: #454545;
|
||||
--vscode-inlineChatInput-focusBorder: #007fd4;
|
||||
--vscode-inlineChatInput-placeholderForeground: #a6a6a6;
|
||||
--vscode-inlineChatInput-background: #3c3c3c;
|
||||
--vscode-inlineChatDiff-inserted: rgba(156, 204, 44, 0.1);
|
||||
--vscode-editorOverviewRuler-inlineChatInserted: rgba(156, 204, 44, 0.12);
|
||||
--vscode-inlineChatDiff-removed: rgba(255, 0, 0, 0.1);
|
||||
--vscode-editorOverviewRuler-inlineChatRemoved: rgba(255, 0, 0, 0.12);
|
||||
--vscode-extensionButton-background: #0e639c;
|
||||
--vscode-extensionButton-foreground: #ffffff;
|
||||
--vscode-extensionButton-hoverBackground: #1177bb;
|
||||
--vscode-extensionButton-separator: rgba(255, 255, 255, 0.4);
|
||||
--vscode-extensionButton-prominentBackground: #0e639c;
|
||||
--vscode-extensionButton-prominentForeground: #ffffff;
|
||||
--vscode-extensionButton-prominentHoverBackground: #1177bb;
|
||||
--vscode-chat-requestBorder: rgba(255, 255, 255, 0.1);
|
||||
--vscode-chat-requestBackground: rgba(30, 30, 30, 0.62);
|
||||
--vscode-chat-slashCommandBackground: rgba(52, 65, 75, 0.56);
|
||||
--vscode-chat-slashCommandForeground: #40a6ff;
|
||||
--vscode-chat-avatarBackground: #1f1f1f;
|
||||
--vscode-chat-avatarForeground: #cccccc;
|
||||
--vscode-terminal-foreground: #cccccc;
|
||||
--vscode-terminal-selectionBackground: #264f78;
|
||||
--vscode-terminal-inactiveSelectionBackground: #3a3d41;
|
||||
--vscode-terminalCommandDecoration-defaultBackground: rgba(255, 255, 255, 0.25);
|
||||
--vscode-terminalCommandDecoration-successBackground: #1b81a8;
|
||||
--vscode-terminalCommandDecoration-errorBackground: #f14c4c;
|
||||
--vscode-terminalOverviewRuler-cursorForeground: rgba(160, 160, 160, 0.8);
|
||||
--vscode-terminal-border: rgba(128, 128, 128, 0.35);
|
||||
--vscode-terminalOverviewRuler-border: rgba(127, 127, 127, 0.3);
|
||||
--vscode-terminal-findMatchBackground: #515c6a;
|
||||
--vscode-terminal-hoverHighlightBackground: rgba(38, 79, 120, 0.13);
|
||||
--vscode-terminal-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
|
||||
--vscode-terminalOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49);
|
||||
--vscode-terminal-dropBackground: rgba(83, 89, 93, 0.5);
|
||||
--vscode-terminal-initialHintForeground: rgba(255, 255, 255, 0.34);
|
||||
--vscode-terminalStickyScrollHover-background: #2a2d2e;
|
||||
--vscode-scmGraph-historyItemRefColor: #3794ff;
|
||||
--vscode-scmGraph-historyItemRemoteRefColor: #b180d7;
|
||||
--vscode-scmGraph-historyItemBaseRefColor: #ea5c00;
|
||||
--vscode-scmGraph-historyItemHoverDefaultLabelForeground: #cccccc;
|
||||
--vscode-scmGraph-historyItemHoverDefaultLabelBackground: #4d4d4d;
|
||||
--vscode-scmGraph-historyItemHoverLabelForeground: #ffffff;
|
||||
--vscode-scmGraph-historyItemHoverAdditionsForeground: #81b88b;
|
||||
--vscode-scmGraph-historyItemHoverDeletionsForeground: #c74e39;
|
||||
--vscode-scmGraph-foreground1: #ffb000;
|
||||
--vscode-scmGraph-foreground2: #dc267f;
|
||||
--vscode-scmGraph-foreground3: #994f00;
|
||||
--vscode-scmGraph-foreground4: #40b0a6;
|
||||
--vscode-scmGraph-foreground5: #b66dff;
|
||||
--vscode-commentsView-resolvedIcon: rgba(204, 204, 204, 0.5);
|
||||
--vscode-commentsView-unresolvedIcon: #007fd4;
|
||||
--vscode-editorCommentsWidget-replyInputBackground: #252526;
|
||||
--vscode-editorCommentsWidget-resolvedBorder: rgba(204, 204, 204, 0.5);
|
||||
--vscode-editorCommentsWidget-unresolvedBorder: #007fd4;
|
||||
--vscode-editorCommentsWidget-rangeBackground: rgba(0, 127, 212, 0.1);
|
||||
--vscode-editorCommentsWidget-rangeActiveBackground: rgba(0, 127, 212, 0.1);
|
||||
--vscode-editorGutter-commentRangeForeground: #37373d;
|
||||
--vscode-editorOverviewRuler-commentForeground: #37373d;
|
||||
--vscode-editorOverviewRuler-commentUnresolvedForeground: #37373d;
|
||||
--vscode-editorGutter-commentGlyphForeground: #d4d4d4;
|
||||
--vscode-editorGutter-commentUnresolvedGlyphForeground: #d4d4d4;
|
||||
--vscode-activityWarningBadge-foreground: #000000;
|
||||
--vscode-activityWarningBadge-background: #cca700;
|
||||
--vscode-activityErrorBadge-foreground: #000000;
|
||||
--vscode-activityErrorBadge-background: #f14c4c;
|
||||
--vscode-ports-iconRunningProcessForeground: #369432;
|
||||
--vscode-editorWatermark-foreground: rgba(212, 212, 212, 0.6);
|
||||
--vscode-settings-headerForeground: #e7e7e7;
|
||||
--vscode-settings-settingsHeaderHoverForeground: rgba(231, 231, 231, 0.7);
|
||||
--vscode-settings-modifiedItemIndicator: #0c7d9d;
|
||||
--vscode-settings-headerBorder: rgba(128, 128, 128, 0.35);
|
||||
--vscode-settings-sashBorder: rgba(128, 128, 128, 0.35);
|
||||
--vscode-settings-dropdownBackground: #3c3c3c;
|
||||
--vscode-settings-dropdownForeground: #f0f0f0;
|
||||
--vscode-settings-dropdownBorder: #3c3c3c;
|
||||
--vscode-settings-dropdownListBorder: #454545;
|
||||
--vscode-settings-checkboxBackground: #3c3c3c;
|
||||
--vscode-settings-checkboxForeground: #f0f0f0;
|
||||
--vscode-settings-checkboxBorder: #6b6b6b;
|
||||
--vscode-settings-textInputBackground: #3c3c3c;
|
||||
--vscode-settings-textInputForeground: #cccccc;
|
||||
--vscode-settings-numberInputBackground: #3c3c3c;
|
||||
--vscode-settings-numberInputForeground: #cccccc;
|
||||
--vscode-settings-focusedRowBackground: rgba(42, 45, 46, 0.6);
|
||||
--vscode-settings-rowHoverBackground: rgba(42, 45, 46, 0.3);
|
||||
--vscode-settings-focusedRowBorder: #007fd4;
|
||||
--vscode-keybindingTable-headerBackground: rgba(204, 204, 204, 0.04);
|
||||
--vscode-keybindingTable-rowsBackground: rgba(204, 204, 204, 0.04);
|
||||
--vscode-debugToolBar-background: #333333;
|
||||
--vscode-debugIcon-startForeground: #89d185;
|
||||
--vscode-notebook-cellBorderColor: #37373d;
|
||||
--vscode-notebook-focusedEditorBorder: #007fd4;
|
||||
--vscode-notebookStatusSuccessIcon-foreground: #89d185;
|
||||
--vscode-notebookEditorOverviewRuler-runningCellForeground: #89d185;
|
||||
--vscode-notebookStatusErrorIcon-foreground: #f48771;
|
||||
--vscode-notebookStatusRunningIcon-foreground: #cccccc;
|
||||
--vscode-notebook-cellToolbarSeparator: rgba(128, 128, 128, 0.35);
|
||||
--vscode-notebook-selectedCellBackground: #37373d;
|
||||
--vscode-notebook-selectedCellBorder: #37373d;
|
||||
--vscode-notebook-focusedCellBorder: #007fd4;
|
||||
--vscode-notebook-inactiveFocusedCellBorder: #37373d;
|
||||
--vscode-notebook-cellStatusBarItemHoverBackground: rgba(255, 255, 255, 0.15);
|
||||
--vscode-notebook-cellInsertionIndicator: #007fd4;
|
||||
--vscode-notebookScrollbarSlider-background: rgba(121, 121, 121, 0.4);
|
||||
--vscode-notebookScrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
|
||||
--vscode-notebookScrollbarSlider-activeBackground: rgba(191, 191, 191, 0.4);
|
||||
--vscode-notebook-symbolHighlightBackground: rgba(255, 255, 255, 0.04);
|
||||
--vscode-notebook-cellEditorBackground: #252526;
|
||||
--vscode-notebook-editorBackground: #1e1e1e;
|
||||
--vscode-debugIcon-breakpointForeground: #e51400;
|
||||
--vscode-debugIcon-breakpointDisabledForeground: #848484;
|
||||
--vscode-debugIcon-breakpointUnverifiedForeground: #848484;
|
||||
--vscode-debugIcon-breakpointCurrentStackframeForeground: #ffcc00;
|
||||
--vscode-debugIcon-breakpointStackframeForeground: #89d185;
|
||||
--vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 0, 0.2);
|
||||
--vscode-editor-focusedStackFrameHighlightBackground: rgba(122, 189, 122, 0.3);
|
||||
--vscode-multiDiffEditor-headerBackground: #262626;
|
||||
--vscode-multiDiffEditor-background: #1e1e1e;
|
||||
--vscode-multiDiffEditor-border: rgba(204, 204, 204, 0.2);
|
||||
--vscode-interactive-activeCodeBorder: #007acc;
|
||||
--vscode-interactive-inactiveCodeBorder: #37373d;
|
||||
--vscode-testing-iconFailed: #f14c4c;
|
||||
--vscode-testing-iconErrored: #f14c4c;
|
||||
--vscode-testing-iconPassed: #73c991;
|
||||
--vscode-testing-runAction: #73c991;
|
||||
--vscode-testing-iconQueued: #cca700;
|
||||
--vscode-testing-iconUnset: #848484;
|
||||
--vscode-testing-iconSkipped: #848484;
|
||||
--vscode-testing-peekBorder: #f14c4c;
|
||||
--vscode-testing-messagePeekBorder: #3794ff;
|
||||
--vscode-testing-peekHeaderBackground: rgba(241, 76, 76, 0.1);
|
||||
--vscode-testing-messagePeekHeaderBackground: rgba(55, 148, 255, 0.1);
|
||||
--vscode-testing-coveredBackground: rgba(156, 204, 44, 0.2);
|
||||
--vscode-testing-coveredBorder: rgba(156, 204, 44, 0.15);
|
||||
--vscode-testing-coveredGutterBackground: rgba(156, 204, 44, 0.12);
|
||||
--vscode-testing-uncoveredBranchBackground: #781212;
|
||||
--vscode-testing-uncoveredBackground: rgba(255, 0, 0, 0.2);
|
||||
--vscode-testing-uncoveredBorder: rgba(255, 0, 0, 0.15);
|
||||
--vscode-testing-uncoveredGutterBackground: rgba(255, 0, 0, 0.3);
|
||||
--vscode-testing-coverCountBadgeBackground: #4d4d4d;
|
||||
--vscode-testing-coverCountBadgeForeground: #ffffff;
|
||||
--vscode-testing-message-error-decorationForeground: #f14c4c;
|
||||
--vscode-testing-message-error-lineBackground: rgba(255, 0, 0, 0.1);
|
||||
--vscode-testing-message-info-decorationForeground: rgba(212, 212, 212, 0.5);
|
||||
--vscode-testing-iconErrored-retired: rgba(241, 76, 76, 0.7);
|
||||
--vscode-testing-iconFailed-retired: rgba(241, 76, 76, 0.7);
|
||||
--vscode-testing-iconPassed-retired: rgba(115, 201, 145, 0.7);
|
||||
--vscode-testing-iconQueued-retired: rgba(204, 167, 0, 0.7);
|
||||
--vscode-testing-iconUnset-retired: rgba(132, 132, 132, 0.7);
|
||||
--vscode-testing-iconSkipped-retired: rgba(132, 132, 132, 0.7);
|
||||
--vscode-editorGutter-modifiedBackground: #1b81a8;
|
||||
--vscode-editorGutter-addedBackground: #487e02;
|
||||
--vscode-editorGutter-deletedBackground: #f14c4c;
|
||||
--vscode-minimapGutter-modifiedBackground: #1b81a8;
|
||||
--vscode-minimapGutter-addedBackground: #487e02;
|
||||
--vscode-minimapGutter-deletedBackground: #f14c4c;
|
||||
--vscode-editorOverviewRuler-modifiedForeground: rgba(27, 129, 168, 0.6);
|
||||
--vscode-editorOverviewRuler-addedForeground: rgba(72, 126, 2, 0.6);
|
||||
--vscode-editorOverviewRuler-deletedForeground: rgba(241, 76, 76, 0.6);
|
||||
--vscode-debugExceptionWidget-border: #a31515;
|
||||
--vscode-debugExceptionWidget-background: #420b0d;
|
||||
--vscode-editor-inlineValuesForeground: rgba(255, 255, 255, 0.5);
|
||||
--vscode-editor-inlineValuesBackground: rgba(255, 200, 0, 0.2);
|
||||
--vscode-statusBar-debuggingBackground: #cc6633;
|
||||
--vscode-statusBar-debuggingForeground: #ffffff;
|
||||
--vscode-commandCenter-debuggingBackground: rgba(204, 102, 51, 0.26);
|
||||
--vscode-debugTokenExpression-name: #c586c0;
|
||||
--vscode-debugTokenExpression-type: #4a90e2;
|
||||
--vscode-debugTokenExpression-value: rgba(204, 204, 204, 0.6);
|
||||
--vscode-debugTokenExpression-string: #ce9178;
|
||||
--vscode-debugTokenExpression-boolean: #4e94ce;
|
||||
--vscode-debugTokenExpression-number: #b5cea8;
|
||||
--vscode-debugTokenExpression-error: #f48771;
|
||||
--vscode-debugView-exceptionLabelForeground: #cccccc;
|
||||
--vscode-debugView-exceptionLabelBackground: #6c2022;
|
||||
--vscode-debugView-stateLabelForeground: #cccccc;
|
||||
--vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27);
|
||||
--vscode-debugView-valueChangedHighlight: #569cd6;
|
||||
--vscode-debugConsole-infoForeground: #3794ff;
|
||||
--vscode-debugConsole-warningForeground: #cca700;
|
||||
--vscode-debugConsole-errorForeground: #f48771;
|
||||
--vscode-debugConsole-sourceForeground: #cccccc;
|
||||
--vscode-debugConsoleInputIcon-foreground: #cccccc;
|
||||
--vscode-debugIcon-pauseForeground: #75beff;
|
||||
--vscode-debugIcon-stopForeground: #f48771;
|
||||
--vscode-debugIcon-disconnectForeground: #f48771;
|
||||
--vscode-debugIcon-restartForeground: #89d185;
|
||||
--vscode-debugIcon-stepOverForeground: #75beff;
|
||||
--vscode-debugIcon-stepIntoForeground: #75beff;
|
||||
--vscode-debugIcon-stepOutForeground: #75beff;
|
||||
--vscode-debugIcon-continueForeground: #75beff;
|
||||
--vscode-debugIcon-stepBackForeground: #75beff;
|
||||
--vscode-mergeEditor-change-background: rgba(155, 185, 85, 0.2);
|
||||
--vscode-mergeEditor-change-word-background: rgba(156, 204, 44, 0.2);
|
||||
--vscode-mergeEditor-changeBase-background: #4b1818;
|
||||
--vscode-mergeEditor-changeBase-word-background: #6f1313;
|
||||
--vscode-mergeEditor-conflict-unhandledUnfocused-border: rgba(255, 166, 0, 0.48);
|
||||
--vscode-mergeEditor-conflict-unhandledFocused-border: #ffa600;
|
||||
--vscode-mergeEditor-conflict-handledUnfocused-border: rgba(134, 134, 134, 0.29);
|
||||
--vscode-mergeEditor-conflict-handledFocused-border: rgba(193, 193, 193, 0.8);
|
||||
--vscode-mergeEditor-conflict-handled-minimapOverViewRuler: rgba(173, 172, 168, 0.93);
|
||||
--vscode-mergeEditor-conflict-unhandled-minimapOverViewRuler: #fcba03;
|
||||
--vscode-mergeEditor-conflictingLines-background: rgba(255, 234, 0, 0.28);
|
||||
--vscode-mergeEditor-conflict-input1-background: rgba(64, 200, 174, 0.2);
|
||||
--vscode-mergeEditor-conflict-input2-background: rgba(64, 166, 255, 0.2);
|
||||
--vscode-extensionIcon-starForeground: #ff8e00;
|
||||
--vscode-extensionIcon-verifiedForeground: #3794ff;
|
||||
--vscode-extensionIcon-preReleaseForeground: #1d9271;
|
||||
--vscode-extensionIcon-sponsorForeground: #d758b3;
|
||||
--vscode-terminal-ansiBlack: #000000;
|
||||
--vscode-terminal-ansiRed: #cd3131;
|
||||
--vscode-terminal-ansiGreen: #0dbc79;
|
||||
--vscode-terminal-ansiYellow: #e5e510;
|
||||
--vscode-terminal-ansiBlue: #2472c8;
|
||||
--vscode-terminal-ansiMagenta: #bc3fbc;
|
||||
--vscode-terminal-ansiCyan: #11a8cd;
|
||||
--vscode-terminal-ansiWhite: #e5e5e5;
|
||||
--vscode-terminal-ansiBrightBlack: #666666;
|
||||
--vscode-terminal-ansiBrightRed: #f14c4c;
|
||||
--vscode-terminal-ansiBrightGreen: #23d18b;
|
||||
--vscode-terminal-ansiBrightYellow: #f5f543;
|
||||
--vscode-terminal-ansiBrightBlue: #3b8eea;
|
||||
--vscode-terminal-ansiBrightMagenta: #d670d6;
|
||||
--vscode-terminal-ansiBrightCyan: #29b8db;
|
||||
--vscode-terminal-ansiBrightWhite: #e5e5e5;
|
||||
--vscode-simpleFindWidget-sashBorder: #454545;
|
||||
--vscode-terminalCommandGuide-foreground: #37373d;
|
||||
--vscode-welcomePage-tileBackground: #252526;
|
||||
--vscode-welcomePage-tileHoverBackground: #2c2c2d;
|
||||
--vscode-welcomePage-tileBorder: rgba(255, 255, 255, 0.1);
|
||||
--vscode-welcomePage-progress-background: #3c3c3c;
|
||||
--vscode-welcomePage-progress-foreground: #3794ff;
|
||||
--vscode-walkthrough-stepTitle-foreground: #ffffff;
|
||||
--vscode-walkThrough-embeddedEditorBackground: rgba(0, 0, 0, 0.4);
|
||||
--vscode-profiles-sashBorder: rgba(128, 128, 128, 0.35);
|
||||
--vscode-gitDecoration-addedResourceForeground: #81b88b;
|
||||
--vscode-gitDecoration-modifiedResourceForeground: #e2c08d;
|
||||
--vscode-gitDecoration-deletedResourceForeground: #c74e39;
|
||||
--vscode-gitDecoration-renamedResourceForeground: #73c991;
|
||||
--vscode-gitDecoration-untrackedResourceForeground: #73c991;
|
||||
--vscode-gitDecoration-ignoredResourceForeground: #8c8c8c;
|
||||
--vscode-gitDecoration-stageModifiedResourceForeground: #e2c08d;
|
||||
--vscode-gitDecoration-stageDeletedResourceForeground: #c74e39;
|
||||
--vscode-gitDecoration-conflictingResourceForeground: #e4676b;
|
||||
--vscode-gitDecoration-submoduleResourceForeground: #8db9e2;
|
||||
--vscode-gitlens-gutterBackgroundColor: rgba(255, 255, 255, 0.07);
|
||||
--vscode-gitlens-gutterForegroundColor: #bebebe;
|
||||
--vscode-gitlens-gutterUncommittedForegroundColor: rgba(0, 188, 242, 0.6);
|
||||
--vscode-gitlens-trailingLineBackgroundColor: rgba(0, 0, 0, 0);
|
||||
--vscode-gitlens-trailingLineForegroundColor: rgba(153, 153, 153, 0.35);
|
||||
--vscode-gitlens-lineHighlightBackgroundColor: rgba(0, 188, 242, 0.2);
|
||||
--vscode-gitlens-lineHighlightOverviewRulerColor: rgba(0, 188, 242, 0.6);
|
||||
--vscode-gitlens-openAutolinkedIssueIconColor: #3fb950;
|
||||
--vscode-gitlens-closedAutolinkedIssueIconColor: #a371f7;
|
||||
--vscode-gitlens-closedPullRequestIconColor: #f85149;
|
||||
--vscode-gitlens-openPullRequestIconColor: #3fb950;
|
||||
--vscode-gitlens-mergedPullRequestIconColor: #a371f7;
|
||||
--vscode-gitlens-unpublishedChangesIconColor: #35b15e;
|
||||
--vscode-gitlens-unpublishedCommitIconColor: #35b15e;
|
||||
--vscode-gitlens-unpulledChangesIconColor: #b15e35;
|
||||
--vscode-gitlens-decorations-addedForegroundColor: #81b88b;
|
||||
--vscode-gitlens-decorations-copiedForegroundColor: #73c991;
|
||||
--vscode-gitlens-decorations-deletedForegroundColor: #c74e39;
|
||||
--vscode-gitlens-decorations-ignoredForegroundColor: #8c8c8c;
|
||||
--vscode-gitlens-decorations-modifiedForegroundColor: #e2c08d;
|
||||
--vscode-gitlens-decorations-untrackedForegroundColor: #73c991;
|
||||
--vscode-gitlens-decorations-renamedForegroundColor: #73c991;
|
||||
--vscode-gitlens-decorations-branchAheadForegroundColor: #35b15e;
|
||||
--vscode-gitlens-decorations-branchBehindForegroundColor: #b15e35;
|
||||
--vscode-gitlens-decorations-branchDivergedForegroundColor: #d8af1b;
|
||||
--vscode-gitlens-decorations-branchMissingUpstreamForegroundColor: #c74e39;
|
||||
--vscode-gitlens-decorations-statusMergingOrRebasingConflictForegroundColor: #c74e39;
|
||||
--vscode-gitlens-decorations-statusMergingOrRebasingForegroundColor: #d8af1b;
|
||||
--vscode-gitlens-decorations-workspaceRepoMissingForegroundColor: #909090;
|
||||
--vscode-gitlens-decorations-workspaceCurrentForegroundColor: #35b15e;
|
||||
--vscode-gitlens-decorations-workspaceRepoOpenForegroundColor: #35b15e;
|
||||
--vscode-gitlens-decorations-worktreeHasUncommittedChangesForegroundColor: #e2c08d;
|
||||
--vscode-gitlens-decorations-worktreeMissingForegroundColor: #c74e39;
|
||||
--vscode-gitlens-graphLane1Color: #15a0bf;
|
||||
--vscode-gitlens-graphLane2Color: #0669f7;
|
||||
--vscode-gitlens-graphLane3Color: #8e00c2;
|
||||
--vscode-gitlens-graphLane4Color: #c517b6;
|
||||
--vscode-gitlens-graphLane5Color: #d90171;
|
||||
--vscode-gitlens-graphLane6Color: #cd0101;
|
||||
--vscode-gitlens-graphLane7Color: #f25d2e;
|
||||
--vscode-gitlens-graphLane8Color: #f2ca33;
|
||||
--vscode-gitlens-graphLane9Color: #7bd938;
|
||||
--vscode-gitlens-graphLane10Color: #2ece9d;
|
||||
--vscode-gitlens-graphChangesColumnAddedColor: #347d39;
|
||||
--vscode-gitlens-graphChangesColumnDeletedColor: #c93c37;
|
||||
--vscode-gitlens-graphMinimapMarkerHeadColor: #05e617;
|
||||
--vscode-gitlens-graphScrollMarkerHeadColor: #05e617;
|
||||
--vscode-gitlens-graphMinimapMarkerUpstreamColor: #09ae17;
|
||||
--vscode-gitlens-graphScrollMarkerUpstreamColor: #09ae17;
|
||||
--vscode-gitlens-graphMinimapMarkerHighlightsColor: #fbff0a;
|
||||
--vscode-gitlens-graphScrollMarkerHighlightsColor: #fbff0a;
|
||||
--vscode-gitlens-graphMinimapMarkerLocalBranchesColor: #3087cf;
|
||||
--vscode-gitlens-graphScrollMarkerLocalBranchesColor: #3087cf;
|
||||
--vscode-gitlens-graphMinimapMarkerPullRequestsColor: #c76801;
|
||||
--vscode-gitlens-graphScrollMarkerPullRequestsColor: #c76801;
|
||||
--vscode-gitlens-graphMinimapMarkerRemoteBranchesColor: #2b5e88;
|
||||
--vscode-gitlens-graphScrollMarkerRemoteBranchesColor: #2b5e88;
|
||||
--vscode-gitlens-graphMinimapMarkerStashesColor: #b34db3;
|
||||
--vscode-gitlens-graphScrollMarkerStashesColor: #b34db3;
|
||||
--vscode-gitlens-graphMinimapMarkerTagsColor: #6b562e;
|
||||
--vscode-gitlens-graphScrollMarkerTagsColor: #6b562e;
|
||||
--vscode-gitlens-launchpadIndicatorMergeableColor: #3fb950;
|
||||
--vscode-gitlens-launchpadIndicatorMergeableHoverColor: #3fb950;
|
||||
--vscode-gitlens-launchpadIndicatorBlockedColor: #c74e39;
|
||||
--vscode-gitlens-launchpadIndicatorBlockedHoverColor: #c74e39;
|
||||
--vscode-gitlens-launchpadIndicatorAttentionColor: #d8af1b;
|
||||
--vscode-gitlens-launchpadIndicatorAttentionHoverColor: #d8af1b;
|
||||
--vscode-remoteHub-decorations-addedForegroundColor: #81b88b;
|
||||
--vscode-remoteHub-decorations-modifiedForegroundColor: #e2c08d;
|
||||
--vscode-remoteHub-decorations-deletedForegroundColor: #c74e39;
|
||||
--vscode-remoteHub-decorations-submoduleForegroundColor: #8db9e2;
|
||||
--vscode-remoteHub-decorations-conflictForegroundColor: #e4676b;
|
||||
--vscode-remoteHub-decorations-incomingAddedForegroundColor: #81b88b;
|
||||
--vscode-remoteHub-decorations-incomingModifiedForegroundColor: #e2c08d;
|
||||
--vscode-remoteHub-decorations-incomingDeletedForegroundColor: #c74e39;
|
||||
--vscode-remoteHub-decorations-incomingRenamedForegroundColor: #73c991;
|
||||
--vscode-remoteHub-decorations-possibleConflictForegroundColor: #cca700;
|
||||
--vscode-remoteHub-decorations-ignoredResourceForeground: #8c8c8c;
|
||||
--vscode-remoteHub-decorations-workspaceRepositoriesView-hasUncommittedChangesForegroundColor: #e2c08d;
|
||||
--vscode-rust_analyzer-syntaxTreeBorder: #ffffff;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="612" height="612" viewBox="0 0 612 612" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="gradient_1" gradientUnits="userSpaceOnUse" x1="300" y1="0" x2="300" y2="600">
|
||||
<stop offset="0" stop-color="#A1A7F6" />
|
||||
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.2" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient_2" gradientUnits="userSpaceOnUse" x1="110.5" y1="0" x2="110.5" y2="221">
|
||||
<stop offset="0.468" stop-color="#BFBAF6" />
|
||||
<stop offset="1" stop-color="#FFFFFF" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-219" y="-219" width="221" height="221" id="filter_3">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_4" gradientUnits="userSpaceOnUse" x1="55.5" y1="0" x2="55.5" y2="111">
|
||||
<stop offset="0" stop-color="#FFFFFF" />
|
||||
<stop offset="1" stop-color="#A8A7F3" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-109" y="-109" width="111" height="111" id="filter_5">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_6" gradientUnits="userSpaceOnUse" x1="174" y1="0" x2="174" y2="348">
|
||||
<stop offset="0.182" stop-color="#A594F6" />
|
||||
<stop offset="1" stop-color="#F4E5FF" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-346" y="-346" width="348" height="348" id="filter_7">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_8" gradientUnits="userSpaceOnUse" x1="57" y1="0" x2="57" y2="114">
|
||||
<stop offset="0" stop-color="#FFFFFF" />
|
||||
<stop offset="0.614" stop-color="#C7BAF8" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-112" y="-112" width="114" height="114" id="filter_9">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g transform="translate(6 2)">
|
||||
<g>
|
||||
<path d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z" fill="#5A00FF" fill-rule="evenodd" />
|
||||
<path d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z" fill="url(#gradient_1)" fill-rule="evenodd" />
|
||||
</g>
|
||||
<path d="M0 110.5C0 49.4725 49.4725 0 110.5 0C171.527 0 221 49.4725 221 110.5C221 171.527 171.527 221 110.5 221C49.4725 221 0 171.527 0 110.5Z" fill="url(#gradient_2)" fill-rule="evenodd" filter="url(#filter_3)" transform="translate(293 324)" />
|
||||
<path d="M0 55.5C0 24.8482 24.8482 0 55.5 0C86.1518 0 111 24.8482 111 55.5C111 86.1518 86.1518 111 55.5 111C24.8482 111 0 86.1518 0 55.5Z" fill="url(#gradient_4)" fill-rule="evenodd" filter="url(#filter_5)" transform="translate(48 269)" />
|
||||
<path d="M0 174C0 77.9024 77.9024 0 174 0C270.098 0 348 77.9024 348 174C348 270.098 270.098 348 174 348C77.9024 348 0 270.098 0 174Z" fill="url(#gradient_6)" fill-rule="evenodd" filter="url(#filter_7)" transform="translate(188 56)" />
|
||||
<path d="M0 57C0 25.5198 25.5198 0 57 0C88.4802 0 114 25.5198 114 57C114 88.4802 88.4802 114 57 114C25.5198 114 0 88.4802 0 57Z" fill="url(#gradient_8)" fill-rule="evenodd" filter="url(#filter_9)" transform="translate(388 129)" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 5.1 KiB |
115
news/src/App.vue
@ -1,115 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import OmHeader from './components/Header.vue';
|
||||
import OmWhatNews from './components/WhatNews.vue';
|
||||
import OmMoreFeature from './components/MoreFeature.vue';
|
||||
import OmCoreFeature from './components/CoreFeature.vue';
|
||||
import OmSponsor from './components/Sponsor.vue';
|
||||
import OmResource from './components/Resource.vue';
|
||||
import OmTroubleshoot from './components/Troubleshoot.vue';
|
||||
import OmContributor from './components/Contributor.vue';
|
||||
|
||||
import data from './data.json';``
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="openmcp-news-root">
|
||||
<om-header :version="data.version" />
|
||||
|
||||
<!-- 1. 📣 What is news in OpenMCP -->
|
||||
<om-what-news :version="data.version" :changelogs="data.changelogs" />
|
||||
|
||||
<!-- 2. 🐳 Learn more features -->
|
||||
<om-more-feature />
|
||||
|
||||
<!-- 3. ✨ Core Features -->
|
||||
<om-core-feature />
|
||||
|
||||
<!-- 4. ❤️ How to sponsor -->
|
||||
<om-sponsor />
|
||||
|
||||
<!-- 5. 📚 Resources -->
|
||||
<om-resource />
|
||||
|
||||
<!-- 6. 🔧 Troubleshooting -->
|
||||
<om-troubleshoot />
|
||||
|
||||
<!-- 7. 👥 Contributors -->
|
||||
<om-contributor :contributors="data.contributors" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body {
|
||||
color: var(--foreground, #222);
|
||||
background: var(--background, #fff);
|
||||
font-size: var(--vscode-font-size);
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.openmcp-news-root {
|
||||
color: var(--foreground, #222);
|
||||
background: var(--background, #fff);
|
||||
padding: 32px 0 64px 0;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
max-width: 820px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.release-link {
|
||||
color: #B988D1;
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 8px;
|
||||
display: inline-block;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.release-link:hover {
|
||||
color: #8e5bbf;
|
||||
}
|
||||
|
||||
.news-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.news-badge {
|
||||
display: inline-block;
|
||||
background: #B988D1;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85em;
|
||||
padding: 2px 10px;
|
||||
margin-right: 10px;
|
||||
font-weight: 500;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.openmcp-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
margin: 0 0 32px 0;
|
||||
padding: 0 0 16px 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.openmcp-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.margin-bottom {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
</style>
|
@ -1,98 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import VanillaTilt from 'vanilla-tilt';
|
||||
|
||||
const props = defineProps<{
|
||||
contributors: {
|
||||
username: string;
|
||||
avatarUrl: string;
|
||||
homeUrl: string;
|
||||
}[];
|
||||
}>();
|
||||
|
||||
const tiltElements = reactive<any[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
// 为每个元素应用Tilt效果
|
||||
tiltElements.forEach(el => {
|
||||
VanillaTilt.init(el, {
|
||||
max: 35,
|
||||
speed: 400,
|
||||
glare: true,
|
||||
'max-glare': 0.2
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="troubleshoot-section">
|
||||
<div class="section-title">👥 Contributors</div>
|
||||
<div class="contributors-grid">
|
||||
<div v-for="(contributor, idx) in props.contributors"
|
||||
:key="contributor.username" class="contributor-tilt"
|
||||
:ref="el => tiltElements[idx] = el"
|
||||
>
|
||||
<a :href="contributor.homeUrl" target="_blank">
|
||||
<img :src="contributor.avatarUrl" :alt="contributor.username" class="contributor-avatar" />
|
||||
<span class="contributor-name">{{ contributor.username }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
.contributors-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contributors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.contributor-tilt {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.contributor-tilt a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contributor-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
margin: 0 auto 0.5rem;
|
||||
border: 3px solid #fff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.contributor-name {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
color: #B988D1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Tilt.js 生成的眩光效果自定义 */
|
||||
.tilt-glare {
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
</style>
|
@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<section class="core-section">
|
||||
<div class="section-title">✨ Core Features</div>
|
||||
<ul class="core-list">
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>Full support for all MCP protocols, including <b>stdio</b>, <b>SSE</b>, and <b>streamable HTTP</b> connection modes.</span>
|
||||
<a href="https://openmcp.kirigaya.cn/plugin-tutorial/usage/debug.html" target="_blank">Learn more</a>
|
||||
</li>
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>Connect Multiple MCP Servers and combine more mcp tools in just one instance.</span>
|
||||
<a href="https://openmcp.kirigaya.cn/plugin-tutorial/usage/multi-server.html" target="_blank">Learn more</a>
|
||||
</li>
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>One-click AI Mock data generation & Automatic mcp tool testing width custom and flexible order.</span>
|
||||
<a href="https://openmcp.kirigaya.cn/sdk-tutorial/" target="_blank">Learn more</a>
|
||||
</li>
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>Support function calling based on both <strong>XML</strong> and <strong>openai tools protocols.</strong></span>
|
||||
<a href="https://openmcp.kirigaya.cn/sdk-tutorial/" target="_blank">Learn more</a>
|
||||
</li>
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>Export connection as mcpconfig.json and deploy in openmcp-sdk</span>
|
||||
<a href="https://openmcp.kirigaya.cn/sdk-tutorial/" target="_blank">Learn more</a>
|
||||
</li>
|
||||
<li class="core-card">
|
||||
<span class="core-dot">🧩</span>
|
||||
<span>Native VSCode experience, cross-platform support</span>
|
||||
<a href="https://openmcp.kirigaya.cn/plugin-tutorial/quick-start/acquire-openmcp.html" target="_blank">Learn more</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.core-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.core-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 18px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 950px) {
|
||||
.core-list {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
@media (max-width: 630px) {
|
||||
.core-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.core-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 1.5rem;
|
||||
border-radius: 10px;
|
||||
background-color: var(--vscode-sideBar-background);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s;
|
||||
box-shadow: 0 2px 8px var(--vscode-widget-shadow, rgba(0,0,0,0.08));
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.core-card a {
|
||||
color: #B988D1;
|
||||
}
|
||||
|
||||
.core-card:hover {
|
||||
transform: translateY(-4px) scale(1.025);
|
||||
box-shadow: 0 6px 18px var(--vscode-widget-shadow, rgba(0,0,0,0.14));
|
||||
}
|
||||
|
||||
.core-dot {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
font-size: 30px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
@ -1,119 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
version: {
|
||||
type: String,
|
||||
default: '0.1.9'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="openmcp-header">
|
||||
<div class="openmcp-icon openmcp-logo"></div>
|
||||
<div>
|
||||
<h1>
|
||||
OpenMCP Client<sup><small>{{ props.version }}</small></sup>
|
||||
</h1>
|
||||
<div class="links">
|
||||
<a href="https://github.com/LSTM-Kirigaya/openmcp-client" target="_blank" title="GitHub">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
|
||||
style="vertical-align: middle;">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z">
|
||||
</path>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a href="https://github.com/LSTM-Kirigaya/openmcp-client/releases" target="_blank" title="Releases">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
|
||||
style="vertical-align: middle;">
|
||||
<path
|
||||
d="M21 10.975V8a2 2 0 00-2-2h-6V4.688c.305-.274.5-.668.5-1.11a1.5 1.5 0 00-3 0c0 .442.195.836.5 1.11V6H5a2 2 0 00-2 2v2.998l-.072.005A.999.999 0 002 12v2a1 1 0 001 1v5a2 2 0 002 2h14a2 2 0 002-2v-5a1 1 0 001-1v-1.975a1 1 0 00-.928-.997L21 10.975zm-5 5.025H8v-3h8v3z">
|
||||
</path>
|
||||
</svg>
|
||||
Releases
|
||||
</a>
|
||||
<a href="https://openmcp.kirigaya.cn/" target="_blank" title="Website">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
|
||||
style="vertical-align: middle;">
|
||||
<path
|
||||
d="M12.001 1.993C6.486 1.994 2 6.48 2 11.994c0 5.514 4.486 9.999 10 10 5.515 0 10.001-4.485 10.001-10s-4.486-10-10-10.001zM12 19.994c-4.412 0-8.001-3.589-8.001-8s3.589-8 8-8.001C16.411 3.994 20 7.583 20 11.994c0 4.41-3.589 8-8 8z">
|
||||
</path>
|
||||
<path
|
||||
d="M12.001 8.994l-4.005 4.005 1.414 1.414 2.591-2.591 2.591 2.591 1.414-1.414-4.005-4.005z">
|
||||
</path>
|
||||
</svg>
|
||||
Website
|
||||
</a>
|
||||
<a href="https://discord.com/invite/SKTZRf6NzU" target="_blank" title="Discord">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"
|
||||
style="vertical-align: middle;">
|
||||
<path
|
||||
d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.09.09 0 00-.07.03c-.18.33-.39.76-.53 1.09a16.09 16.09 0 00-4.8 0c-.14-.34-.35-.76-.54-1.09-.01-.02-.04-.03-.07-.03-1.5.26-2.93.71-4.27 1.33-.01 0-.02.01-.03.02-2.72 4.07-3.47 8.03-3.1 11.95 0 .02.01.04.03.05 1.8 1.32 3.53 2.12 5.24 2.65.03.01.06 0 .07-.02.4-.55.76-1.13 1.07-1.74.02-.04 0-.08-.04-.09-.57-.22-1.11-.48-1.64-.78-.04-.02-.04-.08-.01-.11.11-.08.22-.17.33-.25.02-.02.05-.02.07-.01 3.44 1.57 7.15 1.57 10.55 0 .02-.01.05-.01.07.01.11.09.22.17.33.26.04.03.04.09-.01.11-.52.31-1.07.56-1.64.78-.04.01-.05.06-.04.09.32.61.68 1.19 1.07 1.74.03.01.06.02.09.01 1.72-.53 3.45-1.33 5.25-2.65.02-.01.03-.03.03-.05.44-4.53-.73-8.46-3.1-11.95-.01-.01-.02-.02-.04-.02zM8.52 14.91c-.99 0-1.8-.9-1.8-2s.79-2.01 1.8-2.01c1 0 1.81.9 1.81 2.01-.01 1.1-.8 2-1.81 2zm6.96 0c-.99 0-1.8-.9-1.8-2s.79-2.01 1.8-2.01c1 0 1.81.9 1.81 2.01-.01 1.1-.8 2-1.81 2z">
|
||||
</path>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD"
|
||||
target="_blank" title="QQ Group">
|
||||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor">
|
||||
<path d="M824.8 613.2c-16-51.4-34.4-94.6-62.7-165.3C766.5 262.2 689.3 112 511.5 112 331.7 112 256.2 265.2 261 447.9c-28.4 70.8-46.7 113.7-62.7 165.3-34 109.5-23 154.8-14.6 155.8 18 2.2 70.1-82.4 70.1-82.4 0 49 25.2 112.9 79.8 159-26.4 8.1-85.7 29.9-71.6 53.8 11.4 19.3 196.2 12.3 249.5 6.3 53.3 6 238.1 13 249.5-6.3 14.1-23.8-45.3-45.7-71.6-53.8 54.6-46.2 79.8-110.1 79.8-159 0 0 52.1 84.6 70.1 82.4 8.5-1.1 19.5-46.4-14.5-155.8z"></path>
|
||||
</svg>
|
||||
QQ Group
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<hr>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.openmcp-logo {
|
||||
display: inline-block;
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
border-radius: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1.5px solid #B988D1;
|
||||
height: 0;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.openmcp-header h1 {
|
||||
font-size: 2.1rem;
|
||||
font-weight: bold;
|
||||
margin: 0 0 20px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color: #B988D1;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.openmcp-header .links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.openmcp-header .links a {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: color 0.18s;
|
||||
}
|
||||
|
||||
.openmcp-header .links a:hover {
|
||||
color: #8e5bbf;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
@ -1,118 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import VanillaTilt from 'vanilla-tilt';
|
||||
import { onMounted, ref } from 'vue';
|
||||
const container = ref<any>(null);
|
||||
onMounted(() => {
|
||||
VanillaTilt.init(container.value, {
|
||||
max: 12,
|
||||
speed: 400,
|
||||
glare: true,
|
||||
'max-glare': 0.5
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="feature-section">
|
||||
<div class="section-title">🐳 Learn More Features & Usage</div>
|
||||
|
||||
<div style="margin-bottom: 1.1rem;">Learn more about how to use openmcp to build your AI Agent & MCP fast</div>
|
||||
|
||||
<!-- <a href="https://www.youtube.com/embed/S7igsEhcLiw?si=6sqvbYJxSRoFS26g" target="_blank" class="bilibili-player-container" style="display:flex; width: 100%; justify-content: center;">
|
||||
<iframe width="100%" height="580" src="https://www.youtube.com/embed/S7igsEhcLiw?si=6sqvbYJxSRoFS26g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
</a> -->
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=S7igsEhcLiw" target="_blank" class="bilibili-player-container">
|
||||
<img src="https://pic1.zhimg.com/80/v2-951261f789708621a2c34faa5fa6f330_1440w.png" alt="" ref="container">
|
||||
</a>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.feature-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #B988D1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.feature-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: none;
|
||||
border: 1.5px solid #B988D1;
|
||||
border-radius: 10px;
|
||||
padding: 18px 22px;
|
||||
min-width: 180px;
|
||||
flex: 1 1 180px;
|
||||
color: var(--foreground, #222);
|
||||
text-decoration: none;
|
||||
transition: box-shadow 0.15s, border 0.15s;
|
||||
box-shadow: 0 2px 8px 0 rgba(185, 136, 209, 0.04);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
border-color: #8e5bbf;
|
||||
box-shadow: 0 4px 16px 0 rgba(185, 136, 209, 0.12);
|
||||
}
|
||||
|
||||
.feature-card-title {
|
||||
font-weight: bold;
|
||||
color: #B988D1;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.feature-card-desc {
|
||||
font-size: 0.98em;
|
||||
color: var(--foreground, #444);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.bilibili-player-container img {
|
||||
max-width: 100%;
|
||||
box-shadow: 0 4px 12px var(--vscode-widget-shadow);
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
|
||||
.bilibili-player-container {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
margin: 2rem 0;
|
||||
border-radius: .5em;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
@media (max-width: 2700px) {
|
||||
.bilibili-player-container {
|
||||
width: 88%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.bilibili-player-container {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 741px) {
|
||||
.bilibili-player-container iframe {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
@ -1,98 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const svgString = `
|
||||
<svg width="84" height="84" viewBox="0 0 612 612" fill="none" xmlns="http://www.w3.org/2000/svg" class="openmcp-logo">
|
||||
<defs>
|
||||
<linearGradient id="gradient_1" gradientUnits="userSpaceOnUse" x1="300" y1="0" x2="300" y2="600">
|
||||
<stop offset="0" stop-color="#A1A7F6" />
|
||||
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.2" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient_2" gradientUnits="userSpaceOnUse" x1="110.5" y1="0" x2="110.5" y2="221">
|
||||
<stop offset="0.468" stop-color="#BFBAF6" />
|
||||
<stop offset="1" stop-color="#FFFFFF" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-219" y="-219" width="221" height="221" id="filter_3">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_4" gradientUnits="userSpaceOnUse" x1="55.5" y1="0" x2="55.5" y2="111">
|
||||
<stop offset="0" stop-color="#FFFFFF" />
|
||||
<stop offset="1" stop-color="#A8A7F3" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-109" y="-109" width="111" height="111" id="filter_5">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_6" gradientUnits="userSpaceOnUse" x1="174" y1="0" x2="174" y2="348">
|
||||
<stop offset="0.182" stop-color="#A594F6" />
|
||||
<stop offset="1" stop-color="#F4E5FF" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-346" y="-346" width="348" height="348" id="filter_7">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
<linearGradient id="gradient_8" gradientUnits="userSpaceOnUse" x1="57" y1="0" x2="57" y2="114">
|
||||
<stop offset="0" stop-color="#FFFFFF" />
|
||||
<stop offset="0.614" stop-color="#C7BAF8" />
|
||||
</linearGradient>
|
||||
<filter color-interpolation-filters="sRGB" x="-112" y="-112" width="114" height="114" id="filter_9">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
|
||||
<feOffset dx="0" dy="4" />
|
||||
<feGaussianBlur stdDeviation="2" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g transform="translate(6 2)">
|
||||
<g>
|
||||
<path d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z" fill="#5A00FF" fill-rule="evenodd" />
|
||||
<path d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z" fill="url(#gradient_1)" fill-rule="evenodd" />
|
||||
</g>
|
||||
<path d="M0 110.5C0 49.4725 49.4725 0 110.5 0C171.527 0 221 49.4725 221 110.5C221 171.527 171.527 221 110.5 221C49.4725 221 0 171.527 0 110.5Z" fill="url(#gradient_2)" fill-rule="evenodd" filter="url(#filter_3)" transform="translate(293 324)" />
|
||||
<path d="M0 55.5C0 24.8482 24.8482 0 55.5 0C86.1518 0 111 24.8482 111 55.5C111 86.1518 86.1518 111 55.5 111C24.8482 111 0 86.1518 0 55.5Z" fill="url(#gradient_4)" fill-rule="evenodd" filter="url(#filter_5)" transform="translate(48 269)" />
|
||||
<path d="M0 174C0 77.9024 77.9024 0 174 0C270.098 0 348 77.9024 348 174C348 270.098 270.098 348 174 348C77.9024 348 0 270.098 0 174Z" fill="url(#gradient_6)" fill-rule="evenodd" filter="url(#filter_7)" transform="translate(188 56)" />
|
||||
<path d="M0 57C0 25.5198 25.5198 0 57 0C88.4802 0 114 25.5198 114 57C114 88.4802 88.4802 114 57 114C25.5198 114 0 88.4802 0 57Z" fill="url(#gradient_8)" fill-rule="evenodd" filter="url(#filter_9)" transform="translate(388 129)" />
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
const svgContainer = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (svgContainer.value) {
|
||||
svgContainer.value.innerHTML = svgString;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span ref="svgContainer" class="openmcp-logo"></span>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.openmcp-logo {
|
||||
display: inline-block;
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
border-radius: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<section class="resource-section">
|
||||
<div class="section-title">📚 Resources</div>
|
||||
<ul class="resource-list">
|
||||
<li><a data-v-dd258f77="" class="VPNolebaseInlinePreviewLink" relative=""
|
||||
href="https://www.bilibili.com/video/BV1zYGozgEHc" target="_blank" rel="noreferrer">MCP Series Video
|
||||
Tutorials (Under Construction)</a></li>
|
||||
<li><a data-v-dd258f77="" class="VPNolebaseInlinePreviewLink" relative=""
|
||||
href="https://kirigaya.cn/blog/search?q=mcp" target="_blank" rel="noreferrer">Jin Hui's MCP Series
|
||||
Blog</a></li>
|
||||
<li><a data-v-dd258f77="" class="VPNolebaseInlinePreviewLink" relative=""
|
||||
href="https://openmcp.kirigaya.cn/plugin-tutorial" target="_blank" rel="noreferrer">OpenMCP Official
|
||||
Documentation</a></li>
|
||||
<li><a data-v-dd258f77="" class="VPNolebaseInlinePreviewLink" relative=""
|
||||
href="https://openmcp.kirigaya.cn/sdk-tutorial" target="_blank" rel="noreferrer">openmcp-sdk
|
||||
Official Documentation</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.resource-section {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.resource-list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.resource-list li {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.resource-list a {
|
||||
color: #B988D1;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.resource-list a:hover {
|
||||
color: #8e5bbf;
|
||||
}
|
||||
</style>
|
@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<section class="sponsor-section">
|
||||
<div class="section-title">❤️ How to Sponsor</div>
|
||||
<div class="sponsor-content">
|
||||
<p>If you like openmcp, please support our work!</p>
|
||||
|
||||
<a class="sponsor-item" href="https://github.com/LSTM-Kirigaya/openmcp-client" target="_blank">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle;">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z">
|
||||
</path>
|
||||
</svg>
|
||||
Github
|
||||
</a>
|
||||
<br>
|
||||
<a class="sponsor-item" href="https://afdian.com/a/kirigaya" target="_blank">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.31-8.86c-1.77-.45-2.34-.94-2.34-1.67 0-.84.79-1.43 2.1-1.43 1.38 0 1.9.66 1.94 1.64h1.71c-.05-1.34-.87-2.57-2.49-2.97V5H10.9v1.69c-1.51.32-2.72 1.3-2.72 2.81 0 1.79 1.49 2.69 3.66 3.21 1.95.46 2.34 1.15 2.34 1.87 0 .53-.39 1.39-2.1 1.39-1.6 0-2.23-.72-2.32-1.64H8.04c.1 1.7 1.36 2.66 2.86 2.97V19h2.34v-1.67c1.52-.29 2.72-1.16 2.73-2.77-.01-2.2-1.9-2.96-3.66-3.42z">
|
||||
</path>
|
||||
</svg>
|
||||
爱发电</a>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.sponsor-section {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.sponsor-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sponsor-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #B988D1;
|
||||
color: var(--vscode-button-foreground);
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.sponsor-item:hover {
|
||||
background-color: #8e5bbf;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.sponsor-item:active {
|
||||
transform: scale(0.95);
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
</style>
|
@ -1,44 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="troubleshoot-section">
|
||||
<div class="section-title">🔧 Troubleshooting</div>
|
||||
<ul class="troubleshoot-list">
|
||||
<li>
|
||||
If the interface displays abnormally, try restarting VSCode or report an <a href="https://github.com/LSTM-Kirigaya/openmcp-client/issues" target="_blank">Issue</a>.
|
||||
</li>
|
||||
<li>
|
||||
For more questions, refer to the <a href="https://openmcp.kirigaya.cn/" target="_blank">official
|
||||
documentation</a> or join the community.</li>
|
||||
|
||||
<li>Contant Jinhui via <a href="mailto:1193466151@qq.com">1193466151@qq.com</a> to seek technical consultation or business cooperation.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.troubleshoot-section {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.troubleshoot-list {
|
||||
list-style: disc inside;
|
||||
padding-left: 18px;
|
||||
color: var(--foreground, #444);
|
||||
}
|
||||
|
||||
.troubleshoot-list li {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.troubleshoot-list a {
|
||||
color: #B988D1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.troubleshoot-list a:hover {
|
||||
color: #8e5bbf;
|
||||
}
|
||||
</style>
|
@ -1,73 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
version: {
|
||||
type: String,
|
||||
default: '0.1.9'
|
||||
},
|
||||
changelogs: {
|
||||
type: Array as () => string[],
|
||||
default: () => [
|
||||
'Brand new VSCode WebView adaptation, smoother experience',
|
||||
'Support for AI Mock, toolchain visualization, and automatic topology detection',
|
||||
'Multi-model concurrency, enhanced plugin capabilities'
|
||||
]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="news-section">
|
||||
<div class="news-title">
|
||||
<span>📣 What's New in <span class="highlight">{{ props.version }}</span></span>
|
||||
</div>
|
||||
<div class="news-content">
|
||||
<ul class="news-list">
|
||||
<li v-for="(log, index) in props.changelogs" :key="index">
|
||||
<span class="news-badge">{{ '/' }}</span>
|
||||
<span>{{ log }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
<a class="release-link" href="https://openmcp.kirigaya.cn/preview/changelog.html" target="_blank"
|
||||
rel="noopener">View History Changelog →</a>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.news-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.news-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #B988D1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.news-title .logo {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin-right: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.news-title .highlight {
|
||||
color: #B988D1;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
margin: 2rem 0;
|
||||
background-color: var(--vscode-sideBar-background);
|
||||
box-shadow: 0 2px 8px var(--vscode-widget-shadow);
|
||||
}
|
||||
|
||||
.news-content li {
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
</style>
|
@ -1,151 +0,0 @@
|
||||
:root {
|
||||
--font-monospace-family: var(--vscode-editor-font-family);
|
||||
--font-monospace-weight: var(--vscode-editor-font-weight);
|
||||
--font-monospace-size: var(--vscode-editor-font-size);
|
||||
|
||||
--link-foreground: var(--vscode-textLink-foreground);
|
||||
--link-active: var(--vscode-textLink-activeForeground);
|
||||
|
||||
/* UI & Control */
|
||||
--input-active-background: var(--vscode-input-background);
|
||||
--input-active-border: var(--vscode-focusBorder);
|
||||
--input-active-foreground: var(--vscode-input-foreground);
|
||||
|
||||
--input-error-background: var(--vscode-inputValidation-errorBackground);
|
||||
--input-error-border: var(--vscode-inputValidation-errorBorder);
|
||||
--input-error-foreground: var(--vscode-inputValidation-errorForeground);
|
||||
|
||||
--input-foreground: var(--vscode-input-foreground);
|
||||
--input-background: var(--vscode-input-background);
|
||||
--input-border: var(--vscode-input-border);
|
||||
--input-hover: var(--vscode-input-background);
|
||||
--input-placeholder: var(--vscode-input-placeholderForeground);
|
||||
--input-radius: 0px;
|
||||
|
||||
--scrollbar-background: var(--vscode-scrollbarSlider-background);
|
||||
--scrollbar-hover: var(--vscode-scrollbarSlider-hoverBackground);
|
||||
--scrollbar-active: var(--vscode-scrollbarSlider-activeBackground);
|
||||
|
||||
/* Window */
|
||||
--title-bar: #1f1f1f;
|
||||
--title-color: #fff;
|
||||
--foreground: var(--vscode-editor-foreground);
|
||||
--background: var(--vscode-editor-background);
|
||||
--label: rgb(189, 189, 189);
|
||||
--shadow: #000;
|
||||
--border: var(--vscode-input-border);
|
||||
--window-button-hover: rgba(255,255,255,0.1);
|
||||
--window-button-active: rgba(255,255,255,0.2);
|
||||
--window-blur-background: rgba(0,0,0,0.25);
|
||||
|
||||
--window-title-foreground: var(--foreground);
|
||||
--window-background: var(--sidebar);
|
||||
--window-border: transparent;
|
||||
--window-radius: 0px;
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar: var(--vscode-sideBar-background);
|
||||
--sidebar-border: var(--vscode-sideBar-border);
|
||||
--sidebar-min-width: 280px;
|
||||
|
||||
--sidebar-item-text: var(--vscode-list-inactiveSelectionForeground);
|
||||
--sidebar-item-border: var(--vscode-input-border);
|
||||
--sidebar-item-background: var(--sidebar);
|
||||
--sidebar-item-selected: var(--vscode-list-inactiveSelectionBackground);
|
||||
--sidebar-item-hover: var(--vscode-list-hoverBackground);
|
||||
--sidebar-item-max-height: 40px;
|
||||
--sidebar-item-radix-background: var(--vscode-breadcrumb-background);
|
||||
|
||||
--sidebar-group-text: var(--vscode-sideBarSectionHeader-foreground);
|
||||
--sidebar-group-border: var(--vscode-sideBarSectionHeader-border);
|
||||
--sidebar-group-background: var(--vscode-sideBarSectionHeader-background);
|
||||
|
||||
/* Labels */
|
||||
--signalSize-background: rgba(0,0,0,0.5);
|
||||
--signalSize-border: rgba(255,255,255,0.2);
|
||||
--signalSize-color: var(--foreground);
|
||||
|
||||
/* Color Picker */
|
||||
--picker-swatch-size: 15px;
|
||||
--picker-swatch-cols: 8;
|
||||
--picker-background: var(--vscode-breadcrumbPicker-background);
|
||||
--picker-border: var(--vscode-dropdown-border);
|
||||
|
||||
/* Search */
|
||||
--search-background: var(--vscode-quickInput-background);
|
||||
--search-border: var(--border);
|
||||
--search-panel-background: transparent;
|
||||
--search-panel-border: var(--vscode-pickerGroup-border);
|
||||
--search-panel-text: var(--vscode-quickInput-foreground);
|
||||
--search-label: var(--foreground);
|
||||
--search-selected-background: var(--vscode-list-inactiveSelectionBackground);
|
||||
|
||||
/* Properties */
|
||||
--properties-background: var(--vscode-breadcrumb-background);
|
||||
--properties-border: var(--border);
|
||||
|
||||
/* Navbar */
|
||||
--navBar-background: var(--sidebar);
|
||||
--navBar-height: 32px;
|
||||
--navBar-button: transparent;
|
||||
--navBar-button-text: var(--foreground);
|
||||
--navBar-group-background: var(--background);
|
||||
--navBar-preview-background: var(--vscode-scrollbarSlider-background);
|
||||
--navBar-slider-border: var(--foreground);
|
||||
|
||||
/* Buttons */
|
||||
--button: var(--vscode-button-background);
|
||||
--button-text: var(--vscode-button-foreground);
|
||||
--button-hover: var(--vscode-button-hoverBackground);
|
||||
--button-active: var(--vscode-button-hoverBackground);
|
||||
--button-disabled: var(--vscode-activityBar-background);
|
||||
--button-disabled-text: var(--vscode-activityBar-inactiveForeground);
|
||||
|
||||
/* Grid Lines */
|
||||
--grid-dash: 2;
|
||||
--grid-space: 4;
|
||||
--grid-line: var(--vscode-editorIndentGuide-background);
|
||||
--grid-tick: var(--vscode-editorIndentGuide-activeBackground);
|
||||
|
||||
/* Cursor */
|
||||
--cursor: var(--vscode-editorCursor-foreground);
|
||||
--cursor-ghost: rgba(255, 255, 255, 0.2);
|
||||
--cursor-width: 2;
|
||||
|
||||
/* X-Axis */
|
||||
--axis-height: 38px;
|
||||
--axis-line: var(--border);
|
||||
--axis-background: var(--vscode-sideBar-background);
|
||||
--axis-foreground: var(--foreground);
|
||||
|
||||
/* Signals */
|
||||
--signal-highlight: var(--vscode-list-inactiveSelectionBackground);
|
||||
|
||||
/* Colors */
|
||||
--accent: var(--vscode-button-background);
|
||||
--accent-dim: #234175;
|
||||
--accent-bright: #24c5f7;
|
||||
--accent-hover: var(--vscode-button-hoverBackground);
|
||||
|
||||
--vscode-font-size: 13px;
|
||||
|
||||
--color-red: #ff5252;
|
||||
--color-pink: #ff4081;
|
||||
--color-purple: #e040fb;
|
||||
--color-deepPurple: #7c4dff;
|
||||
--color-indigo: #536dfe;
|
||||
--color-blue: #448aff;
|
||||
--color-lightBlue: #40c4ff;
|
||||
--color-cyan: #18ffff;
|
||||
--color-teal: #64ffda;
|
||||
--color-green: #69f0ae;
|
||||
--color-lightGreen: #b2ff59;
|
||||
--color-lime: #eeff41;
|
||||
--color-yellow: #ffff00;
|
||||
--color-amber: #ffd740;
|
||||
--color-orange: #ffab40;
|
||||
--color-deepOrange: #ff6e40;
|
||||
|
||||
/* Settings */
|
||||
--settings-action-background: var(--background);
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
{
|
||||
"version": "0.1.9",
|
||||
"changelogs": [
|
||||
"Add mook functionality: Automatically fill in test tool form data using random seeds or AI generation.",
|
||||
"Add tool self-check functionality: Under openmcp's tool, click 'Tool Self-Check' on the right side of 'Tool Module' to enter self-check mode. In this mode, users can define the topological order of tool execution and perform automatic detection in one go.",
|
||||
"Fix issue #44: Complete platform adaptation for link redirection.",
|
||||
"Fix issue #36: Ensure successful startup when not opening a folder.",
|
||||
"Fix issue #45: Array type parameters are not supported.",
|
||||
"Fix the issue of abnormal dialog styles when pasting multi-line conversations into the dialog box."
|
||||
],
|
||||
"contributors": [
|
||||
{
|
||||
"username": "LSTM-Kirigaya",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/59416203?v=4",
|
||||
"homeUrl": "https://github.com/LSTM-Kirigaya"
|
||||
},
|
||||
{
|
||||
"username": "li1553770945",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/55867654?v=4",
|
||||
"homeUrl": "https://github.com/li1553770945"
|
||||
},
|
||||
{
|
||||
"username": "STUzhy",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/129645384?v=4",
|
||||
"homeUrl": "https://github.com/STUzhy"
|
||||
},
|
||||
{
|
||||
"username": "appli456",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/8943691?v=4",
|
||||
"homeUrl": "https://github.com/appli456"
|
||||
},
|
||||
{
|
||||
"username": "cybermanhao",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/37235140?v=4",
|
||||
"homeUrl": "https://github.com/cybermanhao"
|
||||
},
|
||||
{
|
||||
"username": "ArcStellar2025",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/115577936?v=4",
|
||||
"homeUrl": "https://github.com/ArcStellar2025"
|
||||
},
|
||||
{
|
||||
"username": "ZYD045692",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/206822796?v=4",
|
||||
"homeUrl": "https://github.com/ZYD045692"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import './css/vscode.css';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
|
||||
createApp(App).mount('#app')
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*",
|
||||
"eslint.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import { viteSingleFile } from 'vite-plugin-singlefile'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
viteSingleFile(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
})
|
18956
package-lock.json
generated
111
package.json
@ -2,9 +2,8 @@
|
||||
"name": "openmcp",
|
||||
"displayName": "OpenMCP",
|
||||
"description": "An all in one MCP Client/TestTool",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.0",
|
||||
"publisher": "kirigaya",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "kirigaya",
|
||||
"email": "1193466151@qq.com"
|
||||
@ -20,16 +19,13 @@
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./dist/extension.cjs.js",
|
||||
"main": "./dist/extension.js",
|
||||
"icon": "icons/openmcp.png",
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"properties": {}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "openmcp.showOpenMCP",
|
||||
"title": "%openmcp.showOpenMCP.title%",
|
||||
"title": "展示 OpenMCP",
|
||||
"category": "openmcp",
|
||||
"icon": {
|
||||
"light": "./icons/light/protocol.svg",
|
||||
@ -38,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.revealWebviewPanel",
|
||||
"title": "%openmcp.sidebar.workspace-connection.revealWebviewPanel.title%",
|
||||
"title": "连接",
|
||||
"category": "openmcp",
|
||||
"icon": {
|
||||
"light": "./icons/light/protocol.svg",
|
||||
@ -47,31 +43,31 @@
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.deleteConnection",
|
||||
"title": "%openmcp.sidebar.workspace-connection.deleteConnection.title%",
|
||||
"title": "删除连接",
|
||||
"category": "openmcp",
|
||||
"icon": "$(trash)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.refresh",
|
||||
"title": "%openmcp.sidebar.workspace-connection.refresh.title%",
|
||||
"title": "刷新",
|
||||
"category": "openmcp",
|
||||
"icon": "$(refresh)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.addConnection",
|
||||
"title": "%openmcp.sidebar.workspace-connection.addConnection.title%",
|
||||
"title": "添加连接",
|
||||
"category": "openmcp",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.workspace-connection.openConfiguration",
|
||||
"title": "%openmcp.sidebar.workspace-connection.openConfiguration.title%",
|
||||
"title": "打开配置",
|
||||
"category": "openmcp",
|
||||
"icon": "$(gear)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.revealWebviewPanel",
|
||||
"title": "%openmcp.sidebar.installed-connection.revealWebviewPanel.title%",
|
||||
"title": "连接",
|
||||
"category": "openmcp",
|
||||
"icon": {
|
||||
"light": "./icons/light/protocol.svg",
|
||||
@ -80,37 +76,31 @@
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.deleteConnection",
|
||||
"title": "%openmcp.sidebar.installed-connection.deleteConnection.title%",
|
||||
"title": "删除连接",
|
||||
"category": "openmcp",
|
||||
"icon": "$(trash)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.refresh",
|
||||
"title": "%openmcp.sidebar.installed-connection.refresh.title%",
|
||||
"title": "刷新",
|
||||
"category": "openmcp",
|
||||
"icon": "$(refresh)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.addConnection",
|
||||
"title": "%openmcp.sidebar.installed-connection.addConnection.title%",
|
||||
"title": "添加连接",
|
||||
"category": "openmcp",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.sidebar.installed-connection.openConfiguration",
|
||||
"title": "%openmcp.sidebar.installed-connection.openConfiguration.title%",
|
||||
"title": "打开配置",
|
||||
"category": "openmcp",
|
||||
"icon": "$(gear)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.hook.test-ocr",
|
||||
"title": "%openmcp.hook.test-ocr.title%",
|
||||
"category": "openmcp",
|
||||
"icon": "$(test)"
|
||||
},
|
||||
{
|
||||
"command": "openmcp.hook.test-news",
|
||||
"title": "%openmcp.hook.test-news.title%",
|
||||
"title": "测试 OCR",
|
||||
"category": "openmcp",
|
||||
"icon": "$(test)"
|
||||
}
|
||||
@ -204,19 +194,19 @@
|
||||
{
|
||||
"id": "openmcp.sidebar.workspace-connection",
|
||||
"icon": "./icons/protocol.svg",
|
||||
"name": "%openmcp.sidebar.workspace-connection.view.title%",
|
||||
"name": "MCP 连接 (工作区)",
|
||||
"type": "tree"
|
||||
},
|
||||
{
|
||||
"id": "openmcp.sidebar.installed-connection",
|
||||
"icon": "./icons/protocol.svg",
|
||||
"name": "%openmcp.sidebar.installed-connection.view.title%",
|
||||
"name": "安装的 MCP 服务器",
|
||||
"type": "tree"
|
||||
},
|
||||
{
|
||||
"id": "openmcp.sidebar.help",
|
||||
"icon": "./icons/protocol.svg",
|
||||
"name": "%openmcp.sidebar.help.view.title%",
|
||||
"name": "入门与帮助",
|
||||
"type": "tree"
|
||||
}
|
||||
]
|
||||
@ -224,79 +214,50 @@
|
||||
},
|
||||
"workspaces": [
|
||||
"service",
|
||||
"renderer"
|
||||
"renderer",
|
||||
"software"
|
||||
],
|
||||
"scripts": {
|
||||
"setup": "yarn install && yarn prepare:ocr",
|
||||
"serve": "turbo serve",
|
||||
"build": "turbo build && tsc -p ./ && node esbuild.config.js",
|
||||
"build:plugin": "yarn build && tsc && vsce package --allow-package-all-secrets",
|
||||
"vscode:prepublish": "node esbuild.config.js",
|
||||
"dev": "turbo dev --filter=!@openmcp/electron",
|
||||
"dev:electron": "turbo dev --filter=@openmcp/electron",
|
||||
"dev:all": "turbo dev",
|
||||
"build": "turbo build --filter=!@openmcp/electron",
|
||||
"build:electron": "turbo build --filter=@openmcp/electron",
|
||||
"build:all": "turbo build",
|
||||
"vscode:prepublish": "webpack --mode production",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "yarn build",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"lint": "eslint src --ext ts",
|
||||
"test": "node ./dist/test/e2e/runTest.js",
|
||||
"prepare:ocr": "rollup -c rollup.tesseract.js --bundleConfigAsCjs",
|
||||
"build:news": "npx tsx scripts/update-news-data.mts",
|
||||
"build:task-loop": "npx vite build --config renderer/vite.config.task-loop.mjs && node renderer/scripts/task-loop.build.mjs"
|
||||
"test": "node ./out/test/runTest.js",
|
||||
"prepare:ocr": "webpack --config webpack/webpack.tesseract.js",
|
||||
"build:task-loop": "webpack --config webpack/webpack.task-loop.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||
"@modelcontextprotocol/sdk": "^1.10.2",
|
||||
"@openmcp/service": "*",
|
||||
"@seald-io/nedb": "^4.1.1",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"abort-controller": "^3.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"axios": "^1.9.0",
|
||||
"axios": "^1.7.7",
|
||||
"bson": "^6.8.0",
|
||||
"form-data-encoder": "^1.7.2",
|
||||
"formdata-node": "^4.3.2",
|
||||
"glob": "^7.2.3",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"mocha": "^10.8.2",
|
||||
"openai": "^5.0.1",
|
||||
"openai": "^4.93.0",
|
||||
"pako": "^2.1.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"tesseract.js": "^6.0.1",
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^11.1.0",
|
||||
"ws": "^8.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-inject": "^5.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/node": "^22.15.29",
|
||||
"@types/node": "16.x",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"@types/sinon": "^17.0.4",
|
||||
"@types/vscode": "^1.72.0",
|
||||
"@vscode/test-cli": "^0.0.11",
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "^3.5.0",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"fork-ts-checker-webpack-plugin": "^9.1.0",
|
||||
"null-loader": "^4.0.1",
|
||||
"ompipe": "^1.0.2",
|
||||
"rollup": "^4.43.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-visualizer": "^6.0.1",
|
||||
"sinon": "^21.0.0",
|
||||
"ts-loader": "^9.5.1",
|
||||
"turbo": "^2.5.3",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-static-copy": "^3.0.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.6",
|
||||
"vue-tsc": "^2.2.10",
|
||||
"webpack": "^5.99.5",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22"
|
||||
"packageManager": "npm@10.0.0"
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"openmcp.showOpenMCP.title": "OpenMCP を表示",
|
||||
"openmcp.sidebar.workspace-connection.revealWebviewPanel.title": "接続",
|
||||
"openmcp.sidebar.workspace-connection.deleteConnection.title": "接続を削除",
|
||||
"openmcp.sidebar.workspace-connection.refresh.title": "更新",
|
||||
"openmcp.sidebar.workspace-connection.addConnection.title": "接続を追加",
|
||||
"openmcp.sidebar.workspace-connection.openConfiguration.title": "設定を開く",
|
||||
"openmcp.sidebar.installed-connection.revealWebviewPanel.title": "接続",
|
||||
"openmcp.sidebar.installed-connection.deleteConnection.title": "接続を削除",
|
||||
"openmcp.sidebar.installed-connection.refresh.title": "更新",
|
||||
"openmcp.sidebar.installed-connection.addConnection.title": "接続を追加",
|
||||
"openmcp.sidebar.installed-connection.openConfiguration.title": "設定を開く",
|
||||
"openmcp.hook.test-ocr.title": "OCR をテスト",
|
||||
"openmcp.sidebar.workspace-connection.view.title": "MCP 接続(ワークスペース)",
|
||||
"openmcp.sidebar.installed-connection.view.title": "インストール済み MCP サーバー",
|
||||
"openmcp.sidebar.help.view.title": "はじめに・ヘルプ",
|
||||
"openmcp.hook.test-news.title": "テストニュースレポート"
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"openmcp.showOpenMCP.title": "Show OpenMCP",
|
||||
"openmcp.sidebar.workspace-connection.revealWebviewPanel.title": "Connect",
|
||||
"openmcp.sidebar.workspace-connection.deleteConnection.title": "Delete Connection",
|
||||
"openmcp.sidebar.workspace-connection.refresh.title": "Refresh",
|
||||
"openmcp.sidebar.workspace-connection.addConnection.title": "Add Connection",
|
||||
"openmcp.sidebar.workspace-connection.openConfiguration.title": "Open Configuration",
|
||||
"openmcp.sidebar.installed-connection.revealWebviewPanel.title": "Connect",
|
||||
"openmcp.sidebar.installed-connection.deleteConnection.title": "Delete Connection",
|
||||
"openmcp.sidebar.installed-connection.refresh.title": "Refresh",
|
||||
"openmcp.sidebar.installed-connection.addConnection.title": "Add Connection",
|
||||
"openmcp.sidebar.installed-connection.openConfiguration.title": "Open Configuration",
|
||||
"openmcp.hook.test-ocr.title": "Test OCR",
|
||||
"openmcp.sidebar.workspace-connection.view.title": "MCP Connections (Workspace)",
|
||||
"openmcp.sidebar.installed-connection.view.title": "Installed MCP Servers",
|
||||
"openmcp.sidebar.help.view.title": "Getting Started & Help",
|
||||
"openmcp.hook.test-news.title": "test news report"
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"openmcp.showOpenMCP.title": "展示 OpenMCP",
|
||||
"openmcp.sidebar.workspace-connection.revealWebviewPanel.title": "连接",
|
||||
"openmcp.sidebar.workspace-connection.deleteConnection.title": "删除连接",
|
||||
"openmcp.sidebar.workspace-connection.refresh.title": "刷新",
|
||||
"openmcp.sidebar.workspace-connection.addConnection.title": "添加连接",
|
||||
"openmcp.sidebar.workspace-connection.openConfiguration.title": "打开配置",
|
||||
"openmcp.sidebar.installed-connection.revealWebviewPanel.title": "连接",
|
||||
"openmcp.sidebar.installed-connection.deleteConnection.title": "删除连接",
|
||||
"openmcp.sidebar.installed-connection.refresh.title": "刷新",
|
||||
"openmcp.sidebar.installed-connection.addConnection.title": "添加连接",
|
||||
"openmcp.sidebar.installed-connection.openConfiguration.title": "打开配置",
|
||||
"openmcp.hook.test-ocr.title": "测试 OCR",
|
||||
"openmcp.sidebar.workspace-connection.view.title": "MCP 连接 (工作区)",
|
||||
"openmcp.sidebar.installed-connection.view.title": "安装的 MCP 服务器",
|
||||
"openmcp.sidebar.help.view.title": "入门与帮助",
|
||||
"openmcp.hook.test-news.title": "测试新闻报告"
|
||||
}
|
2334
news/package-lock.json → renderer/package-lock.json
generated
@ -16,17 +16,8 @@
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.18.6",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@faker-js/faker": "^9.8.0",
|
||||
"chalk": "^5.4.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"core-js": "^3.8.3",
|
||||
"d3": "^7.9.0",
|
||||
"element-plus": "^2.9.9",
|
||||
"elkjs": "^0.10.0",
|
||||
"json-schema-faker": "^0.5.9",
|
||||
"katex": "^0.16.21",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
@ -37,37 +28,19 @@
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.0",
|
||||
"vue-router": "^4.5.0",
|
||||
"xml2js": "^0.6.2"
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.1",
|
||||
"@babel/plugin-proposal-decorators": "^7.27.1",
|
||||
"@babel/plugin-syntax-import-attributes": "^7.27.1",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/plugin-transform-typescript": "^7.27.1",
|
||||
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
||||
"@rollup/pluginutils": "^5.1.4",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vue/babel-plugin-jsx": "^1.4.0",
|
||||
"@vue/devtools-core": "^7.7.6",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"error-stack-parser-es": "^0.1.5",
|
||||
"kolorist": "^1.8.0",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"rollup": "^4.41.0",
|
||||
"sirv": "^3.0.1",
|
||||
"typescript": "~5.8.0",
|
||||
"vite": "^6.2.4",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vue-tsc": "^2.2.8"
|
||||
},
|
||||
"main": "index.js",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 54 KiB |
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4870215 */
|
||||
src: url('iconfont.woff2?t=1751568095152') format('woff2'),
|
||||
url('iconfont.woff?t=1751568095152') format('woff'),
|
||||
url('iconfont.ttf?t=1751568095152') format('truetype');
|
||||
src: url('iconfont.woff2?t=1746703816245') format('woff2'),
|
||||
url('iconfont.woff?t=1746703816245') format('woff'),
|
||||
url('iconfont.ttf?t=1746703816245') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,38 +13,6 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-pin:before {
|
||||
content: "\e863";
|
||||
}
|
||||
|
||||
.icon-serial:before {
|
||||
content: "\e78f";
|
||||
}
|
||||
|
||||
.icon-deploy:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-suffix-xml:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.icon-MCP:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.icon-wendang:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
|
||||
.icon-proxy:before {
|
||||
content: "\e723";
|
||||
}
|
||||
|
||||
.icon-parallel:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-waiting:before {
|
||||
content: "\e6d0";
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
# OpenRouter Icon Placeholder
|
||||
# This would normally be an actual .ico file
|
||||
# For now, using a placeholder that follows the naming convention
|
@ -3,15 +3,6 @@
|
||||
--main-color: #CB81DA;
|
||||
--main-dark-color: #2D323B;
|
||||
--main-light-color: rgba(203, 129, 218, 0.7);
|
||||
--main-light-color-90: rgba(203, 129, 218, 0.9);
|
||||
--main-light-color-80: rgba(203, 129, 218, 0.8);
|
||||
--main-light-color-70: rgba(203, 129, 218, 0.7);
|
||||
--main-light-color-60: rgba(203, 129, 218, 0.6);
|
||||
--main-light-color-50: rgba(203, 129, 218, 0.5);
|
||||
--main-light-color-40: rgba(203, 129, 218, 0.4);
|
||||
--main-light-color-30: rgba(203, 129, 218, 0.3);
|
||||
--main-light-color-20: rgba(203, 129, 218, 0.2);
|
||||
--main-light-color-10: rgba(203, 129, 218, 0.1);
|
||||
--sidebar-width: 330px;
|
||||
--right-nav-width: 50px;
|
||||
--time-scale-height: 30px;
|
||||
@ -230,11 +221,4 @@ a {
|
||||
|
||||
.el-dropdown-menu__item:hover {
|
||||
background-color: var(--background) !important;
|
||||
}
|
||||
|
||||
/* codemirror */
|
||||
.ͼo,
|
||||
.ͼo .cm-gutters {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ function recreateDir(filePath) {
|
||||
|
||||
const currentDir = process.cwd();
|
||||
// 确保上级目录的 openmcp-sdk 存在
|
||||
const openMCPSdkPath = fsPath.join(currentDir, '..', 'openmcp-sdk');
|
||||
const openMCPSdkPath = fsPath.join(currentDir, '..', 'openmcp-sdk')
|
||||
createDirIfExists(openMCPSdkPath);
|
||||
|
||||
const sdkRenderPath = fsPath.join(openMCPSdkPath, 'renderer');
|
||||
|
@ -1,21 +0,0 @@
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
const targetFile = './openmcp-sdk/task-loop.js';
|
||||
|
||||
if (fs.existsSync(targetFile)) {
|
||||
let content = fs.readFileSync(targetFile, 'utf-8');
|
||||
|
||||
// Replace element-plus with ./tools.js
|
||||
content = content.replace(/'element-plus'/g, "'./tools.mjs'");
|
||||
content = content.replace(/"element-plus"/g, "\"./tools.mjs\"");
|
||||
|
||||
// content = content.replace(/const chalk = require\("chalk"\);/g, 'const chalk = require("chalk").default;');
|
||||
|
||||
// Replace define_window_default$number.performance with performance
|
||||
content = content.replace(/define_window_default\$\d+\.performance/g, 'performance');
|
||||
|
||||
fs.writeFileSync(targetFile, content);
|
||||
console.log('\x1b[32m✓ File processing completed\x1b[0m'); // Green color
|
||||
} else {
|
||||
console.log('Target file does not exist:', targetFile);
|
||||
}
|
@ -18,7 +18,8 @@ import MainPanel from '@/components/main-panel/index.vue';
|
||||
import { setDefaultCss } from './hook/css';
|
||||
import { greenLog, pinkLog } from './views/setting/util';
|
||||
import { useMessageBridge } from './api/message-bridge';
|
||||
import { initialise } from './views/connect';
|
||||
import { initialise } from './views/connect/connection';
|
||||
import { getPlatform } from './api/platform';
|
||||
import Tour from '@/components/guide/tour.vue';
|
||||
import { userHasReadGuide } from './components/guide/tour';
|
||||
|
||||
@ -36,9 +37,7 @@ bridge.addCommandListener('hello', data => {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const useAuth = Boolean(import.meta.env.VITE_USE_AUTH !== "false");
|
||||
console.log(import.meta.env.VITE_USE_AUTH, useAuth);
|
||||
|
||||
const useAuth = Boolean(import.meta.env.VITE_USE_AUTH);
|
||||
privilegeStatus.allow = !Boolean(useAuth);
|
||||
|
||||
onMounted(async () => {
|
||||
@ -54,15 +53,15 @@ onMounted(async () => {
|
||||
router.push(targetRoute);
|
||||
}
|
||||
|
||||
// 进行桥接
|
||||
await bridge.awaitForWebsocket();
|
||||
// 进行桥接
|
||||
await bridge.awaitForWebsocket();
|
||||
|
||||
// 根据是否需要密码进行后续的选择
|
||||
if (!privilegeStatus.allow) {
|
||||
return;
|
||||
}
|
||||
|
||||
document?.addEventListener('click', () => {
|
||||
document.addEventListener('click', () => {
|
||||
Connection.showPanel = false;
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { pinkLog, redLog } from '@/views/setting/util';
|
||||
import { acquireVsCodeApi, electronApi, getPlatform } from './platform';
|
||||
import { isReactive } from 'vue';
|
||||
|
||||
export interface VSCodeMessage {
|
||||
command: string;
|
||||
@ -39,22 +38,22 @@ export class MessageBridge {
|
||||
switch (platform) {
|
||||
case 'vscode':
|
||||
this.setupVsCodeListener();
|
||||
pinkLog('current platform: vscode');
|
||||
pinkLog('当前模式: vscode');
|
||||
break;
|
||||
|
||||
case 'electron':
|
||||
this.setupElectronListener();
|
||||
pinkLog('current platform: electron');
|
||||
pinkLog('当前模式: electron');
|
||||
break;
|
||||
|
||||
case 'nodejs':
|
||||
this.setupNodejsListener();
|
||||
pinkLog('current platform: nodejs');
|
||||
pinkLog('当前模式: nodejs');
|
||||
break;
|
||||
|
||||
case 'web':
|
||||
this.setupWebSocket();
|
||||
pinkLog('current platform: web');
|
||||
pinkLog('当前模式: web');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -78,8 +77,6 @@ export class MessageBridge {
|
||||
throw new Error('setupSignature must be a string');
|
||||
}
|
||||
|
||||
console.log(wsUrl);
|
||||
|
||||
this.ws = new WebSocket(wsUrl);
|
||||
const ws = this.ws;
|
||||
|
||||
@ -120,7 +117,6 @@ export class MessageBridge {
|
||||
}
|
||||
|
||||
public async awaitForWebsocket() {
|
||||
|
||||
if (this.isConnected) {
|
||||
return await this.isConnected;
|
||||
}
|
||||
@ -209,21 +205,6 @@ export class MessageBridge {
|
||||
return () => commandHandlers.delete(wrapperCommandHandler);
|
||||
}
|
||||
|
||||
private deserializeReactiveData(data: any) {
|
||||
if (isReactive(data)) {
|
||||
return JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
// 只对第一层进行遍历
|
||||
for (const key in data) {
|
||||
if (isReactive(data[key])) {
|
||||
data[key] = JSON.parse(JSON.stringify(data[key]));
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description do as axios does
|
||||
* @param command
|
||||
@ -231,7 +212,6 @@ export class MessageBridge {
|
||||
* @returns
|
||||
*/
|
||||
public commandRequest<T = any>(command: string, data?: ICommandRequestData): Promise<RestFulResponse<T>> {
|
||||
|
||||
return new Promise<RestFulResponse>((resolve, reject) => {
|
||||
this.addCommandListener(command, (data) => {
|
||||
resolve(data as RestFulResponse);
|
||||
@ -239,7 +219,7 @@ export class MessageBridge {
|
||||
|
||||
this.postMessage({
|
||||
command,
|
||||
data: this.deserializeReactiveData(data)
|
||||
data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -58,7 +58,7 @@
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
:target="mcpClientAdapter.clients[0].connectionSettingRef"
|
||||
:target="connectionSettingRef"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
:next-button-props="{ children: '下一步' }"
|
||||
:show-close="false"
|
||||
@ -78,7 +78,7 @@
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
:target="mcpClientAdapter.clients[0].connectionLogRef"
|
||||
:target="connectionLogRef"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
:next-button-props="{ children: '下一步' }"
|
||||
:show-close="false"
|
||||
@ -92,20 +92,6 @@
|
||||
</div>
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
:target="mcpServerAddRef"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
:next-button-props="{ children: '下一步' }"
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header>
|
||||
<TourTitle>调试</TourTitle>
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
你可以点击最右侧的加号来添加额外的 mcp 服务器,让你当前正在调试的 agent 零成本快速获得额外的能力。
|
||||
</div>
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
target="#sidebar-debug"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
@ -132,35 +118,11 @@
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
我们目前提供了四种主要调试选项,「资源」、「提词」、「工具」分别和 MCP 协议中的 resources、prompts、tools 对应。
|
||||
</div>
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
:target="welcomeRef"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
:next-button-props="{ children: '下一步' }"
|
||||
:show-close="false"
|
||||
placement="right"
|
||||
>
|
||||
<template #header>
|
||||
<TourTitle>调试</TourTitle>
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
而「交互测试」则允许你直接将写好的 mcp 服务器放入大模型中直接做全链路测试,从而更加获取更加真实的反馈和数据,进而改进的你的 mcp 服务器。
|
||||
</div>
|
||||
</el-tour-step>
|
||||
|
||||
<el-tour-step
|
||||
:target="welcomeRef"
|
||||
:prev-button-props="{ children: '上一步' }"
|
||||
:next-button-props="{ children: '下一步' }"
|
||||
:show-close="false"
|
||||
placement="right"
|
||||
>
|
||||
<template #header>
|
||||
<TourTitle>调试</TourTitle>
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
<br><br>
|
||||
|
||||
基于我们在 agent 和 rl 方向的最佳实践,我们后续还会推出更多的调试和数据集聚合制作选项,请期待吧!
|
||||
</div>
|
||||
</el-tour-step>
|
||||
@ -248,15 +210,10 @@
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header>
|
||||
<TourTitle>🎉 恭喜</TourTitle>
|
||||
<TourTitle>🎉恭喜</TourTitle>
|
||||
</template>
|
||||
<div class="tour-common-text">
|
||||
🎉 恭喜,我的朋友,现在的你已经是半个 mcp 专家了,请充好一杯咖啡,慢慢享用快乐的开发时间吧!
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<a href="https://openmcp.kirigaya.cn/" target="_blank">OpenMCP 官方文档</a> 是我们的文档站点,您在其中能找到非常完整的使用说明和案例教程。
|
||||
🎉恭喜,我的朋友,现在的你已经是半个 mcp 专家了,请充好一杯咖啡,慢慢享用快乐的开发时间吧!
|
||||
|
||||
<br><br>
|
||||
|
||||
@ -292,11 +249,10 @@ import TourTitle from './tour-title.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { welcomeRef } from '@/views/debug/welcome';
|
||||
import { connectionLogRef, connectionSettingRef } from '@/views/connect/connection';
|
||||
import { llmSettingRef } from '@/views/setting/api';
|
||||
import { userHasReadGuide } from './tour';
|
||||
import { setTour } from '@/hook/setting';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { mcpServerAddRef } from '@/views/connect';
|
||||
|
||||
const openTour = ref(true);
|
||||
|
||||
|
@ -6,12 +6,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, type PropType } from 'vue';
|
||||
import { computed, defineProps, type PropType } from 'vue';
|
||||
import { renderJson } from '../main-panel/chat/markdown/markdown';
|
||||
|
||||
const props = defineProps({
|
||||
json: {
|
||||
type: Object as PropType<any>,
|
||||
type: Object as PropType<string | object | undefined>,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
@ -1,203 +1,147 @@
|
||||
<template>
|
||||
<div class="k-input-object">
|
||||
<div :ref="el => editorContainer = el" class="k-input-object__editor"></div>
|
||||
<div v-if="errorMessage" class="k-input-object__error">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
<textarea ref="textareaRef" v-model="inputValue" class="k-input-object__textarea"
|
||||
:class="{ 'is-invalid': isInvalid }" @input="handleInput" @blur="handleBlur"
|
||||
@keydown="handleKeydown"
|
||||
:placeholder="props.placeholder"
|
||||
></textarea>
|
||||
</div>
|
||||
<div v-if="errorMessage" class="k-input-object__error">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch, type PropType } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { EditorView, basicSetup } from 'codemirror';
|
||||
import type { Completion, CompletionContext } from "@codemirror/autocomplete"
|
||||
import { jsonLanguage } from "@codemirror/lang-json"
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, nextTick } from 'vue';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { json } from '@codemirror/lang-json'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
import { debounce } from 'lodash'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
export default defineComponent({
|
||||
name: 'KInputObject',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入 JSON 对象'
|
||||
},
|
||||
debounceTime: {
|
||||
type: Number,
|
||||
default: 500
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入 JSON 对象'
|
||||
},
|
||||
debounceTime: {
|
||||
type: Number,
|
||||
default: 500
|
||||
},
|
||||
schema: {
|
||||
type: Object as PropType<{
|
||||
type?: string;
|
||||
properties?: Record<string, {
|
||||
type: string;
|
||||
description?: string;
|
||||
default?: any;
|
||||
enum?: any[];
|
||||
}>;
|
||||
required?: string[];
|
||||
}>,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
emits: ['update:modelValue', 'parse-error'],
|
||||
setup(props, { emit }) {
|
||||
const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
||||
const inputValue = ref<string>(JSON.stringify(props.modelValue, null, 2))
|
||||
const isInvalid = ref<boolean>(false)
|
||||
const errorMessage = ref<string>('')
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'parse-error']);
|
||||
const { t } = useI18n();
|
||||
// 防抖处理输入
|
||||
const debouncedParse = debounce((value: string) => {
|
||||
if (value.trim() === '') {
|
||||
errorMessage.value = '';
|
||||
isInvalid.value = false;
|
||||
emit('update:modelValue', undefined);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
isInvalid.value = false;
|
||||
errorMessage.value = '';
|
||||
emit('update:modelValue', parsed);
|
||||
} catch (error) {
|
||||
isInvalid.value = true;
|
||||
errorMessage.value = 'JSON 解析错误: ' + (error as Error).message;
|
||||
emit('parse-error', error);
|
||||
}
|
||||
}, props.debounceTime)
|
||||
|
||||
const editorContainer = ref<any>(null);
|
||||
const editorView = ref<EditorView | null>(null);
|
||||
const isInvalid = ref(false);
|
||||
const errorMessage = ref('');
|
||||
const handleInput = () => {
|
||||
debouncedParse(inputValue.value)
|
||||
}
|
||||
|
||||
const inputValue = ref<string>(JSON.stringify(props.modelValue, null, 2));
|
||||
const handleBlur = () => {
|
||||
// 立即执行最后一次防抖
|
||||
debouncedParse.flush()
|
||||
}
|
||||
|
||||
// 防抖处理输入
|
||||
const debouncedParse = debounce((value: string) => {
|
||||
if (value.trim() === '') {
|
||||
errorMessage.value = '';
|
||||
isInvalid.value = false;
|
||||
emit('update:modelValue', undefined);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
isInvalid.value = false;
|
||||
errorMessage.value = '';
|
||||
emit('update:modelValue', parsed);
|
||||
} catch (error) {
|
||||
isInvalid.value = true;
|
||||
errorMessage.value = t('error-parse-json') + (error as Error).message;
|
||||
emit('parse-error', error);
|
||||
}
|
||||
}, props.debounceTime);
|
||||
|
||||
onMounted(() => {
|
||||
if (editorContainer.value) {
|
||||
const extensions = [
|
||||
basicSetup,
|
||||
json(),
|
||||
oneDark,
|
||||
EditorView.updateListener.of(update => {
|
||||
if (update.docChanged) {
|
||||
const value = update.state.doc.toString()
|
||||
debouncedParse(value)
|
||||
// 当外部 modelValue 变化时更新输入框内容
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
const currentParsed = tryParse(inputValue.value)
|
||||
if (!isDeepEqual(currentParsed, newVal)) {
|
||||
inputValue.value = JSON.stringify(newVal, null, 2)
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 如果schema不为空,添加自动补全
|
||||
if (Object.keys(props.schema).length > 0) {
|
||||
extensions.push(
|
||||
jsonLanguage.data.of({
|
||||
autocomplete: getJsonCompletion(props.schema)
|
||||
})
|
||||
)
|
||||
// 辅助函数:尝试解析 JSON
|
||||
const tryParse = (value: string): any => {
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
editorView.value = new EditorView({
|
||||
doc: JSON.stringify(props.modelValue, null, 2),
|
||||
extensions,
|
||||
parent: editorContainer.value
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 添加自动补全函数
|
||||
function getJsonCompletion(schema: any) {
|
||||
return (context: CompletionContext) => {
|
||||
// 检查当前输入是否是标点符号
|
||||
const charBefore = context.state.sliceDoc(context.pos - 1, context.pos)
|
||||
if (/[,.{}[\]:]/.test(charBefore)) return null
|
||||
|
||||
const word = context.matchBefore(/\w*/)
|
||||
if (!word) return null
|
||||
|
||||
// 检查当前是否在字符串内
|
||||
const state = context.state
|
||||
const pos = context.pos
|
||||
const line = state.doc.lineAt(pos)
|
||||
const textBefore = line.text.slice(0, pos - line.from)
|
||||
|
||||
// 如果前面有奇数个双引号,说明在字符串内,不触发补全
|
||||
const quoteCount = (textBefore.match(/"/g) || []).length
|
||||
if (quoteCount % 2 !== 0) return null
|
||||
|
||||
const completions: Completion[] = []
|
||||
|
||||
// 处理对象属性补全
|
||||
if (schema.properties) {
|
||||
Object.entries(schema.properties).forEach(([key, value]) => {
|
||||
completions.push({
|
||||
label: key,
|
||||
type: "property",
|
||||
apply: `"${key}": ${getDefaultValue(value as any)}`
|
||||
})
|
||||
})
|
||||
// 辅助函数:深度比较对象
|
||||
const isDeepEqual = (obj1: any, obj2: any): boolean => {
|
||||
return JSON.stringify(obj1) === JSON.stringify(obj2)
|
||||
}
|
||||
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === '{') {
|
||||
event.preventDefault();
|
||||
const start = textareaRef.value!.selectionStart;
|
||||
const end = textareaRef.value!.selectionEnd;
|
||||
const value = inputValue.value;
|
||||
const newValue = value.substring(0, start) + '{\n \n}' + value.substring(end);
|
||||
inputValue.value = newValue;
|
||||
nextTick(() => {
|
||||
textareaRef.value!.setSelectionRange(start + 2, start + 2);
|
||||
});
|
||||
} else if (event.key === '"') {
|
||||
event.preventDefault();
|
||||
const start = textareaRef.value!.selectionStart;
|
||||
const end = textareaRef.value!.selectionEnd;
|
||||
const value = inputValue.value;
|
||||
const newValue = value.substring(0, start) + '""' + value.substring(end);
|
||||
inputValue.value = newValue;
|
||||
nextTick(() => {
|
||||
textareaRef.value!.setSelectionRange(start + 1, start + 1);
|
||||
});
|
||||
} else if (event.key === 'Tab') {
|
||||
event.preventDefault();
|
||||
const start = textareaRef.value!.selectionStart;
|
||||
const end = textareaRef.value!.selectionEnd;
|
||||
const value = inputValue.value;
|
||||
const newValue = value.substring(0, start) + ' ' + value.substring(end);
|
||||
inputValue.value = newValue;
|
||||
nextTick(() => {
|
||||
textareaRef.value!.setSelectionRange(start + 1, start + 1);
|
||||
});
|
||||
} else if (event.key === 'Enter' && inputValue.value.trim() === '') {
|
||||
event.preventDefault();
|
||||
inputValue.value = '{}';
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
from: word.from,
|
||||
options: completions,
|
||||
validFor: /^\w*$/
|
||||
textareaRef,
|
||||
inputValue,
|
||||
isInvalid,
|
||||
errorMessage,
|
||||
handleInput,
|
||||
handleBlur,
|
||||
handleKeydown,
|
||||
props
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取默认值函数
|
||||
function getDefaultValue(property: any): string {
|
||||
if (property.default !== undefined) {
|
||||
return JSON.stringify(property.default)
|
||||
}
|
||||
switch (property.type) {
|
||||
case 'string': return '""'
|
||||
case 'number': return '0'
|
||||
case 'boolean': return 'false'
|
||||
case 'object': return '{}'
|
||||
case 'array': return '[]'
|
||||
default: return 'null'
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
// 当前编辑器内容
|
||||
const currentContent = editorView.value?.state.doc.toString() ?? '';
|
||||
const newContent = JSON.stringify(newVal ?? {}, null, 2);
|
||||
|
||||
if (currentContent !== newContent && editorView.value) {
|
||||
editorView.value.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editorView.value.state.doc.length,
|
||||
insert: newContent
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
|
||||
// 辅助函数:尝试解析 JSON
|
||||
const tryParse = (value: string): any => {
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:深度比较对象
|
||||
const isDeepEqual = (obj1: any, obj2: any): boolean => {
|
||||
return JSON.stringify(obj1) === JSON.stringify(obj2)
|
||||
}
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -207,7 +151,6 @@ const isDeepEqual = (obj1: any, obj2: any): boolean => {
|
||||
border-radius: .5em;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.k-input-object__textarea {
|
||||
@ -231,24 +174,6 @@ const isDeepEqual = (obj1: any, obj2: any): boolean => {
|
||||
border-color: var(--el-color-error);
|
||||
}
|
||||
|
||||
.k-input-object__error {
|
||||
color: var(--el-color-error);
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.k-input-object__editor {
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
}
|
||||
|
||||
.k-input-object__editor.is-invalid {
|
||||
border-color: var(--el-color-error);
|
||||
}
|
||||
|
||||
.k-input-object__error {
|
||||
color: var(--el-color-error);
|
||||
font-size: 12px;
|
||||
|
@ -1,10 +0,0 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
import type { InputSchema, ToolCallContent, ToolItem } from "@/hook/type";
|
||||
import type { Ref } from "vue";
|
||||
import type { ToolCallContent, ToolItem } from "@/hook/type";
|
||||
import { type Ref, ref } from "vue";
|
||||
|
||||
import type { OpenAI } from 'openai';
|
||||
type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
|
||||
@ -14,9 +14,7 @@ export enum MessageState {
|
||||
ToolCall = 'tool call failed',
|
||||
None = 'none',
|
||||
Success = 'success',
|
||||
ParseJsonError = 'parse json error',
|
||||
NoToolFunction = 'no tool function',
|
||||
InvalidXml = 'invalid xml',
|
||||
ParseJsonError = 'parse json error'
|
||||
}
|
||||
|
||||
export interface IExtraInfo {
|
||||
@ -24,7 +22,6 @@ export interface IExtraInfo {
|
||||
state: MessageState,
|
||||
serverName: string,
|
||||
usage?: ChatCompletionChunk['usage'];
|
||||
enableXmlWrapper: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -50,30 +47,36 @@ export interface TextMessage {
|
||||
export type ChatMessage = ToolMessage | TextMessage;
|
||||
|
||||
// 新增状态和工具数据
|
||||
export interface EnableToolItem {
|
||||
name: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
inputSchema: InputSchema;
|
||||
interface EnableToolItem {
|
||||
name: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
inputSchema?: any;
|
||||
}
|
||||
|
||||
export interface ChatSetting {
|
||||
modelIndex?: number
|
||||
modelIndex: number
|
||||
systemPrompt: string
|
||||
enableTools: EnableToolItem[]
|
||||
temperature: number
|
||||
enableWebSearch: boolean
|
||||
contextLength: number
|
||||
parallelToolCalls: boolean
|
||||
enableXmlWrapper: boolean
|
||||
}
|
||||
|
||||
export interface ChatStorage {
|
||||
messages: ChatMessage[]
|
||||
messages: ChatMessage[]
|
||||
settings: ChatSetting
|
||||
}
|
||||
|
||||
export type ToolCall = OpenAI.Chat.Completions.ChatCompletionChunk.Choice.Delta.ToolCall;
|
||||
export interface ToolCall {
|
||||
id?: string;
|
||||
index?: number;
|
||||
type: string;
|
||||
function: {
|
||||
name: string;
|
||||
arguments: string;
|
||||
}
|
||||
}
|
||||
|
||||
interface PromptTextItem {
|
||||
type: 'prompt'
|
||||
@ -92,10 +95,12 @@ interface TextItem {
|
||||
|
||||
export type RichTextItem = PromptTextItem | ResourceTextItem | TextItem;
|
||||
|
||||
export const allTools = ref<ToolItem[]>([]);
|
||||
|
||||
export interface ICommonRenderMessage {
|
||||
role: 'user' | 'assistant/content';
|
||||
content: string;
|
||||
showJson?: any;
|
||||
showJson?: Ref<boolean>;
|
||||
extraInfo: IExtraInfo;
|
||||
}
|
||||
|
||||
@ -104,28 +109,42 @@ export interface IToolRenderMessage {
|
||||
content: string;
|
||||
toolResults: ToolCallContent[][];
|
||||
tool_calls: ToolCall[];
|
||||
showJson?: any;
|
||||
extraInfo: IExtraInfo;
|
||||
showJson?: Ref<boolean>;
|
||||
extraInfo: IExtraInfo;
|
||||
}
|
||||
|
||||
export type IRenderMessage = ICommonRenderMessage | IToolRenderMessage;
|
||||
|
||||
export function getToolSchema(enableTools: EnableToolItem[]): any[] {
|
||||
export function getToolSchema(enableTools: EnableToolItem[]) {
|
||||
const toolsSchema = [];
|
||||
for (let i = 0; i < enableTools.length; i++) {
|
||||
for (let i = 0; i < enableTools.length; i++) {
|
||||
const enableTool = enableTools[i];
|
||||
|
||||
if (enableTool.enabled) {
|
||||
toolsSchema.push({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: enableTool.name,
|
||||
description: enableTool.description || "",
|
||||
parameters: enableTool.inputSchema
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (enableTool.inputSchema) {
|
||||
toolsSchema.push({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: enableTool.name,
|
||||
description: enableTool.description || "",
|
||||
parameters: enableTool.inputSchema
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const tool = allTools.value[i];
|
||||
|
||||
toolsSchema.push({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: tool.name,
|
||||
description: tool.description || "",
|
||||
parameters: tool.inputSchema
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return toolsSchema;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
<div class="input-wrapper">
|
||||
|
||||
<KRichTextarea
|
||||
:ref="el => editorRef = el"
|
||||
:tabId="tabId"
|
||||
v-model="userInput"
|
||||
:placeholder="t('enter-message-dot')"
|
||||
@ -44,7 +43,6 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:scrollToBottom']);
|
||||
const editorRef = ref<any>(null);
|
||||
|
||||
const tab = tabs.content[props.tabId];
|
||||
const tabStorage = tab.storage as ChatStorage;
|
||||
@ -85,8 +83,7 @@ function clearErrorMessage(errorMessage: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSend(newMessage?: string) {
|
||||
|
||||
function handleSend(newMessage?: string) {
|
||||
// 将富文本信息转换成纯文本信息
|
||||
const userMessage = newMessage || userInput.value;
|
||||
|
||||
@ -101,6 +98,8 @@ function handleSend(newMessage?: string) {
|
||||
loop.bindStreaming(streamingContent, streamingToolCalls);
|
||||
|
||||
loop.registerOnError((error) => {
|
||||
console.log('error.msg');
|
||||
console.log(error.msg);
|
||||
|
||||
const errorMessage = clearErrorMessage(error.msg);
|
||||
ElMessage.error(errorMessage);
|
||||
@ -112,11 +111,12 @@ function handleSend(newMessage?: string) {
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: error.state,
|
||||
serverName: llms[llmManager.currentModelIndex].id || 'unknown',
|
||||
enableXmlWrapper: false
|
||||
serverName: llms[llmManager.currentModelIndex].id || 'unknown'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
});
|
||||
|
||||
loop.registerOnChunk(() => {
|
||||
@ -124,6 +124,7 @@ function handleSend(newMessage?: string) {
|
||||
});
|
||||
|
||||
loop.registerOnDone(() => {
|
||||
isLoading.value = false;
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
@ -132,16 +133,9 @@ function handleSend(newMessage?: string) {
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
loop.start(tabStorage, userMessage).then(() => {
|
||||
isLoading.value = false;
|
||||
});
|
||||
loop.start(tabStorage, userMessage);
|
||||
|
||||
// 清空文本
|
||||
userInput.value = '';
|
||||
const editor = editorRef.value.editor;
|
||||
if (editor) {
|
||||
editor.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
function handleAbort() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('context-length')" placement="top" effect="light">
|
||||
<div class="setting-button width-30" @click="showContextLengthDialog = true">
|
||||
<el-tooltip :content="t('context-length')" placement="top">
|
||||
<div class="setting-button" @click="showContextLengthDialog = true">
|
||||
<span class="iconfont icon-length"></span>
|
||||
<span class="value-badge">{{ tabStorage.settings.contextLength }}</span>
|
||||
</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<el-dialog v-model="showContextLengthDialog" :title="t('context-length') + ' ' + tabStorage.settings.contextLength"
|
||||
width="400px">
|
||||
<div class="slider-container">
|
||||
<el-slider v-model="tabStorage.settings.contextLength" :min="1" :max="500" :step="1" />
|
||||
<el-slider v-model="tabStorage.settings.contextLength" :min="1" :max="99" :step="1" />
|
||||
<div class="slider-tips">
|
||||
<span> 1: {{ t('single-dialog') }}</span>
|
||||
<span> >1: {{ t('multi-dialog') }}</span>
|
||||
@ -39,8 +39,4 @@ const showContextLengthDialog = ref(false);
|
||||
.icon-length {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.width-30 {
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
@ -1,210 +0,0 @@
|
||||
<template>
|
||||
<el-tooltip :content="'导出 mcpconfig'" placement="top" effect="light">
|
||||
<div class="setting-button" @click="toggleDialog">
|
||||
<span class="iconfont icon-deploy">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</el-tooltip>
|
||||
|
||||
<el-dialog v-model="showDialog" width="800px">
|
||||
|
||||
<template #header>
|
||||
<div>
|
||||
<div class="export-file-input">
|
||||
<span>{{ t('export-filename') }}</span>
|
||||
<el-input
|
||||
v-model="exportFileName"
|
||||
style="max-width: 300px"
|
||||
>
|
||||
<template #append>.json</template>
|
||||
</el-input>
|
||||
<span class="how-to-use"
|
||||
@click="gotoHowtoUse"
|
||||
>
|
||||
<span class="iconfont icon-info"></span>
|
||||
<span>{{ t('how-to-use') }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="tools-dialog-container">
|
||||
<el-scrollbar height="400px" class="tools-list">
|
||||
<div v-html="exportJson">
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<!--
|
||||
<el-scrollbar height="400px" class="tools-list">
|
||||
<div v-html="exampleCode"></div>
|
||||
</el-scrollbar> -->
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="copyCode">{{ t('copy') }}</el-button>
|
||||
<el-button type="primary" @click="exportCode">{{ t('export') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject, onMounted, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { type ChatStorage, type EnableToolItem, getToolSchema } from '../chat';
|
||||
import { markdownToHtml } from '@/components/main-panel/chat/markdown/markdown';
|
||||
import { llmManager, llms } from '@/views/setting/llm';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useMessageBridge } from '@/api/message-bridge';
|
||||
import { gotoWebsite } from '@/hook/util';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
const showDialog = ref(false);
|
||||
const exportJson = ref('');
|
||||
const exportFileName = ref('mcpconfig');
|
||||
|
||||
// 修改 toggleDialog 方法
|
||||
const toggleDialog = () => {
|
||||
showDialog.value = true;
|
||||
};
|
||||
|
||||
const generateExportData = computed(() => {
|
||||
const currentLLM = llms[llmManager.currentModelIndex];
|
||||
|
||||
const mcpServers = {} as any;
|
||||
for (const client of mcpClientAdapter.clients) {
|
||||
|
||||
const option = client.connectOption;
|
||||
const type = option.connectionType;
|
||||
|
||||
if (type === 'STDIO') {
|
||||
mcpServers[client.name] = {
|
||||
command: option.command,
|
||||
args: option.args,
|
||||
cwd: option.cwd,
|
||||
description: "",
|
||||
}
|
||||
} else if (type === 'SSE') {
|
||||
mcpServers[client.name] = {
|
||||
type: 'sse',
|
||||
url: option.url,
|
||||
description: "",
|
||||
}
|
||||
} else {
|
||||
mcpServers[client.name] = {
|
||||
type: 'streamable_http',
|
||||
url: option.url,
|
||||
description: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mcpconfig = {
|
||||
version: "0.0.1",
|
||||
namespace: "openmcp",
|
||||
mcpServers,
|
||||
defaultLLM: {
|
||||
baseURL: currentLLM.baseUrl,
|
||||
apiToken: currentLLM.userToken,
|
||||
model: currentLLM.userModel
|
||||
}
|
||||
};
|
||||
|
||||
return JSON.stringify(mcpconfig, null, 2);
|
||||
});
|
||||
|
||||
const innerMarkdownHtml = (code: string) => {
|
||||
const rawCode = markdownToHtml(code);
|
||||
const doc = new DOMParser().parseFromString(rawCode, 'text/html');
|
||||
const pres = doc.querySelectorAll('pre');
|
||||
|
||||
if (pres.length < 2) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const inner = pres[1].outerHTML;
|
||||
return inner;
|
||||
}
|
||||
|
||||
const exampleCode = computed(() => {
|
||||
return innerMarkdownHtml(
|
||||
'```typescript\n' +
|
||||
`import { OmAgent } from 'openmcp-sdk/service/sdk';
|
||||
|
||||
const agent = new OmAgent();
|
||||
|
||||
agent.loadMcpConfig('/path/to/${exportFileName.value}.json');
|
||||
|
||||
const prompt = await agent.getPrompt('hacknews', { topn: '5' });
|
||||
const res = await agent.ainvoke({ messages: prompt });
|
||||
|
||||
console.log('⚙️ Agent Response', res);
|
||||
` +
|
||||
'\n```'
|
||||
);
|
||||
});
|
||||
|
||||
const copyCode = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(generateExportData.value);
|
||||
ElMessage.success(t('copy-success'));
|
||||
} catch (error) {
|
||||
ElMessage.error(t('copy-fail'));
|
||||
}
|
||||
}
|
||||
|
||||
const exportCode = async () => {
|
||||
const bridge = useMessageBridge();
|
||||
bridge.postMessage({
|
||||
command: 'vscode/export-file',
|
||||
data: {
|
||||
filename: exportFileName.value,
|
||||
content: generateExportData.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const gotoHowtoUse = () => {
|
||||
if (locale.value === 'zh') {
|
||||
gotoWebsite('https://openmcp.kirigaya.cn/zh/sdk-tutorial/#%E4%BD%BF%E7%94%A8');
|
||||
} else if (locale.value === 'ja') {
|
||||
gotoWebsite('https://openmcp.kirigaya.cn/ja/sdk-tutorial/#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95');
|
||||
} else {
|
||||
gotoWebsite('https://openmcp.kirigaya.cn/sdk-tutorial/#usage');
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
exportJson.value = innerMarkdownHtml(
|
||||
'```json\n' + generateExportData.value + '\n```'
|
||||
);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.export-file-input {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tools-list {
|
||||
border-radius: .5em;
|
||||
border: 1px solid var(--main-color);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.how-to-use {
|
||||
margin-left: 10px;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
background-color: var(--main-color);
|
||||
color: #1e1e1e;
|
||||
padding: 3px 5px;
|
||||
border-radius: .3em;
|
||||
}
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('choose-model')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('choose-model')" placement="top">
|
||||
<div class="setting-button" @click="showModelDialog = true">
|
||||
<span class="iconfont icon-model">
|
||||
{{ currentServerName }}/{{ currentModelName }}
|
||||
@ -64,15 +64,4 @@ const onRadioGroupChange = () => {
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.setting-button:hover {
|
||||
background: var(--main-light-color, #f0f8ff);
|
||||
box-shadow: 0 2px 8px 0 rgba(64,158,255,0.08);
|
||||
border-color: var(--el-color-primary-light-7, #c6e2ff);
|
||||
}
|
||||
|
||||
.setting-button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
</style>
|
||||
<style></style>
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('parallel-tool-calls')" placement="top" effect="light">
|
||||
<div class="setting-button" :class="{ 'active': tabStorage.settings.parallelToolCalls }" size="small"
|
||||
@click="toggle">
|
||||
<span class="iconfont icon-parallel"></span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import type { ChatStorage } from '../chat';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||
|
||||
const toggle = () => {
|
||||
tabStorage.settings.parallelToolCalls = !tabStorage.settings.parallelToolCalls;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('prompts')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('prompts')" placement="top">
|
||||
<div class="setting-button" @click="showChoosePrompt = true; saveCursorPosition();">
|
||||
<span class="iconfont icon-chat"></span>
|
||||
</div>
|
||||
@ -70,7 +70,7 @@ async function whenGetPromptResponse(msg: PromptsGetResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document?.createElement('div');
|
||||
const container = document.createElement('div');
|
||||
const promptChatItem = createApp(PromptChatItem, {
|
||||
messages: msg.messages
|
||||
});
|
||||
@ -88,7 +88,7 @@ async function whenGetPromptResponse(msg: PromptsGetResponse) {
|
||||
}
|
||||
|
||||
// 设置光标到插入元素的后方
|
||||
const newRange = document?.createRange();
|
||||
const newRange = document.createRange();
|
||||
newRange.setStartAfter(firstElement);
|
||||
newRange.collapse(true);
|
||||
const selection = window.getSelection();
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('resources')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('resources')" placement="top">
|
||||
<div class="setting-button" @click="showChooseResource = true; saveCursorPosition();">
|
||||
<span class="iconfont icon-file"></span>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@ import ResourceReader from '@/components/main-panel/resource/resouce-reader.vue'
|
||||
import { ElMessage, ElTooltip, ElProgress, ElPopover } from 'element-plus';
|
||||
|
||||
import ResourceChatItem from '../resource-chat-item.vue';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { useMessageBridge } from '@/api/message-bridge';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -60,8 +60,8 @@ function saveCursorPosition() {
|
||||
|
||||
async function handleResourceSelected(resource: Resources) {
|
||||
selectResource.value = undefined;
|
||||
const msg = await mcpClientAdapter.readResource(resource.uri);
|
||||
|
||||
const bridge = useMessageBridge();
|
||||
const { code, msg } = await bridge.commandRequest('resources/read', { resourceUri: resource.uri });
|
||||
if (msg) {
|
||||
await whenGetResourceResponse(msg as ResourcesReadResponse);
|
||||
}
|
||||
@ -82,7 +82,7 @@ async function whenGetResourceResponse(msg: ResourcesReadResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document?.createElement('div');
|
||||
const container = document.createElement('div');
|
||||
const resourceChatItem = createApp(ResourceChatItem, {
|
||||
contents: msg.contents
|
||||
});
|
||||
@ -101,7 +101,7 @@ async function whenGetResourceResponse(msg: ResourcesReadResponse) {
|
||||
editor.appendChild(firstElement);
|
||||
}
|
||||
|
||||
const newRange = document?.createRange();
|
||||
const newRange = document.createRange();
|
||||
newRange.setStartAfter(firstElement);
|
||||
newRange.collapse(true);
|
||||
const selection = window.getSelection();
|
||||
@ -123,13 +123,4 @@ async function whenGetResourceResponse(msg: ResourcesReadResponse) {
|
||||
.icon-length {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.el-dialog .el-collapse-item__header {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.el-dialog .el-collapse-item__wrap {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
</style>
|
@ -5,11 +5,9 @@
|
||||
<ToolUse />
|
||||
<Prompt />
|
||||
<Resource />
|
||||
<ParallelToolCalls />
|
||||
<Websearch />
|
||||
<Temperature />
|
||||
<ContextLength />
|
||||
<XmlWrapper />
|
||||
<Export />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -24,11 +22,9 @@ import SystemPrompt from './system-prompt.vue';
|
||||
import ToolUse from './tool-use.vue';
|
||||
import Prompt from './prompt.vue';
|
||||
import Resource from './resource.vue';
|
||||
import ParallelToolCalls from './parallel-tool-calls.vue';
|
||||
import Websearch from './websearch.vue';
|
||||
import Temperature from './temperature.vue';
|
||||
import ContextLength from './context-length.vue';
|
||||
import XmlWrapper from './xml-wrapper.vue';
|
||||
import Export from './export.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@ -61,11 +57,9 @@ if (!tabStorage.settings) {
|
||||
modelIndex: llmManager.currentModelIndex,
|
||||
enableTools: [],
|
||||
enableWebSearch: false,
|
||||
temperature: 0.6,
|
||||
contextLength: 100,
|
||||
systemPrompt: '',
|
||||
enableXmlWrapper: false,
|
||||
parallelToolCalls: true
|
||||
temperature: 0.7,
|
||||
contextLength: 20,
|
||||
systemPrompt: ''
|
||||
} as ChatSetting;
|
||||
}
|
||||
|
||||
@ -162,15 +156,16 @@ provide('tabStorage', tabStorage);
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.el-switch__core {
|
||||
.tools-dialog-container .el-switch__core {
|
||||
border: 1px solid var(--main-color) !important;
|
||||
}
|
||||
|
||||
.el-switch .el-switch__action {
|
||||
|
||||
.tools-dialog-container .el-switch .el-switch__action {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
|
||||
.el-switch.is-checked .el-switch__action {
|
||||
.tools-dialog-container .el-switch.is-checked .el-switch__action {
|
||||
background-color: var(--sidebar);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,8 @@ export function getSystemPrompt(name: string) {
|
||||
export async function saveSystemPrompts() {
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
const res = await bridge.commandRequest('system-prompts/save', { prompts: systemPrompts.value });
|
||||
const payload = JSON.parse(JSON.stringify(systemPrompts.value));
|
||||
const res = await bridge.commandRequest('system-prompts/save', { prompts: payload });
|
||||
if (res.code === 200) {
|
||||
pinkLog('system prompt 保存成功');
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('system-prompt')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('system-prompt')" placement="top">
|
||||
<div class="setting-button" :class="{ 'active': hasSystemPrompt }" size="small"
|
||||
@click="showSystemPromptDialog = true">
|
||||
<span class="iconfont icon-prompt"></span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('temperature-parameter')" placement="top" effect="light">
|
||||
<div class="setting-button width-30" @click="showTemperatureSlider = true">
|
||||
<el-tooltip :content="t('temperature-parameter')" placement="top">
|
||||
<div class="setting-button" @click="showTemperatureSlider = true">
|
||||
<span class="iconfont icon-temperature"></span>
|
||||
<span class="value-badge">{{ tabStorage.settings.temperature.toFixed(1) }}</span>
|
||||
</div>
|
||||
@ -34,13 +34,9 @@ const showTemperatureSlider = ref(false);
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
.icon-temperature {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.width-30 {
|
||||
width: 30px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('tool-use')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('tool-use')" placement="top">
|
||||
<div class="setting-button" :class="{ 'active': availableToolsNum > 0 }" size="small" @click="toggleTools">
|
||||
<span class="iconfont icon-tool badge-outer">
|
||||
<span class="badge-inner">
|
||||
@ -10,19 +10,7 @@
|
||||
|
||||
</el-tooltip>
|
||||
|
||||
<el-dialog v-model="showToolsDialog" width="800px">
|
||||
|
||||
<template #header>
|
||||
<div>
|
||||
<span>{{ t('tool-manage') }}</span>
|
||||
<el-tooltip :content="t('enable-xml-wrapper')" placement="top" effect="light">
|
||||
<span class="xml-tag" :class="{
|
||||
'active': tabStorage.settings.enableXmlWrapper
|
||||
}" @click="tabStorage.settings.enableXmlWrapper = !tabStorage.settings.enableXmlWrapper">xml</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-dialog v-model="showToolsDialog" title="工具管理" width="800px">
|
||||
<div class="tools-dialog-container">
|
||||
<el-scrollbar height="400px" class="tools-list">
|
||||
<div v-for="(tool, index) in tabStorage.settings.enableTools" :key="index" class="tool-item">
|
||||
@ -35,27 +23,23 @@
|
||||
</el-scrollbar>
|
||||
|
||||
<el-scrollbar height="400px" class="schema-viewer">
|
||||
<!-- 如果激活 xml 指令包裹,则展示对应的 prompt -->
|
||||
<div v-if="tabStorage.settings.enableXmlWrapper" v-html="activeToolsXmlPrompt" />
|
||||
<!-- 如果是普通模式,则展示普通的工具列表 -->
|
||||
<div v-else v-html="activeToolsSchemaHTML" />
|
||||
<div v-html="activeToolsSchemaHTML"></div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="enableAllTools">{{ t('enable-all-tools') }}</el-button>
|
||||
<el-button type="danger" @click="disableAllTools">{{ t('disable-all-tools') }}</el-button>
|
||||
<el-button type="primary" @click="enableAllTools">激活所有工具</el-button>
|
||||
<el-button type="danger" @click="disableAllTools">禁用所有工具</el-button>
|
||||
<el-button type="primary" @click="showToolsDialog = false">{{ t("cancel") }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject, onMounted, watch } from 'vue';
|
||||
import { ref, computed, inject, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { type ChatStorage, type EnableToolItem, getToolSchema } from '../chat';
|
||||
import { allTools, type ChatStorage, getToolSchema } from '../chat';
|
||||
import { markdownToHtml } from '@/components/main-panel/chat/markdown/markdown';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { toolSchemaToPromptDescription } from '../../core/xml-wrapper';
|
||||
import { useMessageBridge } from '@/api/message-bridge';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -64,34 +48,28 @@ const tabStorage = inject('tabStorage') as ChatStorage;
|
||||
const showToolsDialog = ref(false);
|
||||
|
||||
const availableToolsNum = computed(() => {
|
||||
return tabStorage.settings.enableTools.filter(tool => tool.enabled).length;
|
||||
return tabStorage.settings.enableTools.filter(tool => tool.enabled).length;
|
||||
});
|
||||
|
||||
|
||||
|
||||
// 修改 toggleTools 方法
|
||||
const toggleTools = () => {
|
||||
showToolsDialog.value = true;
|
||||
showToolsDialog.value = true;
|
||||
};
|
||||
|
||||
|
||||
const activeToolsSchemaHTML = computed(() => {
|
||||
const toolsSchema = getToolSchema(tabStorage.settings.enableTools);
|
||||
const jsonString = JSON.stringify(toolsSchema, null, 2);
|
||||
const toolsSchema = getToolSchema(tabStorage.settings.enableTools);
|
||||
const jsonString = JSON.stringify(toolsSchema, null, 2);
|
||||
|
||||
return markdownToHtml(
|
||||
"```json\n" + jsonString + "\n```"
|
||||
);
|
||||
});
|
||||
|
||||
const activeToolsXmlPrompt = computed(() => {
|
||||
const prompt = toolSchemaToPromptDescription(tabStorage.settings.enableTools);
|
||||
return markdownToHtml(
|
||||
"```markdown\n" + prompt + "\n```"
|
||||
);
|
||||
return markdownToHtml(
|
||||
"```json\n" + jsonString + "\n```"
|
||||
);
|
||||
});
|
||||
|
||||
// 新增方法 - 激活所有工具
|
||||
const enableAllTools = () => {
|
||||
tabStorage.settings.enableTools.forEach(tool => {
|
||||
tabStorage.settings.enableTools.forEach(tool => {
|
||||
tool.enabled = true;
|
||||
});
|
||||
};
|
||||
@ -103,64 +81,22 @@ const disableAllTools = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 更新工具列表的方法
|
||||
const updateToolsList = async () => {
|
||||
// 将新的 tool 和并进入 tabStorage.settings.enableTools 中
|
||||
// 只需要保证 enable 信息同步即可,其余工具默认开启
|
||||
const disableToolNames = new Set<string>(
|
||||
tabStorage.settings.enableTools
|
||||
.filter(tool => !tool.enabled)
|
||||
.map(tool => tool.name)
|
||||
);
|
||||
|
||||
const newTools: EnableToolItem[] = [];
|
||||
|
||||
for (const client of mcpClientAdapter.clients) {
|
||||
const tools = await client.getTools({ cache: false });
|
||||
if (tools) {
|
||||
for (const tool of tools.values()) {
|
||||
const enabled = !disableToolNames.has(tool.name);
|
||||
|
||||
newTools.push({
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
enabled
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tabStorage.settings.enableTools = newTools;
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await updateToolsList();
|
||||
watch(() => mcpClientAdapter.refreshSignal.value, async () => {
|
||||
await updateToolsList();
|
||||
});
|
||||
const bridge = useMessageBridge();
|
||||
const res = await bridge.commandRequest('tools/list');
|
||||
if (res.code === 200) {
|
||||
allTools.value = res.msg.tools || [];
|
||||
tabStorage.settings.enableTools = [];
|
||||
for (const tool of allTools.value) {
|
||||
tabStorage.settings.enableTools.push({
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.xml-tag {
|
||||
margin-left: 10px;
|
||||
border-radius: .5em;
|
||||
padding: 2px 5px;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
color: black;
|
||||
background-color: var(--main-color);
|
||||
opacity: 0.3;
|
||||
transition: var(--animation-3s);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.xml-tag.active {
|
||||
opacity: 1;
|
||||
transition: var(--animation-3s);
|
||||
}
|
||||
</style>
|
||||
<style></style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('websearch')" placement="top" effect="light">
|
||||
<el-tooltip :content="t('websearch')" placement="top">
|
||||
<div class="setting-button" :class="{ 'active': tabStorage.settings.enableWebSearch }" size="small"
|
||||
@click="toggleWebSearch">
|
||||
<span class="iconfont icon-web"></span>
|
||||
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<el-tooltip :content="t('enable-xml-wrapper')" placement="top" effect="light">
|
||||
<div class="setting-button" :class="{ 'active': tabStorage.settings.enableXmlWrapper }" size="small"
|
||||
@click="toggle">
|
||||
<span class="iconfont icon-suffix-xml"></span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import type { ChatStorage } from '../chat';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||
|
||||
const toggle = () => {
|
||||
tabStorage.settings.enableXmlWrapper = !tabStorage.settings.enableXmlWrapper;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip :content="props.messages[0].content.text" placement="top" effect="light">
|
||||
<el-tooltip :content="props.messages[0].content.text" placement="top">
|
||||
<span class="chat-prompt-item" contenteditable="false">
|
||||
<span class="iconfont icon-chat"></span>
|
||||
<span class="real-text">{{ props.messages[0].content.text }}</span>
|
||||
@ -22,6 +22,9 @@ const props = defineProps({
|
||||
<style>
|
||||
.chat-prompt-item {
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
border-radius: .3em;
|
||||
align-items: center;
|
||||
@ -31,17 +34,9 @@ const props = defineProps({
|
||||
font-size: 12px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.chat-prompt-item .iconfont {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.chat-prompt-item .real-text {
|
||||
max-width: 60px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip placement="top" effect="light">
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<div class="resource-chat-item-tooltip">
|
||||
<div v-for="(item, index) of toolRenderItems" :key="index">
|
||||
|
@ -117,12 +117,6 @@ function extractTextFromCollection(collection: HTMLCollection) {
|
||||
|
||||
const isComposing = ref(false);
|
||||
|
||||
defineExpose({
|
||||
editor,
|
||||
handleBackspace,
|
||||
handleInput,
|
||||
});
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
|
||||
if (event.key === 'Enter' && !event.shiftKey && !isComposing.value) {
|
||||
@ -187,7 +181,7 @@ function handlePaste(event: ClipboardEvent) {
|
||||
if (selection && selection.rangeCount > 0) {
|
||||
const range = selection.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
const textNode = document?.createTextNode(pastedText);
|
||||
const textNode = document.createTextNode(pastedText);
|
||||
range.insertNode(textNode);
|
||||
range.setStartAfter(textNode);
|
||||
range.collapse(true);
|
||||
@ -234,7 +228,6 @@ function handleCompositionEnd() {
|
||||
.rich-editor {
|
||||
min-height: 100px;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.rich-editor:empty::before {
|
||||
|
@ -1,39 +1,16 @@
|
||||
import type { ToolCallContent, ToolCallResponse } from "@/hook/type";
|
||||
import { callTool } from "../../tool/tools";
|
||||
import { MessageState, type ToolCall } from "../chat-box/chat";
|
||||
import { mcpClientAdapter } from "@/views/connect/core";
|
||||
import type { BasicLlmDescription } from "@/views/setting/llm";
|
||||
import type OpenAI from "openai";
|
||||
|
||||
export interface TaskLoopChatOption {
|
||||
id?: string
|
||||
proxyServer?: string
|
||||
enableXmlWrapper?: boolean
|
||||
}
|
||||
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & TaskLoopChatOption;
|
||||
|
||||
export interface ToolCallResult {
|
||||
state: MessageState;
|
||||
content: ToolCallContent[];
|
||||
}
|
||||
|
||||
export type IToolCallIndex = number;
|
||||
|
||||
export async function handleToolCalls(toolCall: ToolCall): Promise<ToolCallResult> {
|
||||
|
||||
if (!toolCall.function) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'error',
|
||||
text: 'no tool function'
|
||||
}],
|
||||
state: MessageState.NoToolFunction
|
||||
}
|
||||
}
|
||||
|
||||
// 反序列化 streaming 来的参数字符串
|
||||
// TODO: check as string
|
||||
const toolName = toolCall.function.name as string;
|
||||
const argsResult = deserializeToolCallResponse(toolCall.function.arguments as string);
|
||||
const toolName = toolCall.function.name;
|
||||
const argsResult = deserializeToolCallResponse(toolCall.function.arguments);
|
||||
|
||||
if (argsResult.error) {
|
||||
return {
|
||||
@ -48,7 +25,7 @@ export async function handleToolCalls(toolCall: ToolCall): Promise<ToolCallResul
|
||||
const toolArgs = argsResult.value;
|
||||
|
||||
// 进行调用,根据结果返回不同的值
|
||||
const toolResponse = await mcpClientAdapter.callTool(toolName, toolArgs);
|
||||
const toolResponse = await callTool(toolName, toolArgs);
|
||||
return handleToolResponse(toolResponse);
|
||||
}
|
||||
|
||||
@ -67,8 +44,11 @@ function deserializeToolCallResponse(toolArgs: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function handleToolResponse(toolResponse: ToolCallResponse) {
|
||||
function handleToolResponse(toolResponse: ToolCallResponse) {
|
||||
if (typeof toolResponse === 'string') {
|
||||
// 如果是 string,说明是错误信息
|
||||
console.log(toolResponse);
|
||||
|
||||
|
||||
return {
|
||||
content: [{
|
||||
@ -103,69 +83,4 @@ function parseErrorObject(error: any): string {
|
||||
} else {
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 将工具调用的ID映射为索引
|
||||
* @param toolCall 工具调用对象
|
||||
* @param callId2Index ID到索引的映射表
|
||||
* @returns 映射后的索引值
|
||||
*/
|
||||
export function idAsIndexAdapter(toolCall: ToolCall | string, callId2Index: Map<string, number>): IToolCallIndex {
|
||||
// grok 采用 id 作为 index,需要将 id 映射到 zero-based 的 index
|
||||
const id = typeof toolCall === 'string' ? toolCall : toolCall.id;
|
||||
if (!id) {
|
||||
return 0;
|
||||
}
|
||||
if (!callId2Index.has(id)) {
|
||||
callId2Index.set(id, callId2Index.size);
|
||||
}
|
||||
return callId2Index.get(id)!;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 单次调用的索引适配器(暂未实现)
|
||||
* @param toolCall 工具调用对象
|
||||
* @returns 固定返回0
|
||||
*/
|
||||
export function singleCallIndexAdapter(toolCall: ToolCall): IToolCallIndex {
|
||||
// TODO: 等待后续支持
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* @param toolCall
|
||||
* @returns
|
||||
*/
|
||||
export function defaultIndexAdapter(toolCall: ToolCall): IToolCallIndex {
|
||||
return toolCall.index || 0;
|
||||
}
|
||||
|
||||
export function getToolCallIndexAdapter(llm: BasicLlmDescription, chatData: ChatCompletionCreateParamsBase) {
|
||||
|
||||
// 如果是 xml 模式,那么 index adapter 必须是 idAsIndexAdapter
|
||||
|
||||
if (chatData.enableXmlWrapper) {
|
||||
const callId2Index = new Map<string, number>();
|
||||
return (toolCall: ToolCall) => idAsIndexAdapter(toolCall, callId2Index);
|
||||
}
|
||||
|
||||
if (llm.userModel.startsWith('gemini')) {
|
||||
return singleCallIndexAdapter;
|
||||
}
|
||||
|
||||
if (llm.userModel.startsWith('grok')) {
|
||||
const callId2Index = new Map<string, number>();
|
||||
return (toolCall: ToolCall) => idAsIndexAdapter(toolCall, callId2Index);
|
||||
}
|
||||
|
||||
return defaultIndexAdapter;
|
||||
}
|
||||
|
||||
export function getIdAsIndexAdapter() {
|
||||
const callId2Index = new Map<string, number>();
|
||||
return (toolCall: ToolCall) => idAsIndexAdapter(toolCall, callId2Index);
|
||||
}
|
@ -1,32 +1,21 @@
|
||||
/* eslint-disable */
|
||||
import { ref, type Ref } from "vue";
|
||||
import { type ToolCall, type ChatStorage, getToolSchema, MessageState, type ChatMessage, type ChatSetting, type EnableToolItem } from "../chat-box/chat";
|
||||
import { type ToolCall, type ChatStorage, getToolSchema, MessageState } from "../chat-box/chat";
|
||||
import { useMessageBridge, MessageBridge, createMessageBridge } from "@/api/message-bridge";
|
||||
import type { OpenAI } from 'openai';
|
||||
import { llmManager, llms, type BasicLlmDescription } from "@/views/setting/llm";
|
||||
import { redLog } from "@/views/setting/util";
|
||||
import { llmManager, llms } from "@/views/setting/llm";
|
||||
import { pinkLog, redLog } from "@/views/setting/util";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { getToolCallIndexAdapter, handleToolCalls, type IToolCallIndex, type ToolCallResult } from "./handle-tool-calls";
|
||||
import { handleToolCalls, type ToolCallResult } from "./handle-tool-calls";
|
||||
import { getPlatform } from "@/api/platform";
|
||||
import { getSystemPrompt } from "../chat-box/options/system-prompt";
|
||||
import { mcpSetting } from "@/hook/mcp";
|
||||
import { mcpClientAdapter } from "@/views/connect/core";
|
||||
import type { ToolItem } from "@/hook/type";
|
||||
import chalk from 'chalk';
|
||||
import { getXmlWrapperPrompt, getToolCallFromXmlString, getXmlsFromString, handleXmlWrapperToolcall, toNormaliseToolcall, getXmlResultPrompt } from "./xml-wrapper";
|
||||
import { getSystemPrompt, systemPrompts } from "../chat-box/options/system-prompt";
|
||||
|
||||
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
|
||||
export interface TaskLoopChatOption {
|
||||
id?: string
|
||||
proxyServer?: string
|
||||
enableXmlWrapper?: boolean
|
||||
}
|
||||
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & TaskLoopChatOption;
|
||||
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
|
||||
export interface TaskLoopOptions {
|
||||
maxEpochs?: number;
|
||||
maxJsonParseRetry?: number;
|
||||
adapter?: any;
|
||||
verbose?: 0 | 1 | 2 | 3;
|
||||
}
|
||||
|
||||
export interface IErrorMssage {
|
||||
@ -34,8 +23,6 @@ export interface IErrorMssage {
|
||||
msg: string
|
||||
}
|
||||
|
||||
export { MessageState };
|
||||
|
||||
export interface IDoConversationResult {
|
||||
stop: boolean;
|
||||
}
|
||||
@ -48,37 +35,25 @@ export class TaskLoop {
|
||||
private bridge: MessageBridge;
|
||||
private streamingContent: Ref<string>;
|
||||
private streamingToolCalls: Ref<ToolCall[]>;
|
||||
private aborted = false;
|
||||
|
||||
private currentChatId = '';
|
||||
private onError: (error: IErrorMssage) => void = (msg) => { };
|
||||
private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => { };
|
||||
private onDone: () => void = () => { };
|
||||
private onError: (error: IErrorMssage) => void = (msg) => {};
|
||||
private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {};
|
||||
private onDone: () => void = () => {};
|
||||
private onToolCall: (toolCall: ToolCall) => ToolCall = toolCall => toolCall;
|
||||
private onToolCalled: (toolCallResult: ToolCallResult) => ToolCallResult = toolCallResult => toolCallResult;
|
||||
private onEpoch: () => void = () => { };
|
||||
private onEpoch: () => void = () => {};
|
||||
private completionUsage: ChatCompletionChunk['usage'] | undefined;
|
||||
private llmConfig?: BasicLlmDescription;
|
||||
|
||||
// 只会在 nodejs 环境下使用的部分变量
|
||||
private nodejsStatus = {
|
||||
connectionFut: new Promise<void>(resolve => resolve(void 0))
|
||||
};
|
||||
private llmConfig: any;
|
||||
|
||||
constructor(
|
||||
private taskOptions: TaskLoopOptions = {
|
||||
maxEpochs: 50,
|
||||
maxJsonParseRetry: 3,
|
||||
adapter: undefined,
|
||||
verbose: 0
|
||||
},
|
||||
private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3, adapter: undefined },
|
||||
) {
|
||||
this.streamingContent = ref('');
|
||||
this.streamingToolCalls = ref([]);
|
||||
|
||||
// 根据当前环境决定是否要开启 messageBridge
|
||||
const platform = getPlatform();
|
||||
|
||||
if (platform === 'nodejs') {
|
||||
const adapter = taskOptions.adapter;
|
||||
|
||||
@ -86,74 +61,31 @@ export class TaskLoop {
|
||||
throw new Error('adapter is required');
|
||||
}
|
||||
|
||||
// 根据 adapter 创建 nodejs 下特殊的、基于 event 的 message bridge (不占用任何端口)
|
||||
createMessageBridge(adapter.emitter);
|
||||
|
||||
// 用于进行连接同步
|
||||
this.nodejsStatus.connectionFut = mcpClientAdapter.launch();
|
||||
}
|
||||
|
||||
// web 环境下 bridge 会自动加载完成
|
||||
this.bridge = useMessageBridge();
|
||||
|
||||
// 注册 HMR
|
||||
mcpClientAdapter.addConnectRefreshListener();
|
||||
}
|
||||
|
||||
public async waitConnection() {
|
||||
await this.nodejsStatus.connectionFut;
|
||||
}
|
||||
|
||||
public setTaskLoopOptions(taskOptions: TaskLoopOptions) {
|
||||
const {
|
||||
maxEpochs = 50,
|
||||
maxJsonParseRetry = 3,
|
||||
verbose = 1,
|
||||
} = taskOptions;
|
||||
|
||||
this.taskOptions = {
|
||||
maxEpochs,
|
||||
maxJsonParseRetry,
|
||||
verbose,
|
||||
...this.taskOptions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 处理 streaming 输出的每一个分块的 content 部分
|
||||
* @param chunk
|
||||
* @param chatData
|
||||
*/
|
||||
private handleChunkDeltaContent(chunk: ChatCompletionChunk, chatData: ChatCompletionCreateParamsBase) {
|
||||
private handleChunkDeltaContent(chunk: ChatCompletionChunk) {
|
||||
const content = chunk.choices[0]?.delta?.content || '';
|
||||
if (content) {
|
||||
this.streamingContent.value += content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 处理 streaming 输出的每一个 chunk 的 tool_calls 部分
|
||||
* @param chunk
|
||||
* @param chatData
|
||||
* @param toolcallIndexAdapter
|
||||
*/
|
||||
private handleChunkDeltaToolCalls(chunk: ChatCompletionChunk, chatData: ChatCompletionCreateParamsBase, toolcallIndexAdapter: (toolCall: ToolCall) => IToolCallIndex) {
|
||||
private handleChunkDeltaToolCalls(chunk: ChatCompletionChunk) {
|
||||
const toolCall = chunk.choices[0]?.delta?.tool_calls?.[0];
|
||||
|
||||
|
||||
if (toolCall) {
|
||||
if (toolCall.index === undefined || toolCall.index === null) {
|
||||
console.warn('tool_call.index is undefined or null');
|
||||
}
|
||||
|
||||
|
||||
const index = toolcallIndexAdapter(toolCall);
|
||||
const currentCall = this.streamingToolCalls.value[index];
|
||||
const currentCall = this.streamingToolCalls.value[toolCall.index];
|
||||
|
||||
if (currentCall === undefined) {
|
||||
// 新的工具调用开始
|
||||
this.streamingToolCalls.value[index] = {
|
||||
this.streamingToolCalls.value[toolCall.index] = {
|
||||
id: toolCall.id,
|
||||
index,
|
||||
index: toolCall.index,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: toolCall.function?.name || '',
|
||||
@ -167,15 +99,15 @@ export class TaskLoop {
|
||||
currentCall.id = toolCall.id;
|
||||
}
|
||||
if (toolCall.function?.name) {
|
||||
currentCall.function!.name = toolCall.function.name;
|
||||
currentCall.function.name = toolCall.function.name;
|
||||
}
|
||||
if (toolCall.function?.arguments) {
|
||||
currentCall.function!.arguments += toolCall.function.arguments;
|
||||
currentCall.function.arguments += toolCall.function.arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleChunkUsage(chunk: ChatCompletionChunk) {
|
||||
@ -185,7 +117,7 @@ export class TaskLoop {
|
||||
}
|
||||
}
|
||||
|
||||
private doConversation(chatData: ChatCompletionCreateParamsBase, toolcallIndexAdapter: (toolCall: ToolCall) => IToolCallIndex) {
|
||||
private doConversation(chatData: ChatCompletionCreateParamsBase) {
|
||||
|
||||
return new Promise<IDoConversationResult>((resolve, reject) => {
|
||||
const chunkHandler = this.bridge.addCommandListener('llm/chat/completions/chunk', data => {
|
||||
@ -193,22 +125,15 @@ export class TaskLoop {
|
||||
const { chunk } = data.msg as { chunk: ChatCompletionChunk };
|
||||
|
||||
// 处理增量的 content 和 tool_calls
|
||||
if (chatData.enableXmlWrapper) {
|
||||
this.handleChunkDeltaContent(chunk, chatData);
|
||||
// no tool call in enableXmlWrapper
|
||||
this.handleChunkUsage(chunk);
|
||||
} else {
|
||||
this.handleChunkDeltaContent(chunk, chatData);
|
||||
this.handleChunkDeltaToolCalls(chunk, chatData, toolcallIndexAdapter);
|
||||
this.handleChunkUsage(chunk);
|
||||
}
|
||||
|
||||
this.consumeChunks(chunk);
|
||||
this.handleChunkDeltaContent(chunk);
|
||||
this.handleChunkDeltaToolCalls(chunk);
|
||||
this.handleChunkUsage(chunk);
|
||||
|
||||
this.onChunk(chunk);
|
||||
}, { once: false });
|
||||
|
||||
const doneHandler = this.bridge.addCommandListener('llm/chat/completions/done', data => {
|
||||
this.consumeDones();
|
||||
|
||||
|
||||
const doneHandler = this.bridge.addCommandListener('llm/chat/completions/done', data => {
|
||||
this.onDone();
|
||||
chunkHandler();
|
||||
errorHandler();
|
||||
|
||||
@ -218,7 +143,7 @@ export class TaskLoop {
|
||||
}, { once: true });
|
||||
|
||||
const errorHandler = this.bridge.addCommandListener('llm/chat/completions/error', data => {
|
||||
this.consumeErrors({
|
||||
this.onError({
|
||||
state: MessageState.ReceiveChunkError,
|
||||
msg: data.msg || '请求模型服务时发生错误'
|
||||
});
|
||||
@ -232,6 +157,8 @@ export class TaskLoop {
|
||||
|
||||
}, { once: true });
|
||||
|
||||
console.log(chatData);
|
||||
|
||||
this.bridge.postMessage({
|
||||
command: 'llm/chat/completions',
|
||||
data: JSON.parse(JSON.stringify(chatData)),
|
||||
@ -239,54 +166,36 @@ export class TaskLoop {
|
||||
});
|
||||
}
|
||||
|
||||
public setProxyServer(proxyServer: string) {
|
||||
mcpSetting.proxyServer = proxyServer;
|
||||
}
|
||||
|
||||
public makeChatData(tabStorage: ChatStorage): ChatCompletionCreateParamsBase | undefined {
|
||||
const baseURL = this.getLlmConfig().baseUrl;
|
||||
const apiKey = this.getLlmConfig().userToken || '';
|
||||
|
||||
|
||||
if (apiKey.trim() === '') {
|
||||
|
||||
|
||||
if (tabStorage.messages.length > 0 && tabStorage.messages[tabStorage.messages.length - 1].role === 'user') {
|
||||
tabStorage.messages.pop();
|
||||
ElMessage.error('请先设置 API Key');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
const model = this.getLlmConfig().userModel;
|
||||
const temperature = tabStorage.settings.temperature;
|
||||
const parallelToolCalls = tabStorage.settings.parallelToolCalls;
|
||||
const proxyServer = mcpSetting.proxyServer || '';
|
||||
|
||||
// 如果是 xml 模式,则 tools 为空
|
||||
const enableXmlWrapper = tabStorage.settings.enableXmlWrapper;
|
||||
const tools = enableXmlWrapper ? []: getToolSchema(tabStorage.settings.enableTools);
|
||||
const tools = getToolSchema(tabStorage.settings.enableTools);
|
||||
|
||||
const userMessages = [];
|
||||
|
||||
// 尝试获取 system prompt,在 api 模式下,systemPrompt 就是目标提词
|
||||
// 但是在 UI 模式下,systemPrompt 只是一个 index,需要从后端数据库中获取真实 prompt
|
||||
|
||||
let prompt = '';
|
||||
|
||||
// 如果存在系统提示词,则从数据库中获取对应的数据
|
||||
if (tabStorage.settings.systemPrompt) {
|
||||
prompt += getSystemPrompt(tabStorage.settings.systemPrompt) || tabStorage.settings.systemPrompt;
|
||||
const prompt = getSystemPrompt(tabStorage.settings.systemPrompt) || tabStorage.settings.systemPrompt;
|
||||
|
||||
userMessages.push({
|
||||
role: 'system',
|
||||
content: prompt
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是 xml 模式,则在开头注入 xml
|
||||
if (enableXmlWrapper) {
|
||||
prompt += getXmlWrapperPrompt(tabStorage.settings.enableTools, tabStorage);
|
||||
}
|
||||
|
||||
userMessages.push({
|
||||
role: 'system',
|
||||
content: prompt
|
||||
});
|
||||
|
||||
// 如果超出了 tabStorage.settings.contextLength, 则删除最早的消息
|
||||
const loadMessages = tabStorage.messages.slice(- tabStorage.settings.contextLength);
|
||||
userMessages.push(...loadMessages);
|
||||
@ -301,10 +210,7 @@ export class TaskLoop {
|
||||
model,
|
||||
temperature,
|
||||
tools,
|
||||
parallelToolCalls,
|
||||
messages: userMessages,
|
||||
proxyServer,
|
||||
enableXmlWrapper,
|
||||
} as ChatCompletionCreateParamsBase;
|
||||
|
||||
return chatData;
|
||||
@ -319,7 +225,6 @@ export class TaskLoop {
|
||||
});
|
||||
this.streamingContent.value = '';
|
||||
this.streamingToolCalls.value = [];
|
||||
this.aborted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,7 +238,7 @@ export class TaskLoop {
|
||||
public registerOnChunk(handler: (chunk: ChatCompletionChunk) => void) {
|
||||
this.onChunk = handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 注册 chat.completion 完成时触发的回调函数
|
||||
* @param handler
|
||||
@ -366,102 +271,6 @@ export class TaskLoop {
|
||||
this.onToolCalled = handler;
|
||||
}
|
||||
|
||||
private consumeErrors(error: IErrorMssage) {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
if (verbose > 0) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.red('error happen in task loop '),
|
||||
chalk.red(error.msg)
|
||||
);
|
||||
}
|
||||
return this.onError(error);
|
||||
}
|
||||
|
||||
private consumeChunks(chunk: ChatCompletionChunk) {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
if (verbose > 1) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.blue('receive chunk')
|
||||
);
|
||||
} else if (verbose > 2) {
|
||||
const delta = chunk.choices[0]?.delta;
|
||||
if (delta) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.blue('receive chunk'),
|
||||
chalk.bold(JSON.stringify(delta, null, 2))
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.blue('receive chunk'),
|
||||
chalk.blue('delta is empty')
|
||||
);
|
||||
}
|
||||
}
|
||||
return this.onChunk(chunk);
|
||||
}
|
||||
|
||||
private consumeToolCalls(toolCall: ToolCall) {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
|
||||
if (verbose > 0) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.blueBright('🔧 using tool'),
|
||||
chalk.blueBright(toolCall.function!.name)
|
||||
);
|
||||
}
|
||||
|
||||
return this.onToolCall(toolCall);
|
||||
}
|
||||
|
||||
private consumeToolCalleds(result: ToolCallResult) {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
if (verbose > 0) {
|
||||
if (result.state === 'success') {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.green('✓ use tools'),
|
||||
chalk.green(result.state)
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.red('× use tools'),
|
||||
chalk.red(result.content.map(item => item.text).join(', '))
|
||||
);
|
||||
}
|
||||
}
|
||||
return this.onToolCalled(result);
|
||||
}
|
||||
|
||||
private consumeEpochs() {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
if (verbose > 1) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.blue('task loop enters a new epoch')
|
||||
);
|
||||
}
|
||||
return this.onEpoch();
|
||||
}
|
||||
|
||||
private consumeDones() {
|
||||
const { verbose = 0 } = this.taskOptions;
|
||||
|
||||
if (verbose > 1) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.green('task loop finish a epoch')
|
||||
);
|
||||
}
|
||||
|
||||
return this.onDone();
|
||||
}
|
||||
|
||||
public setMaxEpochs(maxEpochs: number) {
|
||||
this.taskOptions.maxEpochs = maxEpochs;
|
||||
}
|
||||
@ -493,43 +302,14 @@ export class TaskLoop {
|
||||
return llms[llmManager.currentModelIndex];
|
||||
}
|
||||
|
||||
public async listTools() {
|
||||
const platform = getPlatform();
|
||||
if (platform === 'nodejs') {
|
||||
// 等待连接完成
|
||||
await this.nodejsStatus.connectionFut;
|
||||
}
|
||||
|
||||
const allTools = [] as ToolItem[];
|
||||
for (const client of mcpClientAdapter.clients) {
|
||||
if (!client.connected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tools = await client.getTools();
|
||||
allTools.push(...Array.from(tools.values()).map(
|
||||
item => ({
|
||||
...item,
|
||||
enabled: true
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
return allTools;
|
||||
public async connectToService() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 开启循环,异步更新 DOM
|
||||
*/
|
||||
public async start(tabStorage: ChatStorage, userMessage: string) {
|
||||
|
||||
const platform = getPlatform();
|
||||
if (platform === 'nodejs') {
|
||||
// 等待连接完成
|
||||
await this.nodejsStatus.connectionFut;
|
||||
}
|
||||
const enableXmlWrapper = tabStorage.settings.enableXmlWrapper;
|
||||
|
||||
// 添加目前的消息
|
||||
tabStorage.messages.push({
|
||||
role: 'user',
|
||||
@ -537,21 +317,16 @@ export class TaskLoop {
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.Success,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
enableXmlWrapper
|
||||
serverName: this.getLlmConfig().id || 'unknown'
|
||||
}
|
||||
});
|
||||
|
||||
let jsonParseErrorRetryCount = 0;
|
||||
const {
|
||||
maxEpochs = 50,
|
||||
verbose = 0
|
||||
} = this.taskOptions || {};
|
||||
this.aborted = false;
|
||||
const maxEpochs = this.taskOptions.maxEpochs || 20;
|
||||
|
||||
for (let i = 0; i < maxEpochs; ++i) {
|
||||
for (let i = 0; i < maxEpochs; ++ i) {
|
||||
|
||||
this.consumeEpochs();
|
||||
this.onEpoch();
|
||||
|
||||
// 初始累计清空
|
||||
this.streamingContent.value = '';
|
||||
@ -562,22 +337,17 @@ export class TaskLoop {
|
||||
const chatData = this.makeChatData(tabStorage);
|
||||
|
||||
if (!chatData) {
|
||||
this.consumeDones();
|
||||
this.onDone();
|
||||
break;
|
||||
}
|
||||
|
||||
this.currentChatId = chatData.id!;
|
||||
const llm = this.getLlmConfig();
|
||||
const toolcallIndexAdapter = getToolCallIndexAdapter(llm, chatData);
|
||||
|
||||
// 发送请求
|
||||
const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter);
|
||||
const doConverationResult = await this.doConversation(chatData);
|
||||
|
||||
// 如果在调用过程中出发了 abort,则直接中断
|
||||
if (this.aborted) {
|
||||
this.aborted = false;
|
||||
break;
|
||||
}
|
||||
console.log('[doConverationResult] Response');
|
||||
console.log(doConverationResult);
|
||||
|
||||
// 如果存在需要调度的工具
|
||||
if (this.streamingToolCalls.value.length > 0) {
|
||||
@ -589,46 +359,25 @@ export class TaskLoop {
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.Success,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
enableXmlWrapper
|
||||
serverName: this.getLlmConfig().id || 'unknown'
|
||||
}
|
||||
});
|
||||
|
||||
if (verbose > 0) {
|
||||
console.log(
|
||||
chalk.gray(`[${new Date().toLocaleString()}]`),
|
||||
chalk.yellow('🤖 Agent wants to use these tools'),
|
||||
chalk.yellow(this.streamingToolCalls.value.map(tool => tool.function!.name || '').join(', '))
|
||||
);
|
||||
}
|
||||
|
||||
pinkLog('调用工具数量:' + this.streamingToolCalls.value.length);
|
||||
|
||||
for (let toolCall of this.streamingToolCalls.value || []) {
|
||||
|
||||
// ready to call tools
|
||||
toolCall = this.consumeToolCalls(toolCall);
|
||||
|
||||
if (this.aborted) {
|
||||
this.aborted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
toolCall = this.onToolCall(toolCall);
|
||||
let toolCallResult = await handleToolCalls(toolCall);
|
||||
|
||||
if (this.aborted) {
|
||||
this.aborted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// hook : finish call tools
|
||||
toolCallResult = this.consumeToolCalleds(toolCallResult);
|
||||
|
||||
toolCallResult = this.onToolCalled(toolCallResult);
|
||||
|
||||
if (toolCallResult.state === MessageState.ParseJsonError) {
|
||||
// 如果是因为解析 JSON 错误,则重新开始
|
||||
tabStorage.messages.pop();
|
||||
jsonParseErrorRetryCount++;
|
||||
|
||||
jsonParseErrorRetryCount ++;
|
||||
|
||||
redLog('解析 JSON 错误 ' + toolCall?.function?.arguments);
|
||||
|
||||
|
||||
// 如果因为 JSON 错误而失败太多,就只能中断了
|
||||
if (jsonParseErrorRetryCount >= (this.taskOptions.maxJsonParseRetry || 3)) {
|
||||
tabStorage.messages.push({
|
||||
@ -638,8 +387,7 @@ export class TaskLoop {
|
||||
created: Date.now(),
|
||||
state: toolCallResult.state,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
usage: undefined,
|
||||
enableXmlWrapper
|
||||
usage: undefined
|
||||
}
|
||||
});
|
||||
break;
|
||||
@ -647,40 +395,33 @@ export class TaskLoop {
|
||||
} else if (toolCallResult.state === MessageState.Success) {
|
||||
tabStorage.messages.push({
|
||||
role: 'tool',
|
||||
index: toolcallIndexAdapter(toolCall),
|
||||
tool_call_id: toolCall.id || '',
|
||||
index: toolCall.index || 0,
|
||||
tool_call_id: toolCall.id || toolCall.function.name,
|
||||
content: toolCallResult.content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: toolCallResult.state,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
usage: this.completionUsage,
|
||||
enableXmlWrapper
|
||||
usage: this.completionUsage
|
||||
}
|
||||
});
|
||||
} else if (toolCallResult.state === MessageState.ToolCall) {
|
||||
|
||||
|
||||
tabStorage.messages.push({
|
||||
role: 'tool',
|
||||
index: toolcallIndexAdapter(toolCall),
|
||||
tool_call_id: toolCall.id || toolCall.function!.name,
|
||||
index: toolCall.index || 0,
|
||||
tool_call_id: toolCall.id || toolCall.function.name,
|
||||
content: toolCallResult.content,
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: toolCallResult.state,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
usage: this.completionUsage,
|
||||
enableXmlWrapper
|
||||
usage: this.completionUsage
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.aborted) {
|
||||
this.aborted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (this.streamingContent.value) {
|
||||
tabStorage.messages.push({
|
||||
role: 'assistant',
|
||||
@ -689,96 +430,10 @@ export class TaskLoop {
|
||||
created: Date.now(),
|
||||
state: MessageState.Success,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
usage: this.completionUsage,
|
||||
enableXmlWrapper
|
||||
usage: this.completionUsage
|
||||
}
|
||||
});
|
||||
|
||||
// 如果 xml 模型,需要检查内部是否含有有效的 xml 进行调用
|
||||
if (tabStorage.settings.enableXmlWrapper) {
|
||||
const xmls = getXmlsFromString(this.streamingContent.value);
|
||||
if (xmls.length === 0) {
|
||||
// 没有 xml 了,说明对话结束
|
||||
break;
|
||||
}
|
||||
|
||||
// 使用 user 作为身份来承载 xml 调用的结果
|
||||
// 并且在 extra 内存储结构化信息
|
||||
const fakeUserMessage = {
|
||||
role: 'user',
|
||||
content: '',
|
||||
extraInfo: {
|
||||
created: Date.now(),
|
||||
state: MessageState.Success,
|
||||
serverName: this.getLlmConfig().id || 'unknown',
|
||||
usage: this.completionUsage,
|
||||
enableXmlWrapper,
|
||||
}
|
||||
} as ChatMessage;
|
||||
|
||||
// 有 xml 了,需要检查 xml 内部是否有有效的 xml 进行调用
|
||||
for (const xml of xmls) {
|
||||
const toolcall = await getToolCallFromXmlString(xml);
|
||||
|
||||
if (!toolcall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// toolcall 事件
|
||||
// 此处使用的是 xml 使用的 toolcall,为了保持一致性,需要转换成 openai 标准下的 toolcall
|
||||
const normaliseToolcall = toNormaliseToolcall(toolcall, toolcallIndexAdapter);
|
||||
this.consumeToolCalls(normaliseToolcall);
|
||||
|
||||
// 调用 XML 调用,其实可以考虑后续把这个循环改成 Promise.race
|
||||
const toolCallResult = await handleXmlWrapperToolcall(toolcall);
|
||||
|
||||
// toolcalled 事件
|
||||
// 因为是交付给后续进行统一消费的,所以此处的输出满足 openai 接口规范
|
||||
this.consumeToolCalleds(toolCallResult);
|
||||
|
||||
// XML 模式下只存在 assistant 和 user 这两个角色,因此,以 user 为身份来存储
|
||||
if (toolCallResult.state === MessageState.InvalidXml) {
|
||||
// 如果是因为解析 XML 错误,则重新开始
|
||||
tabStorage.messages.pop();
|
||||
jsonParseErrorRetryCount ++;
|
||||
|
||||
redLog('解析 XML 错误 ' + normaliseToolcall?.function?.arguments);
|
||||
|
||||
// 如果因为 XML 错误而失败太多,就只能中断了
|
||||
if (jsonParseErrorRetryCount >= (this.taskOptions.maxJsonParseRetry || 3)) {
|
||||
|
||||
const prompt = getXmlResultPrompt(toolcall.callId, `解析 XML 错误,无法继续调用工具 (累计错误次数 ${this.taskOptions.maxJsonParseRetry})`);
|
||||
|
||||
fakeUserMessage.content += prompt;
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (toolCallResult.state === MessageState.Success) {
|
||||
// TODO: xml 目前只支持 text 类型的回复
|
||||
const toolCallResultString = toolCallResult.content
|
||||
.filter(c => c.type === 'text')
|
||||
.map(c => c.text)
|
||||
.join('\n');
|
||||
|
||||
fakeUserMessage.content += getXmlResultPrompt(toolcall.callId, toolCallResultString);
|
||||
|
||||
} else if (toolCallResult.state === MessageState.ToolCall) {
|
||||
// TODO: xml 目前只支持 text 类型的回复
|
||||
const toolCallResultString = toolCallResult.content
|
||||
.filter(c => c.type === 'text')
|
||||
.map(c => c.text)
|
||||
.join('\n');
|
||||
|
||||
fakeUserMessage.content += getXmlResultPrompt(toolcall.callId, toolCallResultString);
|
||||
}
|
||||
}
|
||||
|
||||
tabStorage.messages.push(fakeUserMessage);
|
||||
|
||||
} else {
|
||||
// 普通对话直接结束
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
// 一些提示
|
||||
@ -791,52 +446,4 @@ export class TaskLoop {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async createStorage(settings?: Partial<ChatSetting>): Promise<ChatStorage> {
|
||||
let {
|
||||
enableXmlWrapper = false,
|
||||
systemPrompt = '',
|
||||
temperature = 0.6,
|
||||
contextLength = 100,
|
||||
parallelToolCalls = true,
|
||||
enableWebSearch = false,
|
||||
enableTools = undefined,
|
||||
} = settings || {};
|
||||
|
||||
if (enableTools === undefined) {
|
||||
// 默认缺省的情况下使用全部工具
|
||||
const tools = await this.listTools();
|
||||
enableTools = tools.map(tool => ({
|
||||
...tool,
|
||||
enabled: true
|
||||
})) as EnableToolItem[];
|
||||
}
|
||||
|
||||
const _settings = {
|
||||
enableXmlWrapper,
|
||||
systemPrompt,
|
||||
temperature,
|
||||
contextLength,
|
||||
parallelToolCalls,
|
||||
enableTools,
|
||||
enableWebSearch
|
||||
} as ChatSetting;
|
||||
|
||||
return {
|
||||
messages: [],
|
||||
settings: _settings
|
||||
}
|
||||
}
|
||||
|
||||
public async getPrompt(promptId: string, args?: Record<string, any>) {
|
||||
const prompt = await mcpClientAdapter.readPromptTemplate(promptId, args);
|
||||
// transform prompt to string
|
||||
const promptString = prompt.messages.map(m => m.content.text).join('\n');
|
||||
return promptString;
|
||||
}
|
||||
|
||||
public async getResource(resourceUri: string) {
|
||||
const resource = await mcpClientAdapter.readResource(resourceUri);
|
||||
return resource;
|
||||
}
|
||||
}
|
@ -1,294 +0,0 @@
|
||||
import { parseString } from 'xml2js';
|
||||
import { MessageState, type ToolCall } from '../chat-box/chat';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { handleToolResponse, type IToolCallIndex, type ToolCallResult } from './handle-tool-calls';
|
||||
import type { ChatStorage, EnableToolItem } from "../chat-box/chat";
|
||||
import type { ToolCallContent } from '@/hook/type';
|
||||
|
||||
export interface XmlToolCall {
|
||||
server: string;
|
||||
name: string;
|
||||
callId: string;
|
||||
parameters: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
export function toolSchemaToPromptDescription(enableTools: EnableToolItem[]) {
|
||||
let prompt = '';
|
||||
|
||||
const tools = enableTools.filter(tool => tool.enabled);
|
||||
|
||||
// 无参数的工具
|
||||
const noParamTools = tools.filter(tool =>
|
||||
!tool.inputSchema.required || tool.inputSchema.required.length === 0
|
||||
);
|
||||
|
||||
if (noParamTools.length > 0) {
|
||||
noParamTools.forEach(tool => {
|
||||
prompt += `- ${tool.name}\n`;
|
||||
prompt += `**Description**: ${tool.description}\n\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// 有参数的工具
|
||||
const paramTools = tools.filter(tool =>
|
||||
tool.inputSchema.required && tool.inputSchema.required.length > 0
|
||||
);
|
||||
|
||||
if (paramTools.length > 0) {
|
||||
paramTools.forEach(tool => {
|
||||
prompt += `- ${tool.name}\n`;
|
||||
prompt += `**Description**: ${tool.description}\n`;
|
||||
prompt += `**Parameters**:\n`;
|
||||
|
||||
Object.entries(tool.inputSchema.properties).forEach(([name, prop]) => {
|
||||
const required = tool.inputSchema.required?.includes(name) || false;
|
||||
prompt += `- \`${name}\`: ${prop.description || 'No Description'} (${prop.type}) ${required ? '(required)' : ''}\n`;
|
||||
});
|
||||
|
||||
prompt += '\n';
|
||||
});
|
||||
}
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
export function getXmlWrapperPrompt(tools: EnableToolItem[], tabStorage: ChatStorage) {
|
||||
|
||||
const toolPrompt = toolSchemaToPromptDescription(tools);
|
||||
const requests = [
|
||||
`ALWAYS analyze what function calls would be appropriate for the task`,
|
||||
`ALWAYS format your function call usage EXACTLY as specified in the schema`,
|
||||
`NEVER skip required parameters in function calls`,
|
||||
`NEVER invent functions that arent available to you`,
|
||||
`ALWAYS wait for function call execution results before continuing`,
|
||||
`After invoking a function, wait for the output in <function_results> tag and then continue with your response`,
|
||||
`NEVER mock or form <function_results> on your own, it will be provided to you after the execution`,
|
||||
];
|
||||
|
||||
if (!tabStorage.settings.parallelToolCalls) {
|
||||
requests.push(`NEVER invoke multiple functions in a single response`);
|
||||
}
|
||||
|
||||
const requestString = requests.map((text, index) => {
|
||||
return `${index + 1}. ${text}`;
|
||||
}).join('\n');
|
||||
|
||||
return `
|
||||
[Start Fresh Session from here]
|
||||
|
||||
<SYSTEM>
|
||||
You are OpenMCP Assistant with the capabilities of invoke functions and make the best use of it during your assistance, a knowledgeable assistant focused on answering questions and providing information on any topics.
|
||||
In this environment you have access to a set of tools you can use to answer the user's question.
|
||||
You have access to a set of functions you can use to answer the user's question. You do NOT currently have the ability to inspect files or interact with external resources, except by invoking the below functions.
|
||||
|
||||
Function Call Structure:
|
||||
- All function calls should be wrapped in 'xml' codeblocks tags like \`\`\`xml ... \`\`\`. This is strict requirement.
|
||||
- Wrap all function calls in 'function_calls' tags
|
||||
- Each function call uses 'invoke' tags with a 'name' attribute
|
||||
- Parameters use 'parameter' tags with 'name' attributes
|
||||
- Parameter Formatting:
|
||||
- String/scalar parameters: written directly as values
|
||||
- Lists/objects: must use proper JSON format
|
||||
- Required parameters must always be included
|
||||
- Optional parameters should only be included when needed
|
||||
- If there is xml inside the parameter value, do not use CDATA for wrapping it, just give the xml directly
|
||||
|
||||
The instructions regarding 'invoke' specify that:
|
||||
- When invoking functions, use the 'invoke' tag with a 'name' attribute specifying the function name.
|
||||
- The invoke tag must be nested within an 'function_calls' block.
|
||||
- Parameters for the function should be included as 'parameter' tags within the invoke tag, each with a 'name' attribute.
|
||||
- Include all required parameters for each function call, while optional parameters should only be included when necessary.
|
||||
- String and scalar parameters should be specified directly as values, while lists and objects should use proper JSON format.
|
||||
- Do not refer to function/tool names when speaking directly to users - focus on what I'm doing rather than the tool I'm using.
|
||||
- When invoking a function, ensure all necessary context is provided for the function to execute properly.
|
||||
- Each 'invoke' tag should represent a single, complete function call with all its relevant parameters.
|
||||
- DO not generate any <function_calls> tag in your thinking/resoning process, because those will be interpreted as a function call and executed. just formulate the correct parameters for the function call.
|
||||
|
||||
The instructions regarding 'call_id="$CALL_ID">
|
||||
- It is a unique identifier for the function call.
|
||||
- It is a number that is incremented by 1 for each new function call, starting from 1.
|
||||
|
||||
You can invoke one or more functions by writing a "<function_calls>" block like the following as part of your reply to the user, MAKE SURE TO INVOKE ONLY ONE FUNCTION AT A TIME, meaning only one '<function_calls>' tag in your output :
|
||||
|
||||
<Example>
|
||||
\`\`\`xml
|
||||
<function_calls>
|
||||
<invoke name="$FUNCTION_NAME" call_id="$CALL_ID">
|
||||
<parameter name="$PARAMETER_NAME_1">$PARAMETER_VALUE</parameter>
|
||||
<parameter name="$PARAMETER_NAME_2">$PARAMETER_VALUE</parameter>
|
||||
...
|
||||
</invoke>
|
||||
</function_calls>
|
||||
\`\`\`
|
||||
</Example>
|
||||
|
||||
String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular expressions.
|
||||
|
||||
When a user makes a request:
|
||||
${requestString}
|
||||
|
||||
Answer the user's request using the relevant tool(s), if they are available. Check that all the required parameters for each tool call are provided or can reasonably be inferred from context. IF there are no relevant tools or there are missing values for required parameters, ask the user to supply these values; otherwise proceed with the tool calls. If the user provides a specific value for a parameter (for example provided in quotes), make sure to use that value EXACTLY. DO NOT make up values for or ask about optional parameters. Carefully analyze descriptive terms in the request as they may indicate required parameter values that should be included even if not explicitly quoted.
|
||||
|
||||
<Output Format>
|
||||
<Start HERE>
|
||||
## Thoughts
|
||||
- User Query Elaboration:
|
||||
- Thoughts:
|
||||
- Observations:
|
||||
- Solutions:
|
||||
- Function to be used:
|
||||
- call_id: $CALL_ID + 1 = $CALL_ID
|
||||
|
||||
\`\`\`xml
|
||||
<function_calls>
|
||||
<invoke name="$FUNCTION_NAME" call_id="$CALL_ID">
|
||||
<parameter name="$PARAMETER_NAME_1">$PARAMETER_VALUE</parameter>
|
||||
<parameter name="$PARAMETER_NAME_2">$PARAMETER_VALUE</parameter>
|
||||
...
|
||||
</invoke>
|
||||
</function_calls>
|
||||
\`\`\`
|
||||
<End HERE>
|
||||
</Output Format>
|
||||
|
||||
Do not use <Start HERE> and <End HERE> in your output, that is just output format reference to where to start and end your output.
|
||||
## AVAILABLE TOOLS FOR SUPERASSISTANT
|
||||
|
||||
${toolPrompt}
|
||||
|
||||
- list_servers
|
||||
**Description**: List all connected MCP servers and their capabilities
|
||||
|
||||
- get_server_info
|
||||
**Description**: Get detailed information about a specific server
|
||||
**Parameters**:
|
||||
- \`serverName\`: Name of the server to get info for (string) (required)
|
||||
</SYSTEM>
|
||||
|
||||
User Interaction Starts here:
|
||||
|
||||
`.trim();
|
||||
}
|
||||
|
||||
|
||||
export function getXmlResultPrompt(callId: string, result: string) {
|
||||
return `
|
||||
\`\`\`xml
|
||||
<function_results>
|
||||
<result call_id="${callId}">
|
||||
${result}
|
||||
</result>
|
||||
</function_results>
|
||||
\`\`\`
|
||||
`.trim() + '\n\n';
|
||||
}
|
||||
|
||||
export function getXmlsFromString(content: string) {
|
||||
const matches = content.matchAll(/```xml\n([\s\S]*?)\n```/g);
|
||||
return Array.from(matches).map(match => match[1].trim());
|
||||
}
|
||||
|
||||
|
||||
export async function getToolCallFromXmlString(xmlString: string): Promise<XmlToolCall | null> {
|
||||
try {
|
||||
const result = await new Promise<any>((resolve, reject) => {
|
||||
parseString(xmlString, (err, result) => {
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
if (!result?.function_calls?.invoke) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const invoke = result.function_calls.invoke[0].$;
|
||||
const parameters: Record<string, any> = {};
|
||||
|
||||
if (result.function_calls.invoke[0].parameter) {
|
||||
result.function_calls.invoke[0].parameter.forEach((param: any) => {
|
||||
const name = param.$.name as string;
|
||||
parameters[name] = param._;
|
||||
});
|
||||
}
|
||||
|
||||
// name 可能是 neo4j-mcp.executeReadOnlyCypherQuery
|
||||
return {
|
||||
server: '',
|
||||
name: invoke.name,
|
||||
callId: invoke.call_id,
|
||||
parameters
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to parse function calls:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function getToolResultFromXmlString(xmlString: string) {
|
||||
try {
|
||||
const result = await new Promise<any>((resolve, reject) => {
|
||||
parseString(xmlString, (err, result) => {
|
||||
if (err) reject(err);
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
if (!result?.function_results?.result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resultData = result.function_results.result[0];
|
||||
const callId = resultData.$.call_id;
|
||||
|
||||
// 提取所有评论文本
|
||||
const toolcallContent = [] as ToolCallContent[];
|
||||
const content = resultData._;
|
||||
|
||||
toolcallContent.push({
|
||||
type: 'text',
|
||||
text: content
|
||||
});
|
||||
|
||||
return {
|
||||
callId,
|
||||
toolcallContent
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to parse function results:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function toNormaliseToolcall(xmlToolcall: XmlToolCall, toolcallIndexAdapter: (toolCall: ToolCall) => IToolCallIndex): ToolCall {
|
||||
const toolcall = {
|
||||
id: xmlToolcall.callId,
|
||||
index: -1,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: xmlToolcall.name,
|
||||
arguments: JSON.stringify(xmlToolcall.parameters)
|
||||
}
|
||||
} as ToolCall;
|
||||
|
||||
toolcall.index = toolcallIndexAdapter(toolcall);
|
||||
|
||||
return toolcall;
|
||||
}
|
||||
|
||||
export async function handleXmlWrapperToolcall(toolcall: XmlToolCall): Promise<ToolCallResult> {
|
||||
if (!toolcall) {
|
||||
return {
|
||||
content: [{
|
||||
type: 'error',
|
||||
text: 'invalid xml'
|
||||
}],
|
||||
state: MessageState.InvalidXml
|
||||
}
|
||||
}
|
||||
|
||||
const toolResponse = await mcpClientAdapter.callTool(toolcall.name, toolcall.parameters);
|
||||
return handleToolResponse(toolResponse);
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="chat-container" :ref="el => chatContainerRef = el">
|
||||
<el-scrollbar ref="scrollbarRef" :height="'90%'" @scroll="handleScroll"
|
||||
v-if="renderMessages.length > 0 || isLoading">
|
||||
<el-scrollbar ref="scrollbarRef" :height="'90%'" @scroll="handleScroll" v-if="renderMessages.length > 0 || isLoading">
|
||||
<div class="message-list" :ref="el => messageListRef = el">
|
||||
<div v-for="(message, index) in renderMessages" :key="index"
|
||||
:class="['message-item', message.role.split('/')[0], message.role.split('/')[1]]">
|
||||
:class="['message-item', message.role.split('/')[0], message.role.split('/')[1]]"
|
||||
>
|
||||
<div class="message-avatar" v-if="message.role === 'assistant/content'">
|
||||
<span class="iconfont icon-robot"></span>
|
||||
</div>
|
||||
@ -23,8 +23,10 @@
|
||||
|
||||
<!-- 助手调用的工具部分 -->
|
||||
<div class="message-content" v-else-if="message.role === 'assistant/tool_calls'">
|
||||
<Message.Toolcall :message="message" :tab-id="props.tabId"
|
||||
@update:tool-result="(value, toolIndex, index) => message.toolResults[toolIndex][index] = value" />
|
||||
<Message.Toolcall
|
||||
:message="message" :tab-id="props.tabId"
|
||||
@update:tool-result="(value, toolIndex, index) => message.toolResults[toolIndex][index] = value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -45,22 +47,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatBox :ref="el => footerRef = el" :tab-id="props.tabId" />
|
||||
<ChatBox
|
||||
:ref="el => footerRef = el"
|
||||
:tab-id="props.tabId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineComponent, defineProps, onUnmounted, computed, nextTick, watch, provide, watchEffect } from 'vue';
|
||||
import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed, nextTick, watch, provide } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { type ScrollbarInstance } from 'element-plus';
|
||||
import { ElMessage, type ScrollbarInstance } from 'element-plus';
|
||||
import { tabs } from '../panel';
|
||||
import type { ChatMessage, ChatStorage, IRenderMessage, ToolCall } from './chat-box/chat';
|
||||
import { MessageState } from './chat-box/chat';
|
||||
|
||||
import * as Message from './message';
|
||||
import ChatBox from './chat-box/index.vue';
|
||||
import { getToolCallFromXmlString, getToolResultFromXmlString, getXmlsFromString, toNormaliseToolcall } from './core/xml-wrapper';
|
||||
import { getIdAsIndexAdapter } from './core/handle-tool-calls';
|
||||
|
||||
|
||||
defineComponent({ name: 'chat' });
|
||||
@ -82,71 +85,18 @@ if (!tabStorage.messages) {
|
||||
tabStorage.messages = [] as ChatMessage[];
|
||||
}
|
||||
|
||||
function getXmlToolCalls(message: ChatMessage) {
|
||||
if (message.role !== 'assistant' && message.role !== 'user') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const enableXmlTools = message.extraInfo?.enableXmlWrapper ?? false;
|
||||
|
||||
if (!enableXmlTools) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const xmls = getXmlsFromString(message.content);
|
||||
|
||||
return xmls || [];
|
||||
}
|
||||
|
||||
const renderMessages = ref<IRenderMessage[]>([]);
|
||||
|
||||
watchEffect(async () => {
|
||||
renderMessages.value = [];
|
||||
|
||||
const renderMessages = computed(() => {
|
||||
const messages: IRenderMessage[] = [];
|
||||
for (const message of tabStorage.messages) {
|
||||
const indexAdapter = getIdAsIndexAdapter();
|
||||
const xmls = getXmlToolCalls(message);
|
||||
|
||||
if (message.role === 'user') {
|
||||
if (xmls.length > 0 && message.extraInfo.enableXmlWrapper) {
|
||||
// 判断是否是 xml 模式,如果是 xml 模式且存在有效的 xml,则按照工具来判定
|
||||
// 往前寻找 assistant/tool_calls 并自动加入其中
|
||||
const lastAssistantMessage = renderMessages.value[renderMessages.value.length - 1];
|
||||
if (lastAssistantMessage.role === 'assistant/tool_calls') {
|
||||
|
||||
const toolCallResultXmls = getXmlsFromString(message.content);
|
||||
|
||||
for (const xml of toolCallResultXmls) {
|
||||
const toolResult = await getToolResultFromXmlString(xml);
|
||||
if (toolResult) {
|
||||
const index = indexAdapter(toolResult.callId);
|
||||
|
||||
lastAssistantMessage.toolResults[index] = toolResult.toolcallContent;
|
||||
|
||||
if (lastAssistantMessage.extraInfo.state === MessageState.Unknown) {
|
||||
lastAssistantMessage.extraInfo.state = message.extraInfo.state;
|
||||
} else if (lastAssistantMessage.extraInfo.state === MessageState.Success
|
||||
|| message.extraInfo.state !== MessageState.Success
|
||||
) {
|
||||
lastAssistantMessage.extraInfo.state = message.extraInfo.state;
|
||||
}
|
||||
|
||||
lastAssistantMessage.extraInfo.usage = lastAssistantMessage.extraInfo.usage || message.extraInfo.usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
renderMessages.value.push({
|
||||
role: 'user',
|
||||
content: message.content,
|
||||
extraInfo: message.extraInfo
|
||||
});
|
||||
}
|
||||
|
||||
messages.push({
|
||||
role: 'user',
|
||||
content: message.content,
|
||||
extraInfo: message.extraInfo
|
||||
});
|
||||
} else if (message.role === 'assistant') {
|
||||
if (message.tool_calls) {
|
||||
renderMessages.value.push({
|
||||
messages.push({
|
||||
role: 'assistant/tool_calls',
|
||||
content: message.content,
|
||||
toolResults: Array(message.tool_calls.length).fill([]),
|
||||
@ -158,43 +108,16 @@ watchEffect(async () => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (xmls.length > 0 && message.extraInfo.enableXmlWrapper) {
|
||||
// 判断是否是 xml 模式,如果是 xml 模式且存在有效的 xml,则按照工具来判定
|
||||
const toolCalls = [];
|
||||
for (const xml of xmls) {
|
||||
const xmlToolCall = await getToolCallFromXmlString(xml);
|
||||
if (xmlToolCall) {
|
||||
toolCalls.push(
|
||||
toNormaliseToolcall(xmlToolCall, indexAdapter)
|
||||
);
|
||||
}
|
||||
}
|
||||
const renderAssistantMessage = message.content.replace(/```xml[\s\S]*?```/g, '');
|
||||
|
||||
renderMessages.value.push({
|
||||
role: 'assistant/tool_calls',
|
||||
content: renderAssistantMessage,
|
||||
toolResults: Array(toolCalls.length).fill([]),
|
||||
tool_calls: toolCalls,
|
||||
showJson: ref(false),
|
||||
extraInfo: {
|
||||
...message.extraInfo,
|
||||
state: MessageState.Unknown
|
||||
}
|
||||
});
|
||||
} else {
|
||||
renderMessages.value.push({
|
||||
role: 'assistant/content',
|
||||
content: message.content,
|
||||
extraInfo: message.extraInfo
|
||||
});
|
||||
}
|
||||
|
||||
messages.push({
|
||||
role: 'assistant/content',
|
||||
content: message.content,
|
||||
extraInfo: message.extraInfo
|
||||
});
|
||||
}
|
||||
|
||||
} else if (message.role === 'tool') {
|
||||
// 如果是工具,则合并进入 之前 assistant 一起渲染
|
||||
const lastAssistantMessage = renderMessages.value[renderMessages.value.length - 1];
|
||||
const lastAssistantMessage = messages[messages.length - 1];
|
||||
if (lastAssistantMessage.role === 'assistant/tool_calls') {
|
||||
lastAssistantMessage.toolResults[message.index] = message.content;
|
||||
|
||||
@ -205,13 +128,14 @@ watchEffect(async () => {
|
||||
) {
|
||||
lastAssistantMessage.extraInfo.state = message.extraInfo.state;
|
||||
}
|
||||
|
||||
|
||||
lastAssistantMessage.extraInfo.usage = lastAssistantMessage.extraInfo.usage || message.extraInfo.usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return messages;
|
||||
});
|
||||
|
||||
const isLoading = ref(false);
|
||||
|
||||
@ -308,14 +232,14 @@ watch(streamingToolCalls, () => {
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
.chat-openmcp-icon>div {
|
||||
.chat-openmcp-icon > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: left;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.chat-openmcp-icon>div>span {
|
||||
.chat-openmcp-icon > div > span {
|
||||
margin-bottom: 23px;
|
||||
}
|
||||
|
||||
@ -361,7 +285,7 @@ watch(streamingToolCalls, () => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user .message-text>span {
|
||||
.user .message-text > span {
|
||||
border-radius: .9em;
|
||||
background-color: var(--main-light-color);
|
||||
padding: 10px 15px;
|
||||
@ -416,12 +340,9 @@ watch(streamingToolCalls, () => {
|
||||
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
@ -66,7 +66,7 @@ export default function highlight(option: HighlightOption = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
window.navigator.clipboard.writeText(code).then(() => {
|
||||
const originalText = button.textContent;
|
||||
button.textContent = '已复制';
|
||||
setTimeout(() => {
|
||||
|
@ -110,7 +110,7 @@ const showFullImage = () => {
|
||||
const img = new Image();
|
||||
img.src = thumbnail.value;
|
||||
img.onload = () => {
|
||||
const overlay = document?.createElement('div');
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.position = 'fixed';
|
||||
overlay.style.top = '0';
|
||||
overlay.style.left = '0';
|
||||
@ -121,9 +121,9 @@ const showFullImage = () => {
|
||||
overlay.style.display = 'flex';
|
||||
overlay.style.justifyContent = 'center';
|
||||
overlay.style.alignItems = 'center';
|
||||
overlay.onclick = () => document?.body.removeChild(overlay);
|
||||
overlay.onclick = () => document.body.removeChild(overlay);
|
||||
|
||||
const imgContainer = document?.createElement('div');
|
||||
const imgContainer = document.createElement('div');
|
||||
imgContainer.style.maxWidth = '90vw';
|
||||
imgContainer.style.maxHeight = '90vh';
|
||||
imgContainer.style.overflow = 'auto';
|
||||
@ -137,7 +137,7 @@ const showFullImage = () => {
|
||||
|
||||
imgContainer.appendChild(fullImg);
|
||||
overlay.appendChild(imgContainer);
|
||||
document?.body.appendChild(overlay);
|
||||
document.body.appendChild(overlay);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="message-role">
|
||||
<span class="message-reminder" v-if="callingTools">
|
||||
Agent {{ t('using-tool') }}
|
||||
Agent 正在使用工具
|
||||
<span class="tool-loading iconfont icon-double-loading">
|
||||
</span>
|
||||
</span>
|
||||
@ -20,7 +20,7 @@
|
||||
<span class="tool-name">
|
||||
<span class="iconfont icon-tool"></span>
|
||||
|
||||
{{ props.message.tool_calls[0].function!.name }}
|
||||
{{ props.message.tool_calls[0].function.name }}
|
||||
</span>
|
||||
<el-button size="small" @click="createTest(props.message.tool_calls[0])">
|
||||
<span class="iconfont icon-send"></span>
|
||||
@ -37,7 +37,7 @@
|
||||
<span class="tool-name">
|
||||
<span class="iconfont icon-tool"></span>
|
||||
|
||||
{{ props.message.tool_calls[toolIndex].function!.name }}
|
||||
{{ props.message.tool_calls[toolIndex].function.name }}
|
||||
</span>
|
||||
<el-button size="small" @click="createTest(props.message.tool_calls[toolIndex])">
|
||||
<span class="iconfont icon-send"></span>
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tool-arguments">
|
||||
<json-render :json="props.message.tool_calls[toolIndex].function!.arguments"/>
|
||||
<json-render :json="props.message.tool_calls[toolIndex].function.arguments"/>
|
||||
</div>
|
||||
|
||||
<!-- 工具调用结果 -->
|
||||
@ -318,9 +318,6 @@ function updateToolCallResultItem(value: any, toolIndex: number, index: number)
|
||||
.tool-call-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tool-call-header.result {
|
||||
|