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",
|
||||
"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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import { McpServerSpec } from "../../types";
|
||||
import { McpServerSpec } from "@/types";
|
||||
|
||||
interface McpWizardModalProps {
|
||||
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 ${
|
||||
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"
|
||||
}`}
|
||||
>
|
||||
<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> {
|
||||
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 });
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user