refactor: cleanup and minor improvements
- Remove unused useDarkMode hook (now using shadcn theme-provider) - Clean up MCP components (remove redundant code) - Add restart API to settings - Minor type improvements
This commit is contained in:
@@ -57,7 +57,6 @@
|
|||||||
"i18next": "^25.5.2",
|
"i18next": "^25.5.2",
|
||||||
"jsonc-parser": "^3.2.1",
|
"jsonc-parser": "^3.2.1",
|
||||||
"lucide-react": "^0.542.0",
|
"lucide-react": "^0.542.0",
|
||||||
"next-themes": "^0.4.6",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.65.0",
|
"react-hook-form": "^7.65.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { X, Download } from "lucide-react";
|
import { X, Download } from "lucide-react";
|
||||||
import { useUpdate } from "../contexts/UpdateContext";
|
import { useUpdate } from "@/contexts/UpdateContext";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface UpdateBadgeProps {
|
interface UpdateBadgeProps {
|
||||||
|
|||||||
@@ -18,21 +18,11 @@ import {
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { mcpApi, type AppType } from "@/lib/api";
|
import { mcpApi, type AppType } from "@/lib/api";
|
||||||
import { McpServer, McpServerSpec } from "../../types";
|
import { McpServer, McpServerSpec } from "@/types";
|
||||||
import {
|
import { mcpPresets, getMcpPresetWithDescription } from "@/config/mcpPresets";
|
||||||
mcpPresets,
|
|
||||||
getMcpPresetWithDescription,
|
|
||||||
} from "../../config/mcpPresets";
|
|
||||||
import McpWizardModal from "./McpWizardModal";
|
import McpWizardModal from "./McpWizardModal";
|
||||||
import {
|
import { extractErrorMessage, translateMcpBackendError } from "@/utils/errorUtils";
|
||||||
extractErrorMessage,
|
import { tomlToMcpServer, extractIdFromToml, mcpServerToToml } from "@/utils/tomlUtils";
|
||||||
translateMcpBackendError,
|
|
||||||
} from "../../utils/errorUtils";
|
|
||||||
import {
|
|
||||||
tomlToMcpServer,
|
|
||||||
extractIdFromToml,
|
|
||||||
mcpServerToToml,
|
|
||||||
} from "../../utils/tomlUtils";
|
|
||||||
import { useMcpValidation } from "./useMcpValidation";
|
import { useMcpValidation } from "./useMcpValidation";
|
||||||
|
|
||||||
interface McpFormModalProps {
|
interface McpFormModalProps {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Edit3, Trash2 } from "lucide-react";
|
import { Edit3, Trash2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { settingsApi } from "@/lib/api";
|
import { settingsApi } from "@/lib/api";
|
||||||
import { McpServer } from "../../types";
|
import { McpServer } from "@/types";
|
||||||
import { mcpPresets } from "../../config/mcpPresets";
|
import { mcpPresets } from "@/config/mcpPresets";
|
||||||
import McpToggle from "./McpToggle";
|
import McpToggle from "./McpToggle";
|
||||||
|
|
||||||
interface McpListItemProps {
|
interface McpListItemProps {
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { mcpApi, type AppType } from "@/lib/api";
|
import { mcpApi, type AppType } from "@/lib/api";
|
||||||
import { McpServer } from "../../types";
|
import { McpServer } from "@/types";
|
||||||
import McpListItem from "./McpListItem";
|
import McpListItem from "./McpListItem";
|
||||||
import McpFormModal from "./McpFormModal";
|
import McpFormModal from "./McpFormModal";
|
||||||
import { ConfirmDialog } from "../ConfirmDialog";
|
import { ConfirmDialog } from "../ConfirmDialog";
|
||||||
import {
|
import { extractErrorMessage, translateMcpBackendError } from "@/utils/errorUtils";
|
||||||
extractErrorMessage,
|
|
||||||
translateMcpBackendError,
|
|
||||||
} from "../../utils/errorUtils";
|
|
||||||
|
|
||||||
interface McpPanelProps {
|
interface McpPanelProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { McpServerSpec } from "../../types";
|
import { McpServerSpec } from "@/types";
|
||||||
|
|
||||||
interface McpWizardModalProps {
|
interface McpWizardModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
className={`group flex cursor-pointer items-center justify-between px-3 py-2.5 rounded-lg border transition ${
|
className={`group flex cursor-pointer items-center justify-between px-3 py-2.5 rounded-lg border transition ${
|
||||||
isSelected
|
isSelected
|
||||||
? "border-blue-500 bg-blue-50 dark:border-blue-500 dark:bg-blue-900/20"
|
? "border-blue-500 bg-blue-50 dark:border-blue-500 dark:bg-blue-900/20"
|
||||||
: "border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600 dark:hover:bg-gray-850"
|
: "border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600 dark:hover:bg-gray-800"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
export function useDarkMode() {
|
|
||||||
// 初始设为 false,挂载后在 useEffect 中加载真实值
|
|
||||||
const [isDarkMode, setIsDarkMode] = useState<boolean>(false);
|
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
const isDev = import.meta.env.DEV;
|
|
||||||
|
|
||||||
// 组件挂载后加载初始值(兼容 Tauri 环境)
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window === "undefined") return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 尝试读取已保存的偏好
|
|
||||||
const saved = localStorage.getItem("darkMode");
|
|
||||||
if (saved !== null) {
|
|
||||||
const savedBool = saved === "true";
|
|
||||||
setIsDarkMode(savedBool);
|
|
||||||
if (isDev)
|
|
||||||
console.log("[DarkMode] Loaded from localStorage:", savedBool);
|
|
||||||
} else {
|
|
||||||
// 回退到系统偏好
|
|
||||||
const prefersDark =
|
|
||||||
window.matchMedia &&
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
||||||
setIsDarkMode(prefersDark);
|
|
||||||
if (isDev)
|
|
||||||
console.log("[DarkMode] Using system preference:", prefersDark);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[DarkMode] Error loading preference:", error);
|
|
||||||
setIsDarkMode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsInitialized(true);
|
|
||||||
}, []); // 仅在首次挂载时运行
|
|
||||||
|
|
||||||
// 将 dark 类应用到文档根节点
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) return;
|
|
||||||
|
|
||||||
// 添加短暂延迟以确保 Tauri 中 DOM 已就绪
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
try {
|
|
||||||
if (isDarkMode) {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
if (isDev) console.log("[DarkMode] Added dark class to document");
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
if (isDev) console.log("[DarkMode] Removed dark class from document");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查类名是否已成功应用
|
|
||||||
const hasClass = document.documentElement.classList.contains("dark");
|
|
||||||
if (isDev) console.log("[DarkMode] Document has dark class:", hasClass);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[DarkMode] Error applying dark class:", error);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
}, [isDarkMode, isInitialized]);
|
|
||||||
|
|
||||||
// 将偏好保存到 localStorage
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isInitialized) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
localStorage.setItem("darkMode", isDarkMode.toString());
|
|
||||||
if (isDev) console.log("[DarkMode] Saved to localStorage:", isDarkMode);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("[DarkMode] Error saving preference:", error);
|
|
||||||
}
|
|
||||||
}, [isDarkMode, isInitialized]);
|
|
||||||
|
|
||||||
const toggleDarkMode = () => {
|
|
||||||
setIsDarkMode((prev) => {
|
|
||||||
const newValue = !prev;
|
|
||||||
if (isDev) console.log("[DarkMode] Toggling from", prev, "to", newValue);
|
|
||||||
return newValue;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return { isDarkMode, toggleDarkMode };
|
|
||||||
}
|
|
||||||
@@ -98,6 +98,15 @@ export const settingsApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async openExternal(url: string): Promise<void> {
|
async openExternal(url: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const u = new URL(url);
|
||||||
|
const scheme = u.protocol.replace(":", "").toLowerCase();
|
||||||
|
if (scheme !== "http" && scheme !== "https") {
|
||||||
|
throw new Error("Unsupported URL scheme");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
throw new Error("Invalid URL");
|
||||||
|
}
|
||||||
await invoke("open_external", { url });
|
await invoke("open_external", { url });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user