feat: complete stage 4 cleanup and code formatting
This commit completes stage 4 of the refactoring plan, focusing on cleanup and optimization of the modernized codebase. ## Key Changes ### Code Cleanup - Remove legacy `src/lib/styles.ts` (no longer needed) - Remove old modal components (`ImportProgressModal.tsx`, `ProviderList.tsx`) - Streamline `src/lib/tauri-api.ts` from 712 lines to 17 lines (-97.6%) - Remove global `window.api` pollution - Keep only event listeners (`tauriEvents.onProviderSwitched`) - All API calls now use modular `@/lib/api/*` layer ### Type System - Clean up `src/vite-env.d.ts` (remove 156 lines of outdated types) - Remove obsolete global type declarations - All TypeScript checks pass with zero errors ### Code Formatting - Format all source files with Prettier (82 files) - Fix formatting issues in 15 files: - App.tsx and core components - MCP management components - Settings module components - Provider management components - UI components ### Documentation Updates - Update `REFACTORING_CHECKLIST.md` with stage 4 progress - Mark completed tasks in `REFACTORING_MASTER_PLAN.md` ## Impact **Code Reduction:** - Total: -1,753 lines, +384 lines (net -1,369 lines) - tauri-api.ts: 712 → 17 lines (-97.6%) - Removed styles.ts: -82 lines - Removed vite-env.d.ts declarations: -156 lines **Quality Improvements:** - ✅ Zero TypeScript errors - ✅ Zero TODO/FIXME comments - ✅ 100% Prettier compliant - ✅ Zero `window.api` references - ✅ Fully modular API layer ## Testing - [x] TypeScript compilation passes - [x] Code formatting validated - [x] No linting errors Stage 4 completion: 100% Ready for stage 5 (testing and bug fixes)
This commit is contained in:
@@ -1,18 +1,27 @@
|
||||
import React, { useMemo, useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { X, Save, AlertCircle, ChevronDown, ChevronUp, AlertTriangle } from "lucide-react";
|
||||
import {
|
||||
X,
|
||||
Save,
|
||||
AlertCircle,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
AlertTriangle,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { mcpApi, type AppType } from "@/lib/api";
|
||||
import { McpServer, McpServerSpec } from "../../types";
|
||||
import {
|
||||
mcpPresets,
|
||||
getMcpPresetWithDescription,
|
||||
} from "../../config/mcpPresets";
|
||||
import { buttonStyles, inputStyles } from "../../lib/styles";
|
||||
import McpWizardModal from "./McpWizardModal";
|
||||
import {
|
||||
extractErrorMessage,
|
||||
translateMcpBackendError,
|
||||
} from "../../utils/errorUtils";
|
||||
import { AppType } from "../../lib/tauri-api";
|
||||
import {
|
||||
validateToml,
|
||||
tomlToMcpServer,
|
||||
@@ -125,10 +134,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
const syncTargetLabel =
|
||||
appType === "claude" ? t("apps.codex") : t("apps.claude");
|
||||
const otherAppType: AppType = appType === "claude" ? "codex" : "claude";
|
||||
const syncCheckboxId = useMemo(
|
||||
() => `sync-other-side-${appType}`,
|
||||
[appType],
|
||||
);
|
||||
const syncCheckboxId = useMemo(() => `sync-other-side-${appType}`, [appType]);
|
||||
|
||||
// 检测另一侧是否有同名 MCP
|
||||
useEffect(() => {
|
||||
@@ -140,8 +146,10 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
}
|
||||
|
||||
try {
|
||||
const otherConfig = await window.api.getMcpConfig(otherAppType);
|
||||
const hasConflict = Object.keys(otherConfig.servers || {}).includes(currentId);
|
||||
const otherConfig = await mcpApi.getConfig(otherAppType);
|
||||
const hasConflict = Object.keys(otherConfig.servers || {}).includes(
|
||||
currentId,
|
||||
);
|
||||
setOtherSideHasConflict(hasConflict);
|
||||
} catch (error) {
|
||||
console.error("检查另一侧 MCP 配置失败:", error);
|
||||
@@ -562,8 +570,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
className={inputStyles.text}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.titlePlaceholder")}
|
||||
value={formId}
|
||||
onChange={(e) => handleIdChange(e.target.value)}
|
||||
@@ -576,8 +584,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("mcp.form.name")}
|
||||
</label>
|
||||
<input
|
||||
className={inputStyles.text}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.namePlaceholder")}
|
||||
value={formName}
|
||||
onChange={(e) => setFormName(e.target.value)}
|
||||
@@ -608,8 +616,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("mcp.form.description")}
|
||||
</label>
|
||||
<input
|
||||
className={inputStyles.text}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.descriptionPlaceholder")}
|
||||
value={formDescription}
|
||||
onChange={(e) => setFormDescription(e.target.value)}
|
||||
@@ -621,8 +629,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("mcp.form.tags")}
|
||||
</label>
|
||||
<input
|
||||
className={inputStyles.text}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.tagsPlaceholder")}
|
||||
value={formTags}
|
||||
onChange={(e) => setFormTags(e.target.value)}
|
||||
@@ -634,8 +642,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("mcp.form.homepage")}
|
||||
</label>
|
||||
<input
|
||||
className={inputStyles.text}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.homepagePlaceholder")}
|
||||
value={formHomepage}
|
||||
onChange={(e) => setFormHomepage(e.target.value)}
|
||||
@@ -647,8 +655,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<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}
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t("mcp.form.docsPlaceholder")}
|
||||
value={formDocs}
|
||||
onChange={(e) => setFormDocs(e.target.value)}
|
||||
@@ -673,8 +681,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<textarea
|
||||
className={`${inputStyles.text} h-48 resize-none font-mono text-xs`}
|
||||
<Textarea
|
||||
className="h-48 resize-none font-mono text-xs"
|
||||
placeholder={
|
||||
useToml
|
||||
? t("mcp.form.tomlPlaceholder")
|
||||
@@ -707,7 +715,9 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<label
|
||||
htmlFor={syncCheckboxId}
|
||||
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer select-none"
|
||||
title={t("mcp.form.syncOtherSideHint", { target: syncTargetLabel })}
|
||||
title={t("mcp.form.syncOtherSideHint", {
|
||||
target: syncTargetLabel,
|
||||
})}
|
||||
>
|
||||
{t("mcp.form.syncOtherSide", { target: syncTargetLabel })}
|
||||
</label>
|
||||
@@ -716,7 +726,9 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
<div className="flex items-center gap-1.5 text-amber-600 dark:text-amber-400">
|
||||
<AlertTriangle size={14} />
|
||||
<span className="text-xs font-medium">
|
||||
{t("mcp.form.willOverwriteWarning", { target: syncTargetLabel })}
|
||||
{t("mcp.form.willOverwriteWarning", {
|
||||
target: syncTargetLabel,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -724,16 +736,15 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-200 rounded-lg transition-colors text-sm font-medium"
|
||||
>
|
||||
<Button type="button" variant="ghost" size="sm" onClick={onClose}>
|
||||
{t("common.cancel")}
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={handleSubmit}
|
||||
disabled={saving || (!isEditing && !!idError)}
|
||||
className={`inline-flex items-center gap-2 ${buttonStyles.mcp}`}
|
||||
className="bg-emerald-500 text-white hover:bg-emerald-600 dark:bg-emerald-600 dark:hover:bg-emerald-700"
|
||||
>
|
||||
<Save size={16} />
|
||||
{saving
|
||||
@@ -741,7 +752,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||
: isEditing
|
||||
? t("common.save")
|
||||
: t("common.add")}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Edit3, Trash2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { settingsApi } from "@/lib/api";
|
||||
import { McpServer } from "../../types";
|
||||
import { mcpPresets } from "../../config/mcpPresets";
|
||||
import { cardStyles, buttonStyles, cn } from "../../lib/styles";
|
||||
import McpToggle from "./McpToggle";
|
||||
|
||||
interface McpListItemProps {
|
||||
@@ -44,14 +45,14 @@ const McpListItem: React.FC<McpListItemProps> = ({
|
||||
const url = docsUrl || homepageUrl;
|
||||
if (!url) return;
|
||||
try {
|
||||
await window.api.openExternal(url);
|
||||
await settingsApi.openExternal(url);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(cardStyles.interactive, "!p-4 h-16")}>
|
||||
<div className="h-16 rounded-lg border border-border bg-card p-4 transition-[border-color,box-shadow] duration-200 hover:border-primary/40 hover:shadow-sm">
|
||||
<div className="flex items-center gap-4 h-full">
|
||||
{/* 左侧:Toggle 开关 */}
|
||||
<div className="flex-shrink-0">
|
||||
@@ -82,32 +83,36 @@ const McpListItem: React.FC<McpListItemProps> = ({
|
||||
{/* 右侧:操作按钮 */}
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
{docsUrl && (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={openDocs}
|
||||
className={buttonStyles.ghost}
|
||||
title={t("mcp.presets.docs")}
|
||||
>
|
||||
{t("mcp.presets.docs")}
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => onEdit(id)}
|
||||
className={buttonStyles.icon}
|
||||
title={t("common.edit")}
|
||||
>
|
||||
<Edit3 size={16} />
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => onDelete(id)}
|
||||
className={cn(
|
||||
buttonStyles.icon,
|
||||
"hover:text-red-500 hover:bg-red-100 dark:hover:text-red-400 dark:hover:bg-red-500/10",
|
||||
)}
|
||||
className="hover:text-red-500 hover:bg-red-100 dark:hover:text-red-400 dark:hover:bg-red-500/10"
|
||||
title={t("common.delete")}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { X, Plus, Server, Check } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { mcpApi, type AppType } from "@/lib/api";
|
||||
import { McpServer } from "../../types";
|
||||
import McpListItem from "./McpListItem";
|
||||
import McpFormModal from "./McpFormModal";
|
||||
@@ -9,9 +11,6 @@ import {
|
||||
extractErrorMessage,
|
||||
translateMcpBackendError,
|
||||
} from "../../utils/errorUtils";
|
||||
// 预设相关逻辑已迁移到“新增 MCP”面板,列表此处无需引用
|
||||
import { buttonStyles } from "../../lib/styles";
|
||||
import { AppType } from "../../lib/tauri-api";
|
||||
|
||||
interface McpPanelProps {
|
||||
onClose: () => void;
|
||||
@@ -43,7 +42,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
const reload = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const cfg = await window.api.getMcpConfig(appType);
|
||||
const cfg = await mcpApi.getConfig(appType);
|
||||
setServers(cfg.servers || {});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -55,9 +54,9 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
try {
|
||||
// 初始化:仅从对应客户端导入已有 MCP,不做“预设落库”
|
||||
if (appType === "claude") {
|
||||
await window.api.importMcpFromClaude();
|
||||
await mcpApi.importFromClaude();
|
||||
} else if (appType === "codex") {
|
||||
await window.api.importMcpFromCodex();
|
||||
await mcpApi.importFromCodex();
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("MCP 初始化导入失败(忽略继续)", e);
|
||||
@@ -82,7 +81,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
|
||||
try {
|
||||
// 后台调用 API
|
||||
await window.api.setMcpEnabled(appType, id, enabled);
|
||||
await mcpApi.setEnabled(appType, id, enabled);
|
||||
onNotify?.(
|
||||
enabled ? t("mcp.msg.enabled") : t("mcp.msg.disabled"),
|
||||
"success",
|
||||
@@ -118,7 +117,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
message: t("mcp.confirm.deleteMessage", { id }),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await window.api.deleteMcpServerInConfig(appType, id);
|
||||
await mcpApi.deleteServerInConfig(appType, id);
|
||||
await reload();
|
||||
setConfirmDialog(null);
|
||||
onNotify?.(t("mcp.msg.deleted"), "success", 1500);
|
||||
@@ -142,7 +141,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
) => {
|
||||
try {
|
||||
const payload: McpServer = { ...server, id };
|
||||
await window.api.upsertMcpServerInConfig(appType, id, payload, {
|
||||
await mcpApi.upsertServerInConfig(appType, id, payload, {
|
||||
syncOtherSide: options?.syncOtherSide,
|
||||
});
|
||||
await reload();
|
||||
@@ -197,19 +196,18 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
</h3>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={handleAdd}
|
||||
className={`inline-flex items-center gap-2 ${buttonStyles.mcp}`}
|
||||
className="bg-emerald-500 text-white hover:bg-emerald-600 dark:bg-emerald-600 dark:hover:bg-emerald-700"
|
||||
>
|
||||
<Plus size={16} />
|
||||
{t("mcp.add")}
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 text-gray-500 hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors"
|
||||
>
|
||||
</Button>
|
||||
<Button type="button" variant="ghost" size="icon" onClick={onClose}>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -272,13 +270,15 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify, appType }) => {
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex-shrink-0 flex items-center justify-end p-6 border-t border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-800">
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
className={`inline-flex items-center gap-2 ${buttonStyles.mcp}`}
|
||||
className="bg-emerald-500 text-white hover:bg-emerald-600 dark:bg-emerald-600 dark:hover:bg-emerald-700"
|
||||
>
|
||||
<Check size={16} />
|
||||
{t("common.done")}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -173,8 +173,7 @@ const McpWizardModal: React.FC<McpWizardModalProps> = ({
|
||||
setWizardTitle(title);
|
||||
|
||||
const resolvedType =
|
||||
initialServer?.type ??
|
||||
(initialServer?.url ? "http" : "stdio");
|
||||
initialServer?.type ?? (initialServer?.url ? "http" : "stdio");
|
||||
|
||||
setWizardType(resolvedType);
|
||||
|
||||
@@ -203,7 +202,9 @@ const McpWizardModal: React.FC<McpWizardModalProps> = ({
|
||||
setWizardArgs(Array.isArray(argsValue) ? argsValue.join("\n") : "");
|
||||
const envCandidate = initialServer?.env;
|
||||
const env =
|
||||
envCandidate && typeof envCandidate === "object" ? envCandidate : undefined;
|
||||
envCandidate && typeof envCandidate === "object"
|
||||
? envCandidate
|
||||
: undefined;
|
||||
setWizardEnv(
|
||||
env
|
||||
? Object.entries(env)
|
||||
|
||||
Reference in New Issue
Block a user