2025-10-13 23:37:33 +08:00
|
|
|
|
import React, { useEffect, useState } from "react";
|
2025-10-09 11:30:28 +08:00
|
|
|
|
import { useTranslation } from "react-i18next";
|
2025-10-17 17:49:16 +08:00
|
|
|
|
import { toast } from "sonner";
|
2025-10-16 16:20:45 +08:00
|
|
|
|
import { Save } from "lucide-react";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Dialog,
|
|
|
|
|
|
DialogContent,
|
|
|
|
|
|
DialogHeader,
|
|
|
|
|
|
DialogTitle,
|
|
|
|
|
|
DialogFooter,
|
|
|
|
|
|
} from "@/components/ui/dialog";
|
2025-10-17 16:35:12 +08:00
|
|
|
|
import { McpServerSpec } from "@/types";
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
|
|
|
|
|
interface McpWizardModalProps {
|
|
|
|
|
|
isOpen: boolean;
|
|
|
|
|
|
onClose: () => void;
|
2025-10-11 11:43:32 +08:00
|
|
|
|
onApply: (title: string, json: string) => void;
|
2025-10-13 23:37:33 +08:00
|
|
|
|
initialTitle?: string;
|
|
|
|
|
|
initialServer?: McpServerSpec;
|
2025-10-09 11:30:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析环境变量文本为对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
const parseEnvText = (text: string): Record<string, string> => {
|
|
|
|
|
|
const lines = text
|
|
|
|
|
|
.split("\n")
|
|
|
|
|
|
.map((l) => l.trim())
|
|
|
|
|
|
.filter((l) => l.length > 0);
|
|
|
|
|
|
const env: Record<string, string> = {};
|
|
|
|
|
|
for (const l of lines) {
|
|
|
|
|
|
const idx = l.indexOf("=");
|
|
|
|
|
|
if (idx > 0) {
|
|
|
|
|
|
const k = l.slice(0, idx).trim();
|
|
|
|
|
|
const v = l.slice(idx + 1).trim();
|
|
|
|
|
|
if (k) env[k] = v;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return env;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 解析headers文本为对象(支持 KEY: VALUE 或 KEY=VALUE)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const parseHeadersText = (text: string): Record<string, string> => {
|
|
|
|
|
|
const lines = text
|
|
|
|
|
|
.split("\n")
|
|
|
|
|
|
.map((l) => l.trim())
|
|
|
|
|
|
.filter((l) => l.length > 0);
|
|
|
|
|
|
const headers: Record<string, string> = {};
|
|
|
|
|
|
for (const l of lines) {
|
|
|
|
|
|
// 支持 KEY: VALUE 或 KEY=VALUE
|
|
|
|
|
|
const colonIdx = l.indexOf(":");
|
|
|
|
|
|
const equalIdx = l.indexOf("=");
|
|
|
|
|
|
let idx = -1;
|
|
|
|
|
|
if (colonIdx > 0 && (equalIdx === -1 || colonIdx < equalIdx)) {
|
|
|
|
|
|
idx = colonIdx;
|
|
|
|
|
|
} else if (equalIdx > 0) {
|
|
|
|
|
|
idx = equalIdx;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (idx > 0) {
|
|
|
|
|
|
const k = l.slice(0, idx).trim();
|
|
|
|
|
|
const v = l.slice(idx + 1).trim();
|
|
|
|
|
|
if (k) headers[k] = v;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return headers;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-09 11:30:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* MCP 配置向导模态框
|
|
|
|
|
|
* 帮助用户快速生成 MCP JSON 配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
const McpWizardModal: React.FC<McpWizardModalProps> = ({
|
|
|
|
|
|
isOpen,
|
|
|
|
|
|
onClose,
|
|
|
|
|
|
onApply,
|
2025-10-13 23:37:33 +08:00
|
|
|
|
initialTitle,
|
|
|
|
|
|
initialServer,
|
2025-10-09 11:30:28 +08:00
|
|
|
|
}) => {
|
|
|
|
|
|
const { t } = useTranslation();
|
feat: add model configuration support and fix Gemini deeplink bug (#251)
* feat(providers): add notes field for provider management
- Add notes field to Provider model (backend and frontend)
- Display notes with higher priority than URL in provider card
- Style notes as non-clickable text to differentiate from URLs
- Add notes input field in provider form
- Add i18n support (zh/en) for notes field
* chore: format code and clean up unused props
- Run cargo fmt on Rust backend code
- Format TypeScript imports and code style
- Remove unused appId prop from ProviderPresetSelector
- Clean up unused variables in tests
- Integrate notes field handling in provider dialogs
* feat(deeplink): implement ccswitch:// protocol for provider import
Add deep link support to enable one-click provider configuration import via ccswitch:// URLs.
Backend:
- Implement URL parsing and validation (src-tauri/src/deeplink.rs)
- Add Tauri commands for parse and import (src-tauri/src/commands/deeplink.rs)
- Register ccswitch:// protocol in macOS Info.plist
- Add comprehensive unit tests (src-tauri/tests/deeplink_import.rs)
Frontend:
- Create confirmation dialog with security review UI (src/components/DeepLinkImportDialog.tsx)
- Add API wrapper (src/lib/api/deeplink.ts)
- Integrate event listeners in App.tsx
Configuration:
- Update Tauri config for deep link handling
- Add i18n support for Chinese and English
- Include test page for deep link validation (deeplink-test.html)
Files: 15 changed, 1312 insertions(+)
* chore(deeplink): integrate deep link handling into app lifecycle
Wire up deep link infrastructure with app initialization and event handling.
Backend Integration:
- Register deep link module and commands in mod.rs
- Add URL handling in app setup (src-tauri/src/lib.rs:handle_deeplink_url)
- Handle deep links from single instance callback (Windows/Linux CLI)
- Handle deep links from macOS system events
- Add tauri-plugin-deep-link dependency (Cargo.toml)
Frontend Integration:
- Listen for deeplink-import/deeplink-error events in App.tsx
- Update DeepLinkImportDialog component imports
Configuration:
- Enable deep link plugin in tauri.conf.json
- Update Cargo.lock for new dependencies
Localization:
- Add Chinese translations for deep link UI (zh.json)
- Add English translations for deep link UI (en.json)
Files: 9 changed, 359 insertions(+), 18 deletions(-)
* refactor(deeplink): enhance Codex provider template generation
Align deep link import with UI preset generation logic by:
- Adding complete config.toml template matching frontend defaults
- Generating safe provider name from sanitized input
- Including model_provider, reasoning_effort, and wire_api settings
- Removing minimal template that only contained base_url
- Cleaning up deprecated test file deeplink-test.html
* style: fix clippy uninlined_format_args warnings
Apply clippy --fix to use inline format arguments in:
- src/mcp.rs (8 fixes)
- src/services/env_manager.rs (10 fixes)
* style: apply code formatting and cleanup
- Format TypeScript files with Prettier (App.tsx, EnvWarningBanner.tsx, formatters.ts)
- Organize Rust imports and module order alphabetically
- Add newline at end of JSON files (en.json, zh.json)
- Update Cargo.lock for dependency changes
* feat: add model name configuration support for Codex and fix Gemini model handling
- Add visual model name input field for Codex providers
- Add model name extraction and update utilities in providerConfigUtils
- Implement model name state management in useCodexConfigState hook
- Add conditional model field rendering in CodexFormFields (non-official only)
- Integrate model name sync with TOML config in ProviderForm
- Fix Gemini deeplink model injection bug
- Correct environment variable name from GOOGLE_GEMINI_MODEL to GEMINI_MODEL
- Add test cases for Gemini model injection (with/without model)
- All tests passing (9/9)
- Fix Gemini model field binding in edit mode
- Add geminiModel state to useGeminiConfigState hook
- Extract model value during initialization and reset
- Sync model field with geminiEnv state to prevent data loss on submit
- Fix missing model value display when editing Gemini providers
Changes:
- 6 files changed, 245 insertions(+), 13 deletions(-)
2025-11-19 09:03:18 +08:00
|
|
|
|
const [wizardType, setWizardType] = useState<"stdio" | "http" | "sse">(
|
|
|
|
|
|
"stdio",
|
|
|
|
|
|
);
|
2025-10-11 11:43:32 +08:00
|
|
|
|
const [wizardTitle, setWizardTitle] = useState("");
|
2025-10-09 12:04:37 +08:00
|
|
|
|
// stdio 字段
|
2025-10-09 11:30:28 +08:00
|
|
|
|
const [wizardCommand, setWizardCommand] = useState("");
|
|
|
|
|
|
const [wizardArgs, setWizardArgs] = useState("");
|
|
|
|
|
|
const [wizardEnv, setWizardEnv] = useState("");
|
2025-11-16 16:15:17 +08:00
|
|
|
|
// http 和 sse 字段
|
2025-10-09 12:04:37 +08:00
|
|
|
|
const [wizardUrl, setWizardUrl] = useState("");
|
|
|
|
|
|
const [wizardHeaders, setWizardHeaders] = useState("");
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 生成预览 JSON
|
|
|
|
|
|
const generatePreview = (): string => {
|
2025-10-12 00:08:37 +08:00
|
|
|
|
const config: McpServerSpec = {
|
2025-10-09 11:30:28 +08:00
|
|
|
|
type: wizardType,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
if (wizardType === "stdio") {
|
|
|
|
|
|
// stdio 类型必需字段
|
|
|
|
|
|
config.command = wizardCommand.trim();
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
// 可选字段
|
|
|
|
|
|
if (wizardArgs.trim()) {
|
|
|
|
|
|
config.args = wizardArgs
|
|
|
|
|
|
.split("\n")
|
|
|
|
|
|
.map((s) => s.trim())
|
|
|
|
|
|
.filter((s) => s.length > 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (wizardEnv.trim()) {
|
|
|
|
|
|
const env = parseEnvText(wizardEnv);
|
|
|
|
|
|
if (Object.keys(env).length > 0) {
|
|
|
|
|
|
config.env = env;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-11-16 16:15:17 +08:00
|
|
|
|
// http 和 sse 类型必需字段
|
2025-10-09 12:04:37 +08:00
|
|
|
|
config.url = wizardUrl.trim();
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
// 可选字段
|
|
|
|
|
|
if (wizardHeaders.trim()) {
|
|
|
|
|
|
const headers = parseHeadersText(wizardHeaders);
|
|
|
|
|
|
if (Object.keys(headers).length > 0) {
|
|
|
|
|
|
config.headers = headers;
|
|
|
|
|
|
}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return JSON.stringify(config, null, 2);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleApply = () => {
|
2025-10-11 11:43:32 +08:00
|
|
|
|
if (!wizardTitle.trim()) {
|
2025-10-17 17:49:16 +08:00
|
|
|
|
toast.error(t("mcp.error.idRequired"), { duration: 3000 });
|
2025-10-11 11:43:32 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-09 12:04:37 +08:00
|
|
|
|
if (wizardType === "stdio" && !wizardCommand.trim()) {
|
2025-10-17 17:49:16 +08:00
|
|
|
|
toast.error(t("mcp.error.commandRequired"), { duration: 3000 });
|
2025-10-09 11:30:28 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-16 16:15:17 +08:00
|
|
|
|
if ((wizardType === "http" || wizardType === "sse") && !wizardUrl.trim()) {
|
2025-10-17 17:49:16 +08:00
|
|
|
|
toast.error(t("mcp.wizard.urlRequired"), { duration: 3000 });
|
2025-10-09 12:04:37 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
|
|
|
|
|
const json = generatePreview();
|
2025-10-11 11:43:32 +08:00
|
|
|
|
onApply(wizardTitle.trim(), json);
|
2025-10-09 11:30:28 +08:00
|
|
|
|
handleClose();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
setWizardType("stdio");
|
2025-10-11 11:43:32 +08:00
|
|
|
|
setWizardTitle("");
|
2025-10-09 11:30:28 +08:00
|
|
|
|
setWizardCommand("");
|
|
|
|
|
|
setWizardArgs("");
|
|
|
|
|
|
setWizardEnv("");
|
2025-10-09 12:04:37 +08:00
|
|
|
|
setWizardUrl("");
|
|
|
|
|
|
setWizardHeaders("");
|
2025-10-09 11:30:28 +08:00
|
|
|
|
onClose();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
|
|
|
|
if (e.key === "Enter" && e.metaKey) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
handleApply();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-13 23:37:33 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!isOpen) return;
|
|
|
|
|
|
|
|
|
|
|
|
const title = initialTitle ?? "";
|
|
|
|
|
|
setWizardTitle(title);
|
|
|
|
|
|
|
|
|
|
|
|
const resolvedType =
|
2025-10-16 12:13:51 +08:00
|
|
|
|
initialServer?.type ?? (initialServer?.url ? "http" : "stdio");
|
2025-10-13 23:37:33 +08:00
|
|
|
|
|
|
|
|
|
|
setWizardType(resolvedType);
|
|
|
|
|
|
|
2025-11-16 16:15:17 +08:00
|
|
|
|
if (resolvedType === "http" || resolvedType === "sse") {
|
2025-10-13 23:37:33 +08:00
|
|
|
|
setWizardUrl(initialServer?.url ?? "");
|
|
|
|
|
|
const headersCandidate = initialServer?.headers;
|
|
|
|
|
|
const headers =
|
|
|
|
|
|
headersCandidate && typeof headersCandidate === "object"
|
|
|
|
|
|
? headersCandidate
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
setWizardHeaders(
|
|
|
|
|
|
headers
|
|
|
|
|
|
? Object.entries(headers)
|
|
|
|
|
|
.map(([k, v]) => `${k}: ${v ?? ""}`)
|
|
|
|
|
|
.join("\n")
|
|
|
|
|
|
: "",
|
|
|
|
|
|
);
|
|
|
|
|
|
setWizardCommand("");
|
|
|
|
|
|
setWizardArgs("");
|
|
|
|
|
|
setWizardEnv("");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setWizardCommand(initialServer?.command ?? "");
|
|
|
|
|
|
const argsValue = initialServer?.args;
|
|
|
|
|
|
setWizardArgs(Array.isArray(argsValue) ? argsValue.join("\n") : "");
|
|
|
|
|
|
const envCandidate = initialServer?.env;
|
|
|
|
|
|
const env =
|
2025-10-16 12:13:51 +08:00
|
|
|
|
envCandidate && typeof envCandidate === "object"
|
|
|
|
|
|
? envCandidate
|
|
|
|
|
|
: undefined;
|
2025-10-13 23:37:33 +08:00
|
|
|
|
setWizardEnv(
|
|
|
|
|
|
env
|
|
|
|
|
|
? Object.entries(env)
|
|
|
|
|
|
.map(([k, v]) => `${k}=${v ?? ""}`)
|
|
|
|
|
|
.join("\n")
|
|
|
|
|
|
: "",
|
|
|
|
|
|
);
|
|
|
|
|
|
setWizardUrl("");
|
|
|
|
|
|
setWizardHeaders("");
|
|
|
|
|
|
}, [isOpen]);
|
|
|
|
|
|
|
2025-10-09 11:30:28 +08:00
|
|
|
|
const preview = generatePreview();
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2025-10-16 16:20:45 +08:00
|
|
|
|
<Dialog open={isOpen} onOpenChange={(open) => !open && handleClose()}>
|
|
|
|
|
|
<DialogContent className="max-w-2xl max-h-[90vh] flex flex-col">
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
<DialogTitle>{t("mcp.wizard.title")}</DialogTitle>
|
|
|
|
|
|
</DialogHeader>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
|
|
|
|
|
{/* Content */}
|
2025-10-18 17:16:13 +08:00
|
|
|
|
<div className="flex-1 overflow-y-auto px-6 py-4 space-y-4">
|
2025-10-09 11:30:28 +08:00
|
|
|
|
{/* Hint */}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
<div className="rounded-lg border border-border-active bg-blue-50 p-3 dark:bg-blue-900/20">
|
2025-10-09 11:30:28 +08:00
|
|
|
|
<p className="text-sm text-blue-800 dark:text-blue-200">
|
|
|
|
|
|
{t("mcp.wizard.hint")}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Form Fields */}
|
2025-10-11 10:15:40 +08:00
|
|
|
|
<div className="space-y-4 min-h-[400px]">
|
2025-10-09 11:30:28 +08:00
|
|
|
|
{/* Type */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-2 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.type")} <span className="text-red-500">*</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="flex gap-4">
|
|
|
|
|
|
<label className="inline-flex items-center gap-2 cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
value="stdio"
|
|
|
|
|
|
checked={wizardType === "stdio"}
|
|
|
|
|
|
onChange={(e) =>
|
2025-11-16 16:15:17 +08:00
|
|
|
|
setWizardType(e.target.value as "stdio" | "http" | "sse")
|
2025-10-09 11:30:28 +08:00
|
|
|
|
}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-4 h-4 text-emerald-500 bg-white dark:bg-gray-800 border-border-default focus:ring-emerald-500 dark:focus:ring-emerald-400 focus:ring-2"
|
2025-10-09 11:30:28 +08:00
|
|
|
|
/>
|
|
|
|
|
|
<span className="text-sm text-gray-900 dark:text-gray-100">
|
2025-10-11 16:20:12 +08:00
|
|
|
|
{t("mcp.wizard.typeStdio")}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label className="inline-flex items-center gap-2 cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
value="http"
|
|
|
|
|
|
checked={wizardType === "http"}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
onChange={(e) =>
|
2025-11-16 16:15:17 +08:00
|
|
|
|
setWizardType(e.target.value as "stdio" | "http" | "sse")
|
2025-10-09 11:30:28 +08:00
|
|
|
|
}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-4 h-4 text-emerald-500 bg-white dark:bg-gray-800 border-border-default focus:ring-emerald-500 dark:focus:ring-emerald-400 focus:ring-2"
|
2025-10-09 11:30:28 +08:00
|
|
|
|
/>
|
|
|
|
|
|
<span className="text-sm text-gray-900 dark:text-gray-100">
|
2025-10-11 16:20:12 +08:00
|
|
|
|
{t("mcp.wizard.typeHttp")}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
2025-11-16 16:15:17 +08:00
|
|
|
|
<label className="inline-flex items-center gap-2 cursor-pointer">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="radio"
|
|
|
|
|
|
value="sse"
|
|
|
|
|
|
checked={wizardType === "sse"}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setWizardType(e.target.value as "stdio" | "http" | "sse")
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-4 h-4 text-emerald-500 bg-white dark:bg-gray-800 border-border-default focus:ring-emerald-500 dark:focus:ring-emerald-400 focus:ring-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span className="text-sm text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.typeSse")}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-10-11 11:43:32 +08:00
|
|
|
|
{/* Title */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.form.title")} <span className="text-red-500">*</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={wizardTitle}
|
|
|
|
|
|
onChange={(e) => setWizardTitle(e.target.value)}
|
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
|
placeholder={t("mcp.form.titlePlaceholder")}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100"
|
2025-10-11 11:43:32 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
{/* Stdio 类型字段 */}
|
|
|
|
|
|
{wizardType === "stdio" && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{/* Command */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.command")}{" "}
|
|
|
|
|
|
<span className="text-red-500">*</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={wizardCommand}
|
|
|
|
|
|
onChange={(e) => setWizardCommand(e.target.value)}
|
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
|
placeholder={t("mcp.wizard.commandPlaceholder")}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
{/* Args */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.args")}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={wizardArgs}
|
|
|
|
|
|
onChange={(e) => setWizardArgs(e.target.value)}
|
|
|
|
|
|
placeholder={t("mcp.wizard.argsPlaceholder")}
|
|
|
|
|
|
rows={3}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100 resize-y"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
|
2025-10-09 12:04:37 +08:00
|
|
|
|
{/* Env */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.env")}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={wizardEnv}
|
|
|
|
|
|
onChange={(e) => setWizardEnv(e.target.value)}
|
|
|
|
|
|
placeholder={t("mcp.wizard.envPlaceholder")}
|
|
|
|
|
|
rows={3}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100 resize-y"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2025-11-16 16:15:17 +08:00
|
|
|
|
{/* HTTP 和 SSE 类型字段 */}
|
|
|
|
|
|
{(wizardType === "http" || wizardType === "sse") && (
|
2025-10-09 12:04:37 +08:00
|
|
|
|
<>
|
|
|
|
|
|
{/* URL */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.url")}{" "}
|
|
|
|
|
|
<span className="text-red-500">*</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
value={wizardUrl}
|
|
|
|
|
|
onChange={(e) => setWizardUrl(e.target.value)}
|
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
|
placeholder={t("mcp.wizard.urlPlaceholder")}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Headers */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.headers")}
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={wizardHeaders}
|
|
|
|
|
|
onChange={(e) => setWizardHeaders(e.target.value)}
|
|
|
|
|
|
placeholder={t("mcp.wizard.headersPlaceholder")}
|
|
|
|
|
|
rows={3}
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
className="w-full rounded-lg border border-border-default px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-emerald-500/20 dark:bg-gray-800 dark:text-gray-100 resize-y"
|
2025-10-09 12:04:37 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
2025-10-09 11:30:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Preview */}
|
2025-10-09 12:04:37 +08:00
|
|
|
|
{(wizardCommand ||
|
|
|
|
|
|
wizardArgs ||
|
|
|
|
|
|
wizardEnv ||
|
|
|
|
|
|
wizardUrl ||
|
|
|
|
|
|
wizardHeaders) && (
|
refactor: implement unified border design system
- Define custom border utilities in @layer utilities for consistent theming
- Add border-default (1px gray), border-active (2px primary), border-hover (40% primary), and border-dragging (60% primary) classes
- Update all UI components (Input, Select, TextArea, Button, Dialog, Dropdown) to use unified border classes
- Replace hardcoded border colors (gray-200/300/600/700) with theme-responsive border-border-default
- Update provider cards, MCP components, settings, and forms with new border system
- Remove dark mode border overrides to simplify CSS and improve maintainability
- Ensure all borders automatically adapt to light/dark themes via CSS variables
2025-10-20 23:44:06 +08:00
|
|
|
|
<div className="space-y-2 border-t border-border-default pt-4 ">
|
2025-10-09 11:30:28 +08:00
|
|
|
|
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
|
|
|
|
{t("mcp.wizard.preview")}
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<pre className="overflow-x-auto rounded-lg bg-gray-50 p-3 text-xs font-mono text-gray-700 dark:bg-gray-800 dark:text-gray-300">
|
|
|
|
|
|
{preview}
|
|
|
|
|
|
</pre>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Footer */}
|
2025-10-16 16:20:45 +08:00
|
|
|
|
<DialogFooter className="gap-3 pt-4">
|
|
|
|
|
|
<Button type="button" variant="ghost" onClick={handleClose}>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
{t("common.cancel")}
|
2025-10-16 16:20:45 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
<Button type="button" variant="mcp" onClick={handleApply}>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
<Save className="h-4 w-4" />
|
|
|
|
|
|
{t("mcp.wizard.apply")}
|
2025-10-16 16:20:45 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
</Dialog>
|
2025-10-09 11:30:28 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default McpWizardModal;
|