opt layout

This commit is contained in:
锦恢 2025-04-13 02:09:26 +08:00
parent c964f79c99
commit c8a9951be6
13 changed files with 177 additions and 53 deletions

View File

@ -1,8 +1,9 @@
{ {
"name": "openmcp", "name": "openmcp",
"displayName": "openmcp", "displayName": "OpenMCP",
"description": "An all in one MCP Client/TestTool", "description": "An all in one MCP Client/TestTool",
"version": "0.0.1", "version": "0.0.1",
"publisher": "kirigaya",
"author": { "author": {
"name": "kirigaya", "name": "kirigaya",
"email": "1193466151@qq.com" "email": "1193466151@qq.com"
@ -19,7 +20,7 @@
], ],
"activationEvents": [], "activationEvents": [],
"main": "./dist/extension.js", "main": "./dist/extension.js",
"icon": "./icons/openmcp.png", "icon": "icons/openmcp.png",
"contributes": { "contributes": {
"commands": [ "commands": [
{ {

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4870215 */ font-family: "iconfont"; /* Project id 4870215 */
src: url('iconfont.woff2?t=1744289078529') format('woff2'), src: url('iconfont.woff2?t=1744476757936') format('woff2'),
url('iconfont.woff?t=1744289078529') format('woff'), url('iconfont.woff?t=1744476757936') format('woff'),
url('iconfont.ttf?t=1744289078529') format('truetype'); url('iconfont.ttf?t=1744476757936') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-connect:before {
content: "\ecda";
}
.icon-openmcp:before { .icon-openmcp:before {
content: "\e666"; content: "\e666";
} }

Binary file not shown.

View File

@ -14,7 +14,7 @@ import MainPanel from '@/components/main-panel/index.vue';
import { setDefaultCss } from './hook/css'; import { setDefaultCss } from './hook/css';
import { pinkLog } from './views/setting/util'; import { pinkLog } from './views/setting/util';
import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge'; import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge';
import { connectionArgs, connectionMethods, connectionResult, doConnect, launchConnect } from './views/connect/connection'; import { connectionArgs, connectionMethods, connectionResult, doConnect, getServerVersion, launchConnect } from './views/connect/connection';
import { loadSetting } from './hook/setting'; import { loadSetting } from './hook/setting';
import { loadPanels } from './hook/panel'; import { loadPanels } from './hook/panel';
@ -26,16 +26,24 @@ bridge.addCommandListener('hello', data => {
pinkLog(`version: ${data.version}`); pinkLog(`version: ${data.version}`);
}, { once: true }); }, { once: true });
// connect
bridge.addCommandListener('connect', async data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
const res = await getServerVersion() as { name: string, version: string };
connectionResult.serverInfo.name = res.name || '';
connectionResult.serverInfo.version = res.version || '';
}, { once: true });
function initDebug() { function initDebug() {
connectionArgs.commandString = 'mcp run ../servers/main.py'; connectionArgs.commandString = 'mcp run ../servers/main.py';
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
bridge.addCommandListener('connect', data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
}, { once: true });
setTimeout(() => { setTimeout(() => {
// //
loadSetting(); loadSetting();
@ -56,12 +64,6 @@ function initProduce() {
connectionArgs.commandString = 'mcp run ../servers/main.py'; connectionArgs.commandString = 'mcp run ../servers/main.py';
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
bridge.addCommandListener('connect', data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
}, { once: true });
// //
loadSetting(); loadSetting();

View File

@ -2,12 +2,24 @@
<div class="connected-status-container" <div class="connected-status-container"
@click.stop="toggleConnectionPanel()" @click.stop="toggleConnectionPanel()"
> >
<span <span class="mcp-server-info">
class="status-circle" <el-tooltip
:class="statusColorStyle" class="extra-connect-container"
effect="dark"
placement="right"
:content="fullDisplayServerName"
> >
<span class="name">{{ displayServerName }}</span>
</el-tooltip>
</span>
<span class="connect-status">
<span
class="status-circle"
:class="statusColorStyle"
>
</span>
<span class="status-string">{{ statusString }}</span>
</span> </span>
<span class="status-string">{{ statusString }}</span>
</div> </div>
</template> </template>
@ -38,7 +50,17 @@ const statusColorStyle = computed(() => {
} }
}); });
const fullDisplayServerName = computed(() => {
return connectionResult.serverInfo.name + '/' + connectionResult.serverInfo.version;
});
const displayServerName = computed(() => {
if (connectionResult.serverInfo.name.length > 20) {
return connectionResult.serverInfo.name.substring(0, 20);
} else {
return connectionResult.serverInfo.name;
}
});
function toggleConnectionPanel() { function toggleConnectionPanel() {
Connection.showPanel = true; Connection.showPanel = true;
@ -59,27 +81,65 @@ function toggleConnectionPanel() {
.status-circle { .status-circle {
height: 12px; height: 12px;
width: 12px; width: 12px;
margin-right: 10px; margin-right: 8px;
border-radius: 99%; border-radius: 99%;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
} }
.extra-connect-container { .extra-connect-container {
cursor: pointer;
user-select: none; user-select: none;
} }
.connected-status-container { .connected-status-container {
user-select: none; user-select: none;
cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: auto;
justify-content: center; padding: 8px 0;
flex-direction: column;
border-radius: 6px;
transition: background-color 0.3s ease;
} }
.connected-status-container:hover .status-string { .connected-status-container .connect-status {
color: var(--main-color); display: flex;
align-items: center;
margin-top: 20px;
}
.connected-status-container:hover {
background-color: var(--sidebar-hover);
}
.status-string {
color: var(--foreground);
transition: var(--animation-3s); transition: var(--animation-3s);
font-size: 13px;
font-weight: 500;
white-space: nowrap;
margin-top: 4px;
}
.mcp-server-info {
display: flex;
flex-direction: column;
}
.mcp-server-info .name {
font-size: 14px;
font-weight: 600;
max-width: 60px;
white-space: wrap;
background-color: #f39a6d;
padding: 5px;
border-radius: .5em;
color: #1e1e1e;
}
.mcp-server-info .version {
font-size: 12px;
font-weight: 400;
} }
</style> </style>

View File

@ -2,8 +2,6 @@
<div class="sidebar-container"> <div class="sidebar-container">
<div> <div>
<McpTitle></McpTitle> <McpTitle></McpTitle>
<hr>
<br>
<SidebarItemContainer></SidebarItemContainer> <SidebarItemContainer></SidebarItemContainer>
</div> </div>
@ -15,7 +13,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { sidebarItems } from './sidebar';
import McpTitle from './mcp-title.vue'; import McpTitle from './mcp-title.vue';
import SidebarItemContainer from './sidebar-item-container.vue'; import SidebarItemContainer from './sidebar-item-container.vue';
@ -27,7 +24,7 @@ defineComponent({ name: 'sidebar' });
<style> <style>
.sidebar-container { .sidebar-container {
width: 300px; width: fit-content;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mcp-title"> <div class="mcp-title">
<div class="openmcp-logo" style="width: 48px; height: 48px; margin-right: 10px;"></div> <div class="openmcp-logo" style="width: 48px; height: 48px; margin-top: 10px; margin-bottom: 15px; margin-right: 10px;"></div>
<div>OpenMCP</div> <!-- <div>OpenMCP</div> -->
</div> </div>
</template> </template>

View File

@ -1,14 +1,12 @@
<template> <template>
<div class="sidebar-item-container"> <div class="sidebar-item-container">
<div <div v-for="(item, index) of sidebarItems" :key="index">
class="sidebar-option-item" <el-tooltip :content="t(item.ident)" placement="right">
:class="{'active': isActive(item.ident)}" <div class="sidebar-option-item" :class="{ 'active': isActive(item.ident) }"
v-for="(item, index) of sidebarItems" @click="gotoOption(item.ident)">
:key="index" <span :class="`iconfont ${item.icon}`"></span>
@click="gotoOption(item.ident)" </div>
> </el-tooltip>
<span :class="`iconfont ${item.icon}`"></span>
<span>{{ t(item.ident) }}</span>
</div> </div>
</div> </div>
</template> </template>
@ -43,6 +41,7 @@ function gotoOption(ident: string) {
.sidebar-option-item { .sidebar-option-item {
margin: 7px; margin: 7px;
height: 32px; height: 32px;
width: fit-content;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 5px 12px; padding: 5px 12px;
@ -58,8 +57,11 @@ function gotoOption(ident: string) {
} }
.sidebar-option-item .iconfont { .sidebar-option-item .iconfont {
margin-top: 2px; margin-top: 2px;
margin-right: 7px; margin-right: 7px;
width: 13px;
display: flex;
align-content: center;
font-size: 17px; font-size: 17px;
} }
@ -67,5 +69,4 @@ function gotoOption(ident: string) {
background-color: var(--main-light-color); background-color: var(--main-light-color);
transition: var(--animation-3s); transition: var(--animation-3s);
} }
</style> </style>

View File

@ -6,7 +6,7 @@ export const sidebarItems = reactive([
ident: 'debug' ident: 'debug'
}, },
{ {
icon: 'icon-plugin', icon: 'icon-connect',
ident: 'connect' ident: 'connect'
}, },
{ {

View File

@ -162,5 +162,26 @@ export function doReconnect() {
export const connectionResult = reactive({ export const connectionResult = reactive({
success: false, success: false,
logString: '' logString: '',
serverInfo: {
name: '',
version: ''
}
}); });
export function getServerVersion() {
return new Promise((resolve, reject) => {
const bridge = useMessageBridge();
bridge.addCommandListener('server/version', data => {
if (data.code === 200) {
resolve(data.msg);
} else {
reject(data.msg);
}
}, { once: true });
bridge.postMessage({
command: 'server/version',
});
});
}

View File

@ -72,9 +72,14 @@ export class MCPClient {
console.log(`Connected to MCP server via ${this.options.connectionType}`); console.log(`Connected to MCP server via ${this.options.connectionType}`);
} }
public getServerVersion() {
return this.client.getServerVersion();
}
// 断开连接 // 断开连接
public async disconnect(): Promise<void> { public async disconnect(): Promise<void> {
await this.client.close(); await this.client.close();
console.log('Disconnected from MCP server'); console.log('Disconnected from MCP server');
} }

View File

@ -253,3 +253,32 @@ export async function callTool(
webview.postMessage({ command: 'tools/call', data: result }); webview.postMessage({ command: 'tools/call', data: result });
} }
} }
export async function getServerVersion(
client: MCPClient | undefined,
webview: PostMessageble
) {
if (!client) {
const connectResult = {
code: 501,
msg:'mcp client 尚未连接'
};
webview.postMessage({ command: 'server/version', data: connectResult });
return;
}
try {
const version = client.getServerVersion();
const result = {
code: 200,
msg: version
};
webview.postMessage({ command:'server/version', data: result });
} catch (error) {
const result = {
code: 500,
msg: (error as any).toString()
};
webview.postMessage({ command:'server/version', data: result });
}
}

View File

@ -1,7 +1,7 @@
import { PostMessageble } from '../adapter'; import { PostMessageble } from '../adapter';
import { connect, MCPClient, type MCPOptions } from './connect'; import { connect, MCPClient, type MCPOptions } from './connect';
import { callTool, getPrompt, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './handler'; import { callTool, getPrompt, getServerVersion, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './handler';
import { chatCompletionHandler } from './llm'; import { chatCompletionHandler } from './llm';
import { panelLoadHandler, panelSaveHandler } from './panel'; import { panelLoadHandler, panelSaveHandler } from './panel';
import { settingLoadHandler, settingSaveHandler } from './setting'; import { settingLoadHandler, settingSaveHandler } from './setting';
@ -42,6 +42,10 @@ export function messageController(command: string, data: any, webview: PostMessa
connectHandler(data, webview); connectHandler(data, webview);
break; break;
case 'server/version':
getServerVersion(client, webview);
break;
case 'prompts/list': case 'prompts/list':
listPrompts(client, webview); listPrompts(client, webview);
break; break;