diff --git a/package.json b/package.json index 4d3e087..4ae44f5 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "i18next": "^25.5.2", "jsonc-parser": "^3.2.1", "lucide-react": "^0.542.0", - "next-themes": "^0.4.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.65.0", diff --git a/src/components/UpdateBadge.tsx b/src/components/UpdateBadge.tsx index 3d872e5..0d1c508 100644 --- a/src/components/UpdateBadge.tsx +++ b/src/components/UpdateBadge.tsx @@ -1,5 +1,5 @@ import { X, Download } from "lucide-react"; -import { useUpdate } from "../contexts/UpdateContext"; +import { useUpdate } from "@/contexts/UpdateContext"; import { useTranslation } from "react-i18next"; interface UpdateBadgeProps { diff --git a/src/components/mcp/McpFormModal.tsx b/src/components/mcp/McpFormModal.tsx index 8ef24af..6f8a8aa 100644 --- a/src/components/mcp/McpFormModal.tsx +++ b/src/components/mcp/McpFormModal.tsx @@ -18,21 +18,11 @@ import { 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 { McpServer, McpServerSpec } from "@/types"; +import { mcpPresets, getMcpPresetWithDescription } from "@/config/mcpPresets"; import McpWizardModal from "./McpWizardModal"; -import { - extractErrorMessage, - translateMcpBackendError, -} from "../../utils/errorUtils"; -import { - tomlToMcpServer, - extractIdFromToml, - mcpServerToToml, -} from "../../utils/tomlUtils"; +import { extractErrorMessage, translateMcpBackendError } from "@/utils/errorUtils"; +import { tomlToMcpServer, extractIdFromToml, mcpServerToToml } from "@/utils/tomlUtils"; import { useMcpValidation } from "./useMcpValidation"; interface McpFormModalProps { diff --git a/src/components/mcp/McpListItem.tsx b/src/components/mcp/McpListItem.tsx index 07a6e9d..4d9bb78 100644 --- a/src/components/mcp/McpListItem.tsx +++ b/src/components/mcp/McpListItem.tsx @@ -3,8 +3,8 @@ 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 { McpServer } from "@/types"; +import { mcpPresets } from "@/config/mcpPresets"; import McpToggle from "./McpToggle"; interface McpListItemProps { diff --git a/src/components/mcp/McpPanel.tsx b/src/components/mcp/McpPanel.tsx index 35bf329..1aea617 100644 --- a/src/components/mcp/McpPanel.tsx +++ b/src/components/mcp/McpPanel.tsx @@ -9,14 +9,11 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { mcpApi, type AppType } from "@/lib/api"; -import { McpServer } from "../../types"; +import { McpServer } from "@/types"; import McpListItem from "./McpListItem"; import McpFormModal from "./McpFormModal"; import { ConfirmDialog } from "../ConfirmDialog"; -import { - extractErrorMessage, - translateMcpBackendError, -} from "../../utils/errorUtils"; +import { extractErrorMessage, translateMcpBackendError } from "@/utils/errorUtils"; interface McpPanelProps { open: boolean; diff --git a/src/components/mcp/McpWizardModal.tsx b/src/components/mcp/McpWizardModal.tsx index 41aa7af..3c6a98d 100644 --- a/src/components/mcp/McpWizardModal.tsx +++ b/src/components/mcp/McpWizardModal.tsx @@ -9,7 +9,7 @@ import { DialogTitle, DialogFooter, } from "@/components/ui/dialog"; -import { McpServerSpec } from "../../types"; +import { McpServerSpec } from "@/types"; interface McpWizardModalProps { isOpen: boolean; diff --git a/src/components/providers/forms/EndpointSpeedTest.tsx b/src/components/providers/forms/EndpointSpeedTest.tsx index 6c86ff4..bec767c 100644 --- a/src/components/providers/forms/EndpointSpeedTest.tsx +++ b/src/components/providers/forms/EndpointSpeedTest.tsx @@ -551,7 +551,7 @@ const EndpointSpeedTest: React.FC = ({ className={`group flex cursor-pointer items-center justify-between px-3 py-2.5 rounded-lg border transition ${ isSelected ? "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" }`} >
diff --git a/src/hooks/useDarkMode.ts b/src/hooks/useDarkMode.ts deleted file mode 100644 index 9a6efd5..0000000 --- a/src/hooks/useDarkMode.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { useState, useEffect } from "react"; - -export function useDarkMode() { - // 初始设为 false,挂载后在 useEffect 中加载真实值 - const [isDarkMode, setIsDarkMode] = useState(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 }; -} diff --git a/src/lib/api/settings.ts b/src/lib/api/settings.ts index 3dec1f7..9934f5a 100644 --- a/src/lib/api/settings.ts +++ b/src/lib/api/settings.ts @@ -98,6 +98,15 @@ export const settingsApi = { }, async openExternal(url: string): Promise { + 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 }); }, };