diff --git a/src/components/chat-view/ChatView.tsx b/src/components/chat-view/ChatView.tsx index 9d18248..bf9cbf2 100644 --- a/src/components/chat-view/ChatView.tsx +++ b/src/components/chat-view/ChatView.tsx @@ -59,12 +59,12 @@ import { editorStateToPlainText } from './chat-input/utils/editor-state-to-plain import { ChatHistory } from './ChatHistoryView' import CommandsView from './CommandsView' import CustomModeView from './CustomModeView' -import MarkdownReasoningBlock from './Markdown/MarkdownReasoningBlock' +import HelloInfo from './HelloInfo' import McpHubView from './McpHubView' // Moved after MarkdownReasoningBlock import QueryProgress, { QueryProgressState } from './QueryProgress' import ReactMarkdown from './ReactMarkdown' -import ShortcutInfo from './ShortcutInfo' import SimilaritySearchResults from './SimilaritySearchResults' +import MarkdownReasoningBlock from './Markdown/MarkdownReasoningBlock' // Add an empty line here const getNewInputMessage = (app: App, defaultMention: string): ChatUserMessage => { @@ -609,7 +609,7 @@ const Chat = forwardRef((props, ref) => { } } else if (toolArgs.type === 'regex_search_files') { // @ts-expect-error Obsidian API type mismatch - const baseVaultPath = app.vault.adapter.getBasePath() + const baseVaultPath = String(app.vault.adapter.getBasePath()) const ripgrepPath = settings.ripgrepPath const absolutePath = path.join(baseVaultPath, toolArgs.filepath) const results = await regexSearchFiles(absolutePath, toolArgs.regex, ripgrepPath) @@ -730,7 +730,7 @@ const Chat = forwardRef((props, ref) => { return item.text } if (item.type === "resource") { - const { blob: _, ...rest } = item.resource + const { blob: _blob, ...rest } = item.resource return JSON.stringify(rest, null, 2) } return "" @@ -776,7 +776,7 @@ const Chat = forwardRef((props, ref) => { role: 'assistant', applyStatus: ApplyStatus.Idle, isToolResult: true, - content: `${result.returnMsg.promptContent}`, + content: `${typeof result.returnMsg.promptContent === 'string' ? result.returnMsg.promptContent : ''}`, reasoningContent: '', metadata: { usage: undefined, @@ -1037,7 +1037,7 @@ const Chat = forwardRef((props, ref) => { // If the chat is empty, show a message to start a new chat chatMessages.length === 0 && (
- + setTab(tab)} />
) } diff --git a/src/components/chat-view/HelloInfo.tsx b/src/components/chat-view/HelloInfo.tsx new file mode 100644 index 0000000..46fa705 --- /dev/null +++ b/src/components/chat-view/HelloInfo.tsx @@ -0,0 +1,144 @@ +import { NotebookPen, Server, SquareSlash } from 'lucide-react'; +import React from 'react'; + +import { t } from '../../lang/helpers'; + +interface HelloInfoProps { + onNavigate: (tab: 'commands' | 'custom-mode' | 'mcp') => void; +} + +const HelloInfo: React.FC = ({ onNavigate }) => { + const navigationItems = [ + { + label: t('chat.navigation.commands'), + description: t('chat.navigation.commandsDesc'), + icon: , + action: () => onNavigate('commands'), + }, + { + label: t('chat.navigation.customMode'), + description: t('chat.navigation.customModeDesc'), + icon: , + action: () => onNavigate('custom-mode'), + }, + { + label: t('chat.navigation.mcp'), + description: t('chat.navigation.mcpDesc'), + icon: , + action: () => onNavigate('mcp'), + } + ]; + + return ( +
+ {/*
+

{t('chat.welcome.title')}

+

{t('chat.welcome.subtitle')}

+
*/} +
+ {navigationItems.map((item, index) => ( + +
+ {item.icon} +
+
+
{item.label}
+
{item.description}
+
+
+ ))} +
+ +
+ ); +}; + +export default HelloInfo; diff --git a/src/components/chat-view/McpHubView.tsx b/src/components/chat-view/McpHubView.tsx index cc19596..b5c6209 100644 --- a/src/components/chat-view/McpHubView.tsx +++ b/src/components/chat-view/McpHubView.tsx @@ -1,4 +1,5 @@ import { AlertTriangle, ChevronDown, ChevronRight, FileText, Folder, Power, RotateCcw, Trash2, Wrench } from 'lucide-react' +import { Notice } from 'obsidian' import React, { useEffect, useState } from 'react' import { useMcpHub } from '../../contexts/McpHubContext' @@ -13,6 +14,11 @@ const McpHubView = () => { const [expandedServers, setExpandedServers] = useState>({}); const [activeServerDetailTab, setActiveServerDetailTab] = useState>({}); + // 新增状态变量用于创建新服务器 + const [newServerName, setNewServerName] = useState('') + const [newServerConfig, setNewServerConfig] = useState('') + const [isCreateSectionExpanded, setIsCreateSectionExpanded] = useState(false) + const fetchServers = async () => { const hub = await getMcpHub() console.log('Fetching MCP Servers from hub:', hub) @@ -67,6 +73,42 @@ const McpHubView = () => { } } + const handleCreate = async () => { + // 验证输入 + if (newServerName.trim().length === 0) { + new Notice("服务器名称不能为空") + return + } + + if (newServerConfig.trim().length === 0) { + new Notice("配置不能为空") + return + } + + // check config is valid json + try { + JSON.parse(newServerConfig) + } catch (error) { + new Notice("配置格式无效,请输入有效的 JSON 格式") + return + } + + const hub = await getMcpHub(); + if (hub) { + try { + await hub.createServer(newServerName, newServerConfig, "global") + const updatedServers = hub.getAllServers() + setMcpServers(updatedServers) + + // 清空表单 + setNewServerName('') + setNewServerConfig('') + new Notice(`服务器 "${newServerName}" 创建成功`) + } catch (error) { + new Notice(`创建服务器失败: ${error.message}`) + } + } + } const toggleServerExpansion = (serverKey: string) => { setExpandedServers(prev => ({ ...prev, [serverKey]: !prev[serverKey] })); @@ -79,6 +121,10 @@ const McpHubView = () => { setActiveServerDetailTab(prev => ({ ...prev, [serverKey]: tab })); }; + const toggleCreateSectionExpansion = () => { + setIsCreateSectionExpanded(prev => !prev) + } + const ToolRow = ({ tool }: { tool: McpTool }) => { return (
@@ -168,11 +214,66 @@ const McpHubView = () => { 启用 MCP 服务器

- 开启后 Roo 可用已连接 MCP 服务器的工具,能力更强。不用这些工具时建议关闭,节省 API Token 费用。 + 开启后可用已连接 MCP 服务器的工具,能力更强。不用这些工具时建议关闭,节省 API Token 费用。 + + Learn more about MCP +

+ {/* Create New Server Section */} + {settings.mcpEnabled && ( +
+
+
+
+
+ {isCreateSectionExpanded ? : } +
+

+ 添加新的 MCP 服务器

+
+
+ + {isCreateSectionExpanded && ( +
+
服务器名称
+ setNewServerName(e.target.value)} + placeholder="输入服务器名称" + className="infio-mcp-create-input" + /> +
配置 (JSON 格式)
+