diff --git a/package.json b/package.json index 9d9ff9b..f481ae8 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, { "command": "openmcp.sidebar.workspace-connection.revealWebviewPanel", - "title": "展示 OpenMCP", + "title": "连接", "category": "openmcp", "icon": { "light": "./icons/light/protocol.svg", @@ -58,8 +58,11 @@ "view/item/context": [ { "command": "openmcp.sidebar.workspace-connection.revealWebviewPanel", - "group": "navigation", - "when": "view == openmcp.sidebar-view.workspace-connection" + "group": "inline@1", + "when": "view == openmcp.sidebar-view.workspace-connection", + "args": { + "view": "${viewItem}" + } } ] }, @@ -77,7 +80,7 @@ { "id": "openmcp.sidebar-view.workspace-connection", "icon": "./icons/protocol.svg", - "name": "MCP 连接", + "name": "MCP 连接 (工作区)", "type": "tree" }, { diff --git a/src/extension.ts b/src/extension.ts index 4d3a3e9..facdc43 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,7 @@ import * as fspath from 'path'; import * as OpenMCPService from '../resources/service'; import { getLaunchCWD, revealOpenMcpWebviewPanel } from './webview'; -import { registerSidebar } from './sidebar'; +import { ConnectionViewItem, registerSidebar } from './sidebar'; import { getWorkspaceConnectionConfigItemByPath, ISSEConnectionItem, IStdioConnectionItem } from './global'; export function activate(context: vscode.ExtensionContext) { @@ -18,7 +18,8 @@ export function activate(context: vscode.ExtensionContext) { registerSidebar(context); context.subscriptions.push( - vscode.commands.registerCommand('openmcp.sidebar.workspace-connection.revealWebviewPanel', (item: IStdioConnectionItem | ISSEConnectionItem) => { + vscode.commands.registerCommand('openmcp.sidebar.workspace-connection.revealWebviewPanel', (view: ConnectionViewItem) => { + const item = view.item; revealOpenMcpWebviewPanel(context, item.name, item); }) ); diff --git a/src/global.ts b/src/global.ts index a176e24..42507ef 100644 --- a/src/global.ts +++ b/src/global.ts @@ -82,10 +82,10 @@ export function getWorkspaceConnectionConfig() { const workspacePath = getWorkspacePath(); for (const item of connection.items) { if (item.filePath && item.filePath.startsWith('{workspace}')) { - item.filePath = item.filePath.replace('{workspace}', workspacePath).replace('\\', '/'); + item.filePath = item.filePath.replace('{workspace}', workspacePath).replace(/\\/g, '/'); } if (item.type === 'stdio' && item.cwd && item.cwd.startsWith('{workspace}')) { - item.cwd = item.cwd.replace('{workspace}', workspacePath).replace('\\', '/'); + item.cwd = item.cwd.replace('{workspace}', workspacePath).replace(/\\/g, '/'); } } @@ -106,14 +106,14 @@ export function saveWorkspaceConnectionConfig(workspace: string) { const workspacePath = getWorkspacePath(); for (const item of connectionConfig.items) { - if (item.filePath && item.filePath.startsWith(workspacePath)) { - item.filePath = item.filePath.replace(workspacePath, '{workspace}').replace('\\', '/'); + if (item.filePath && item.filePath.replace(/\\/g, '/').startsWith(workspacePath)) { + item.filePath = item.filePath.replace(workspacePath, '{workspace}').replace(/\\/g, '/'); } - if (item.type ==='stdio' && item.cwd && item.cwd.startsWith(workspacePath)) { - item.cwd = item.cwd.replace(workspacePath, '{workspace}').replace('\\', '/'); + if (item.type ==='stdio' && item.cwd && item.cwd.replace(/\\/g, '/').startsWith(workspacePath)) { + item.cwd = item.cwd.replace(workspacePath, '{workspace}').replace(/\\/g, '/'); } } - fs.writeFileSync(connectionConfigPath, JSON.stringify(connectionConfig), 'utf-8'); + fs.writeFileSync(connectionConfigPath, JSON.stringify(connectionConfig, null, 2), 'utf-8'); } interface ClientStdioConnectionItem { @@ -152,9 +152,9 @@ export function updateWorkspaceConnectionConfig(absPath: string, data: ClientStd name: data.clientName, command: data.command, args: data.args, - cwd: data.cwd.replace('\\', '/'), + cwd: data.cwd.replace(/\\/g, '/'), env: data.env, - filePath: absPath.replace('\\', '/') + filePath: absPath.replace(/\\/g, '/') }; // 插入到第一个 @@ -162,7 +162,7 @@ export function updateWorkspaceConnectionConfig(absPath: string, data: ClientStd const workspacePath = getWorkspacePath(); saveWorkspaceConnectionConfig(workspacePath); vscode.commands.executeCommand('openmcp.sidebar.workspace-connection.refresh'); - + } else { } @@ -171,9 +171,9 @@ export function updateWorkspaceConnectionConfig(absPath: string, data: ClientStd function normaliseConnectionFilePath(item: IStdioConnectionItem | ISSEConnectionItem, workspace: string) { if (item.filePath) { if (item.filePath.startsWith('{workspace}')) { - return item.filePath.replace('{workspace}', workspace).replace('\\', '/'); + return item.filePath.replace('{workspace}', workspace).replace(/\\/g, '/'); } else { - return item.filePath.replace('\\', '/'); + return item.filePath.replace(/\\/g, '/'); } } @@ -182,7 +182,7 @@ function normaliseConnectionFilePath(item: IStdioConnectionItem | ISSEConnection export function getWorkspacePath() { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - return (workspaceFolder?.uri.fsPath || '').replace('\\', '/'); + return (workspaceFolder?.uri.fsPath || '').replace(/\\/g, '/'); } /** @@ -193,7 +193,7 @@ export function getWorkspaceConnectionConfigItemByPath(absPath: string) { const workspacePath = getWorkspacePath(); const workspaceConnectionConfig = getWorkspaceConnectionConfig(); - const normaliseAbsPath = absPath.replace('\\', '/'); + const normaliseAbsPath = absPath.replace(/\\/g, '/'); for (const item of workspaceConnectionConfig.items) { const filePath = normaliseConnectionFilePath(item, workspacePath); if (filePath === normaliseAbsPath) { diff --git a/src/sidebar.ts b/src/sidebar.ts index a34e9ba..cfa3c81 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -1,29 +1,39 @@ import * as vscode from 'vscode'; -import { getConnectionConfig, getWorkspaceConnectionConfig } from './global'; +import { getConnectionConfig, getWorkspaceConnectionConfig, ISSEConnectionItem, IStdioConnectionItem } from './global'; -class McpWorkspaceConnectProvider implements vscode.TreeDataProvider { - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; +class McpWorkspaceConnectProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; constructor(private context: vscode.ExtensionContext) { } // 实现 TreeDataProvider 接口 - getTreeItem(element: SidebarItem): vscode.TreeItem { + getTreeItem(element: ConnectionViewItem): vscode.TreeItem { return element; } - getChildren(element?: SidebarItem): Thenable { + getChildren(element?: ConnectionViewItem): Thenable { // TODO: 读取 configDir 下的所有文件,作为子节点 const connection = getWorkspaceConnectionConfig(); const sidebarItems = connection.items.map((item, index) => { - return new SidebarItem(item.name, vscode.TreeItemCollapsibleState.None); + // 连接的名字 + const itemName = this.displayName(item); + return new ConnectionViewItem(itemName, vscode.TreeItemCollapsibleState.None, item, 'server'); }) // 返回子节点 return Promise.resolve(sidebarItems); } + public displayName(item: IStdioConnectionItem | ISSEConnectionItem) { + if (item.filePath) { + const filename = item.filePath.split('/').pop(); + return `${filename} (${item.type})`; + } + return item.name; + } + // 添加 refresh 方法 public refresh(): void { this._onDidChangeTreeData.fire(); @@ -105,4 +115,16 @@ class SidebarItem extends vscode.TreeItem { this.command = command; this.iconPath = new vscode.ThemeIcon(icon || 'circle-outline'); } +} + +export class ConnectionViewItem extends vscode.TreeItem { + constructor( + public readonly label: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly item: IStdioConnectionItem | ISSEConnectionItem, + public readonly icon?: string + ) { + super(label, collapsibleState); + this.iconPath = new vscode.ThemeIcon(icon || 'circle-outline'); + } } \ No newline at end of file