diff --git a/eslint.config.mjs b/eslint.config.mjs index 9eb20d1238..3253e4cc82 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -48,7 +48,8 @@ export default defineConfig([ '@eslint-react/no-unstable-context-value': 'off', '@eslint-react/hooks-extra/prefer-use-state-lazy-initialization': 'off', '@eslint-react/hooks-extra/no-unnecessary-use-prefix': 'off', - '@eslint-react/no-children-to-array': 'off' + '@eslint-react/no-children-to-array': 'off', + '@eslint-react/dom/no-unsafe-iframe-sandbox': 'off' } }, { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index f4012363e3..f6b86a5c86 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Available Tools", "enable": "Enable Tool", + "execute": { + "button": "Execute", + "copied": "Copied to clipboard", + "copy": "Copy", + "copyFailed": "Failed to copy", + "error": { + "invalidJson": "Invalid JSON format: {{error}}", + "noToolOrServer": "Tool or server not found" + }, + "execute": "Execute", + "label": "Execute", + "params": "Parameters (JSON)", + "paramsPlaceholder": "Enter JSON parameters...", + "result": { + "error": "Error Result", + "success": "Result", + "text": "Text" + }, + "table": { + "name": "Name", + "value": "Value" + }, + "title": "Execute Tool: {{name}}", + "tooltip": "Execute tool with custom parameters", + "view": { + "formatted": "Formatted", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Allowed Values" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 0e5b2f60e7..b5de19669b 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -4114,6 +4114,35 @@ }, "availableTools": "可用工具", "enable": "启用工具", + "execute": { + "button": "执行", + "copied": "已复制到剪贴板", + "copy": "复制", + "copyFailed": "复制失败", + "error": { + "invalidJson": "无效的 JSON 格式: {{error}}", + "noToolOrServer": "未找到工具或服务器" + }, + "execute": "执行", + "label": "执行", + "params": "参数 (JSON)", + "paramsPlaceholder": "输入 JSON 参数...", + "result": { + "error": "错误结果", + "success": "结果", + "text": "文本" + }, + "table": { + "name": "名称", + "value": "值" + }, + "title": "执行工具: {{name}}", + "tooltip": "使用自定义参数执行工具", + "view": { + "formatted": "美化", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "允许的值" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 9625c68386..4f425bd0ec 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -4114,6 +4114,35 @@ }, "availableTools": "可用工具", "enable": "啟用工具", + "execute": { + "button": "執行", + "copied": "已複製到剪貼板", + "copy": "複製", + "copyFailed": "複製失敗", + "error": { + "invalidJson": "無效的 JSON 格式: {{error}}", + "noToolOrServer": "未找到工具或伺服器" + }, + "execute": "執行", + "label": "執行", + "params": "參數 (JSON)", + "paramsPlaceholder": "輸入 JSON 參數...", + "result": { + "error": "錯誤結果", + "success": "結果", + "text": "文字" + }, + "table": { + "name": "名稱", + "value": "值" + }, + "title": "執行工具: {{name}}", + "tooltip": "使用自訂參數執行工具", + "view": { + "formatted": "美化", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "允許的值" diff --git a/src/renderer/src/i18n/translate/de-de.json b/src/renderer/src/i18n/translate/de-de.json index b3acb49950..e5c9e3d454 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Verfügbare Tools", "enable": "Tool aktivieren", + "execute": { + "button": "Ausführen", + "copied": "In Zwischenablage kopiert", + "copy": "Kopieren", + "copyFailed": "Kopieren fehlgeschlagen", + "error": { + "invalidJson": "Ungültiges JSON-Format: {{error}}", + "noToolOrServer": "Tool oder Server nicht gefunden" + }, + "execute": "Ausführen", + "label": "Ausführen", + "params": "Parameter (JSON)", + "paramsPlaceholder": "JSON-Parameter eingeben...", + "result": { + "error": "Fehlerergebnis", + "success": "Ergebnis", + "text": "Text" + }, + "table": { + "name": "Name", + "value": "Wert" + }, + "title": "Tool ausführen: {{name}}", + "tooltip": "Tool mit benutzerdefinierten Parametern ausführen", + "view": { + "formatted": "Formatiert", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Erlaubte Werte" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index ae7b855646..0a58a006ab 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Διαθέσιμα Εργαλεία", "enable": "Ενεργοποίηση εργαλείου", + "execute": { + "button": "Εκτέλεση", + "copied": "Αντιγράφηκε στο πρόχειρο", + "copy": "Αντιγραφή", + "copyFailed": "Αποτυχία αντιγραφής", + "error": { + "invalidJson": "Μη έγκυρη μορφή JSON: {{error}}", + "noToolOrServer": "Εργαλείο ή διακομιστής δεν βρέθηκε" + }, + "execute": "Εκτέλεση", + "label": "Εκτέλεση", + "params": "Παράμετροι (JSON)", + "paramsPlaceholder": "Εισαγάγετε παραμέτρους JSON...", + "result": { + "error": "Αποτέλεσμα σφάλματος", + "success": "Αποτέλεσμα", + "text": "Κείμενο" + }, + "table": { + "name": "Όνομα", + "value": "Τιμή" + }, + "title": "Εκτέλεση εργαλείου: {{name}}", + "tooltip": "Εκτέλεση εργαλείου με προσαρμοσμένες παραμέτρους", + "view": { + "formatted": "Μορφοποιημένο", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Επιτρεπόμενες τιμές" diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 26b499cba2..f6982dfec1 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Herramientas disponibles", "enable": "Habilitar herramienta", + "execute": { + "button": "Ejecutar", + "copied": "Copiado al portapapeles", + "copy": "Copiar", + "copyFailed": "Error al copiar", + "error": { + "invalidJson": "Formato JSON inválido: {{error}}", + "noToolOrServer": "Herramienta o servidor no encontrado" + }, + "execute": "Ejecutar", + "label": "Ejecutar", + "params": "Parámetros (JSON)", + "paramsPlaceholder": "Ingrese parámetros JSON...", + "result": { + "error": "Resultado de error", + "success": "Resultado", + "text": "Texto" + }, + "table": { + "name": "Nombre", + "value": "Valor" + }, + "title": "Ejecutar herramienta: {{name}}", + "tooltip": "Ejecutar herramienta con parámetros personalizados", + "view": { + "formatted": "Formateado", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Valores permitidos" diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 4dff56d7e9..a26c83a276 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Outils disponibles", "enable": "Activer l'outil", + "execute": { + "button": "Exécuter", + "copied": "Copié dans le presse-papiers", + "copy": "Copier", + "copyFailed": "Échec de la copie", + "error": { + "invalidJson": "Format JSON invalide: {{error}}", + "noToolOrServer": "Outil ou serveur introuvable" + }, + "execute": "Exécuter", + "label": "Exécuter", + "params": "Paramètres (JSON)", + "paramsPlaceholder": "Entrez les paramètres JSON...", + "result": { + "error": "Résultat d'erreur", + "success": "Résultat", + "text": "Texte" + }, + "table": { + "name": "Nom", + "value": "Valeur" + }, + "title": "Exécuter l'outil: {{name}}", + "tooltip": "Exécuter l'outil avec des paramètres personnalisés", + "view": { + "formatted": "Formaté", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Valeurs autorisées" diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 090a1927cd..871f5fd8bb 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -4114,6 +4114,35 @@ }, "availableTools": "利用可能なツール", "enable": "ツールを有効にする", + "execute": { + "button": "実行", + "copied": "クリップボードにコピーしました", + "copy": "コピー", + "copyFailed": "コピーに失敗しました", + "error": { + "invalidJson": "無効なJSON形式: {{error}}", + "noToolOrServer": "ツールまたはサーバーが見つかりません" + }, + "execute": "実行", + "label": "実行", + "params": "パラメータ (JSON)", + "paramsPlaceholder": "JSONパラメータを入力...", + "result": { + "error": "エラー結果", + "success": "結果", + "text": "テキスト" + }, + "table": { + "name": "名前", + "value": "値" + }, + "title": "ツールを実行: {{name}}", + "tooltip": "カスタムパラメータでツールを実行", + "view": { + "formatted": "フォーマット済み", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "許可された値" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 50cc4fae03..c800152212 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Ferramentas Disponíveis", "enable": "Habilitar Ferramenta", + "execute": { + "button": "Executar", + "copied": "Copiado para a área de transferência", + "copy": "Copiar", + "copyFailed": "Falha ao copiar", + "error": { + "invalidJson": "Formato JSON inválido: {{error}}", + "noToolOrServer": "Ferramenta ou servidor não encontrado" + }, + "execute": "Executar", + "label": "Executar", + "params": "Parâmetros (JSON)", + "paramsPlaceholder": "Digite os parâmetros JSON...", + "result": { + "error": "Resultado de erro", + "success": "Resultado", + "text": "Texto" + }, + "table": { + "name": "Nome", + "value": "Valor" + }, + "title": "Executar ferramenta: {{name}}", + "tooltip": "Executar ferramenta com parâmetros personalizados", + "view": { + "formatted": "Formatado", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Valores permitidos" diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 8a6a781451..c97147ae27 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -4114,6 +4114,35 @@ }, "availableTools": "Доступные инструменты", "enable": "Включить инструмент", + "execute": { + "button": "Выполнить", + "copied": "Скопировано в буфер обмена", + "copy": "Копировать", + "copyFailed": "Не удалось скопировать", + "error": { + "invalidJson": "Неверный формат JSON: {{error}}", + "noToolOrServer": "Инструмент или сервер не найден" + }, + "execute": "Выполнить", + "label": "Выполнить", + "params": "Параметры (JSON)", + "paramsPlaceholder": "Введите параметры JSON...", + "result": { + "error": "Результат ошибки", + "success": "Результат", + "text": "Текст" + }, + "table": { + "name": "Имя", + "value": "Значение" + }, + "title": "Выполнить инструмент: {{name}}", + "tooltip": "Выполнить инструмент с пользовательскими параметрами", + "view": { + "formatted": "Форматированный", + "json": "JSON" + } + }, "inputSchema": { "enum": { "allowedValues": "Допустимые значения" diff --git a/src/renderer/src/pages/settings/MCPSettings/ExecuteToolModal.tsx b/src/renderer/src/pages/settings/MCPSettings/ExecuteToolModal.tsx new file mode 100644 index 0000000000..8dd1236a19 --- /dev/null +++ b/src/renderer/src/pages/settings/MCPSettings/ExecuteToolModal.tsx @@ -0,0 +1,442 @@ +import 'katex/dist/katex.min.css' + +import { loggerService } from '@logger' +import type { MCPServer, MCPTool } from '@renderer/types' +import { Button, Flex, Input, message, Modal, Space, Table, Typography } from 'antd' +import type { ColumnsType } from 'antd/es/table' +import { Code as CodeIcon, Copy, Play, Sparkles } from 'lucide-react' +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import ReactMarkdown from 'react-markdown' +import rehypeKatex from 'rehype-katex' +import rehypeRaw from 'rehype-raw' +import remarkCjkFriendly from 'remark-cjk-friendly' +import remarkGfm from 'remark-gfm' +import remarkMath from 'remark-math' + +const logger = loggerService.withContext('ExecuteToolModal') + +// HTML 文档结构检测模式(在模块级别定义,避免重复创建) +const HTML_DOCUMENT_PATTERNS = [/]/i, /
]/i, /]/i] + +// Markdown 检测模式(在模块级别定义,避免重复创建) +const MARKDOWN_PATTERNS = [ + /^#{1,6}\s+.+$/m, // 标题 + /^\s*[-*+]\s+.+$/m, // 列表 + /^\s*\d+\.\s+.+$/m, // 有序列表 + /\[.+\]\(.+\)/, // 链接 + /!\[.+\]\(.+\)/, // 图片 + /```[\s\S]*?```/, // 代码块 + /`[^`]+`/, // 行内代码 + /\*\*.*?\*\*/, // 粗体 + /\*.*?\*/, // 斜体 + /^>\s+.+$/m // 引用 +] + +/** + * 检测文本内容类型 + * @param text 要检测的文本 + * @returns 内容类型:'json' | 'markdown' | 'html' | 'text' + */ +function detectContentType(text: string): 'json' | 'markdown' | 'html' | 'text' { + if (!text) return 'text' + + // 检测 HTML 特征(优先检测,因为 HTML 可能包含其他格式) + // 检查是否包含完整的 HTML 文档结构或大量 HTML 标签 + const hasHtmlDocument = HTML_DOCUMENT_PATTERNS.some((pattern) => pattern.test(text)) + + // 检查 HTML 标签数量 + // 注意:每次调用时创建新的正则表达式实例,避免全局标志的状态污染 + const htmlTags = text.match(/<[a-z][a-z0-9]*[\s>]/gi) + const htmlTagCount = htmlTags ? htmlTags.length : 0 + + // 如果包含 HTML 文档结构,或者有多个 HTML 标签,认为是 HTML + if (hasHtmlDocument || htmlTagCount >= 3) { + return 'html' + } + + // 尝试解析为 JSON + try { + const parsed = JSON.parse(text) + if (typeof parsed === 'object' && parsed !== null) { + return 'json' + } + } catch { + // 不是 JSON + } + + // 检测 Markdown 特征 + const hasMarkdown = MARKDOWN_PATTERNS.some((pattern) => pattern.test(text)) + if (hasMarkdown) { + return 'markdown' + } + + return 'text' +} + +interface ExecuteToolModalProps { + open: boolean + tool: MCPTool | null + server: MCPServer | null + onClose: () => void +} + +interface TableData { + key: string + name: string + value: any +} + +const ExecuteToolModal: React.FC