feat(mcp): add collapsible additional info section in form modal
Improves UX by collapsing optional metadata fields (description, tags, homepage, docs) behind an expandable "Additional Info" section. Changes: - Add collapsible section with ChevronUp/ChevronDown icons - Smart default state: collapsed for new entries, expanded for existing entries with metadata - Add i18n keys: mcp.form.additionalInfo (zh: "附加信息", en: "Additional Info") - Keep core fields (ID, name, config) always visible for better focus Benefits: - Cleaner, less cluttered form interface - Users can focus on essential configuration first - Optional metadata easily accessible when needed
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { X, Save, AlertCircle } from "lucide-react";
|
import { X, Save, AlertCircle, ChevronDown, ChevronUp } from "lucide-react";
|
||||||
import { McpServer, McpServerSpec } from "../../types";
|
import { McpServer, McpServerSpec } from "../../types";
|
||||||
import { mcpPresets } from "../../config/mcpPresets";
|
import { mcpPresets } from "../../config/mcpPresets";
|
||||||
import { buttonStyles, inputStyles } from "../../lib/styles";
|
import { buttonStyles, inputStyles } from "../../lib/styles";
|
||||||
@@ -80,6 +80,22 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
const [formDocs, setFormDocs] = useState(initialData?.docs || "");
|
const [formDocs, setFormDocs] = useState(initialData?.docs || "");
|
||||||
const [formTags, setFormTags] = useState(initialData?.tags?.join(", ") || "");
|
const [formTags, setFormTags] = useState(initialData?.tags?.join(", ") || "");
|
||||||
|
|
||||||
|
// 编辑模式下禁止修改 ID
|
||||||
|
const isEditing = !!editingId;
|
||||||
|
|
||||||
|
// 判断是否在编辑模式下有附加信息
|
||||||
|
const hasAdditionalInfo = !!(
|
||||||
|
initialData?.description ||
|
||||||
|
initialData?.tags?.length ||
|
||||||
|
initialData?.homepage ||
|
||||||
|
initialData?.docs
|
||||||
|
);
|
||||||
|
|
||||||
|
// 附加信息展开状态(编辑模式下有值时默认展开)
|
||||||
|
const [showMetadata, setShowMetadata] = useState(
|
||||||
|
isEditing ? hasAdditionalInfo : false,
|
||||||
|
);
|
||||||
|
|
||||||
// 根据 appType 决定初始格式
|
// 根据 appType 决定初始格式
|
||||||
const [formConfig, setFormConfig] = useState(() => {
|
const [formConfig, setFormConfig] = useState(() => {
|
||||||
const spec = initialData?.server;
|
const spec = initialData?.server;
|
||||||
@@ -95,9 +111,6 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
const [isWizardOpen, setIsWizardOpen] = useState(false);
|
const [isWizardOpen, setIsWizardOpen] = useState(false);
|
||||||
const [idError, setIdError] = useState("");
|
const [idError, setIdError] = useState("");
|
||||||
|
|
||||||
// 编辑模式下禁止修改 ID
|
|
||||||
const isEditing = !!editingId;
|
|
||||||
|
|
||||||
// 判断是否使用 TOML 格式
|
// 判断是否使用 TOML 格式
|
||||||
const useToml = appType === "codex";
|
const useToml = appType === "codex";
|
||||||
|
|
||||||
@@ -503,57 +516,78 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description (描述) */}
|
{/* 可折叠的附加信息按钮 */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<button
|
||||||
{t("mcp.form.description")}
|
type="button"
|
||||||
</label>
|
onClick={() => setShowMetadata(!showMetadata)}
|
||||||
<input
|
className="flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
||||||
className={inputStyles.text}
|
>
|
||||||
placeholder={t("mcp.form.descriptionPlaceholder")}
|
{showMetadata ? (
|
||||||
value={formDescription}
|
<ChevronUp size={16} />
|
||||||
onChange={(e) => setFormDescription(e.target.value)}
|
) : (
|
||||||
/>
|
<ChevronDown size={16} />
|
||||||
|
)}
|
||||||
|
{t("mcp.form.additionalInfo")}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tags */}
|
{/* 附加信息区域(可折叠) */}
|
||||||
<div>
|
{showMetadata && (
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<>
|
||||||
{t("mcp.form.tags")}
|
{/* Description (描述) */}
|
||||||
</label>
|
<div>
|
||||||
<input
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
className={inputStyles.text}
|
{t("mcp.form.description")}
|
||||||
placeholder={t("mcp.form.tagsPlaceholder")}
|
</label>
|
||||||
value={formTags}
|
<input
|
||||||
onChange={(e) => setFormTags(e.target.value)}
|
className={inputStyles.text}
|
||||||
/>
|
placeholder={t("mcp.form.descriptionPlaceholder")}
|
||||||
</div>
|
value={formDescription}
|
||||||
|
onChange={(e) => setFormDescription(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Homepage */}
|
{/* Tags */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
{t("mcp.form.homepage")}
|
{t("mcp.form.tags")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className={inputStyles.text}
|
className={inputStyles.text}
|
||||||
placeholder={t("mcp.form.homepagePlaceholder")}
|
placeholder={t("mcp.form.tagsPlaceholder")}
|
||||||
value={formHomepage}
|
value={formTags}
|
||||||
onChange={(e) => setFormHomepage(e.target.value)}
|
onChange={(e) => setFormTags(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Docs */}
|
{/* Homepage */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
{t("mcp.form.docs")}
|
{t("mcp.form.homepage")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className={inputStyles.text}
|
className={inputStyles.text}
|
||||||
placeholder={t("mcp.form.docsPlaceholder")}
|
placeholder={t("mcp.form.homepagePlaceholder")}
|
||||||
value={formDocs}
|
value={formHomepage}
|
||||||
onChange={(e) => setFormDocs(e.target.value)}
|
onChange={(e) => setFormHomepage(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Docs */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
|
{t("mcp.form.docs")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className={inputStyles.text}
|
||||||
|
placeholder={t("mcp.form.docsPlaceholder")}
|
||||||
|
value={formDocs}
|
||||||
|
onChange={(e) => setFormDocs(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 配置输入框(根据格式显示 JSON 或 TOML) */}
|
{/* 配置输入框(根据格式显示 JSON 或 TOML) */}
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -291,6 +291,7 @@
|
|||||||
"homepagePlaceholder": "https://example.com",
|
"homepagePlaceholder": "https://example.com",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"docsPlaceholder": "https://example.com/docs",
|
"docsPlaceholder": "https://example.com/docs",
|
||||||
|
"additionalInfo": "Additional Info",
|
||||||
"jsonConfig": "JSON Configuration",
|
"jsonConfig": "JSON Configuration",
|
||||||
"jsonPlaceholder": "{\n \"type\": \"stdio\",\n \"command\": \"uvx\",\n \"args\": [\"mcp-server-fetch\"]\n}",
|
"jsonPlaceholder": "{\n \"type\": \"stdio\",\n \"command\": \"uvx\",\n \"args\": [\"mcp-server-fetch\"]\n}",
|
||||||
"tomlConfig": "TOML Configuration",
|
"tomlConfig": "TOML Configuration",
|
||||||
|
|||||||
@@ -291,6 +291,7 @@
|
|||||||
"homepagePlaceholder": "https://example.com",
|
"homepagePlaceholder": "https://example.com",
|
||||||
"docs": "文档链接",
|
"docs": "文档链接",
|
||||||
"docsPlaceholder": "https://example.com/docs",
|
"docsPlaceholder": "https://example.com/docs",
|
||||||
|
"additionalInfo": "附加信息",
|
||||||
"jsonConfig": "JSON 配置",
|
"jsonConfig": "JSON 配置",
|
||||||
"jsonPlaceholder": "{\n \"type\": \"stdio\",\n \"command\": \"uvx\",\n \"args\": [\"mcp-server-fetch\"]\n}",
|
"jsonPlaceholder": "{\n \"type\": \"stdio\",\n \"command\": \"uvx\",\n \"args\": [\"mcp-server-fetch\"]\n}",
|
||||||
"tomlConfig": "TOML 配置",
|
"tomlConfig": "TOML 配置",
|
||||||
|
|||||||
Reference in New Issue
Block a user