import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Link } from "react-router-dom";
import * as promptsApi from "../api/prompts";
import type { PromptFile } from "../api/prompts";
import * as settingsApi from "../api/settings";
import { useAvatars } from "../context/AvatarContext";
import { useBootstrap } from "../context/BootstrapContext";
import type { AgentToolInfo, EnvironmentInfo, EnvironmentToolsInfo, ExtensionInfo, McpServerInfo, RuntimeInfo, SettingsPaneId, SkillInfo } from "../types/events";
import type { McpToolInfo } from "../types/events";
import { renderMarkdown } from "../utils/markdown";
import styles from "./SettingsPage.module.css";

const WebTerminal = lazy(() =>
	import("../components/terminal/WebTerminal").then((module) => ({ default: module.WebTerminal })),
);

const PANE_IDS: SettingsPaneId[] = ["prompt", "models", "skills", "mcp", "extensions", "prompts", "other", "terminal", "env"];

function formatUptime(seconds: number): string {
	if (seconds < 60) return `${seconds} 秒`;
	const m = Math.floor(seconds / 60) % 60;
	const h = Math.floor(seconds / 3600) % 24;
	const d = Math.floor(seconds / 86400);
	if (d > 0) return `${d} 天 ${h} 时 ${m} 分`;
	if (h > 0) return `${h} 时 ${m} 分`;
	return `${m} 分`;
}

function isNpmSkill(skill: SkillInfo): boolean {
	if (skill.toggleable === false) return true;
	const source = skill.source || "";
	if (source.startsWith("npm:")) return true;
	const path = skill.path || "";
	return path.includes("/node_modules/") || path.includes("\\node_modules\\");
}

function isSkillToggleable(skill: SkillInfo): boolean {
	return !isNpmSkill(skill) && skill.toggleable !== false;
}

function formatSkillMeta(skill: SkillInfo): string {
	const source = (skill.source || "").trim();
	if (!source || source === "auto") return "";
	return source;
}
const SETTINGS_PANE_KEY = "sproutclaw-settings-pane";

function validateModelsConfigJson(text: string): string | null {
	const trimmed = text.trim();
	if (!trimmed) {
		return "JSON 不能为空";
	}

	let parsed: unknown;
	try {
		parsed = JSON.parse(trimmed);
	} catch (err) {
		const message = err instanceof SyntaxError ? err.message : String(err);
		return `JSON 语法错误：${message}`;
	}

	if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
		return "模型配置必须是 JSON 对象";
	}

	const root = parsed as Record<string, unknown>;
	if (!("providers" in root)) {
		return '缺少必填字段 "providers"';
	}
	if (!root.providers || typeof root.providers !== "object" || Array.isArray(root.providers)) {
		return '"providers" 必须是对象';
	}

	for (const [name, provider] of Object.entries(root.providers as Record<string, unknown>)) {
		if (!provider || typeof provider !== "object" || Array.isArray(provider)) {
			return `"providers.${name}" 必须是对象`;
		}
	}

	return null;
}

function renderNameList(label: string, items: string[] | undefined) {
	if (!Array.isArray(items) || !items.length) return null;
	return (
		<div className={styles.nameList}>
			<span>{label}</span>
			<div>
				{items.map((item) => (
					<code key={item}>{item}</code>
				))}
			</div>
		</div>
	);
}

function extensionCategory(extension: ExtensionInfo): "local" | "npm" {
	if (extension.category === "npm" || extension.category === "local") {
		return extension.category;
	}
	if (extension.source?.startsWith("npm:")) return "npm";
	const pathHint = `${extension.path || ""} ${extension.location || ""}`.replace(/\\/g, "/");
	if (pathHint.includes("/.pi/agent/npm/node_modules/")) return "npm";
	return "local";
}

function extensionTogglePath(extension: ExtensionInfo): string {
	return extension.resolvedPath || extension.path || "";
}

// Only local extensions can be enabled/disabled (by moving the directory between
// extensions/ and extensions-disabled/). npm extensions are not managed here.
function extensionToggleable(extension: ExtensionInfo): boolean {
	if (extension.toggleable === false) return false;
	return extensionCategory(extension) === "local";
}

function ExtensionCard({
	extension,
	toggling,
	disabled,
	onToggle,
}: {
	extension: ExtensionInfo;
	toggling: boolean;
	disabled: boolean;
	onToggle: (enabled: boolean) => void;
}) {
	const meta = [extension.kind, extension.scope, extension.source].filter(Boolean).join(" · ");
	const enabled = extension.enabled !== false;
	const toggleable = extensionToggleable(extension);
	const togglePath = extensionTogglePath(extension);
	const versionLabel = extension.version ? `v${extension.version}` : "";
	const counts = (
		[
			["命令", extension.commands?.length || 0],
			["工具", extension.tools?.length || 0],
			["事件", extension.handlers?.length || 0],
			["Flags", extension.flags?.length || 0],
			["快捷键", extension.shortcuts?.length || 0],
		] as const
	).filter(([, count]) => count > 0);
	const hasDetails = ["commands", "tools", "handlers", "flags", "shortcuts"].some(
		(key) => (extension[key as keyof ExtensionInfo] as string[] | undefined)?.length,
	);

	return (
		<div className={`${styles.extensionItem} ${enabled ? "" : styles.skillItemDisabled}`}>
			<div className={styles.extensionHeader}>
				<div className={styles.extensionTitleRow}>
					<div className={styles.itemName}>{extension.name || extension.path || "未命名"}</div>
					{toggleable ? (
						<label className={styles.skillToggle}>
							<input
								type="checkbox"
								checked={enabled}
								disabled={!togglePath || toggling || disabled}
								onChange={(e) => {
									if (!togglePath) return;
									onToggle(e.target.checked);
								}}
							/>
							<span className={styles.skillToggleUi} aria-hidden />
							<span className={styles.skillToggleLabel}>{enabled ? "已启用" : "已禁用"}</span>
						</label>
					) : versionLabel ? (
						<div className={styles.itemMeta}>{versionLabel}</div>
					) : null}
				</div>
				{meta ? <div className={styles.extensionSubtitle}>{meta}</div> : null}
			</div>
			{counts.length ? (
				<div className={styles.extensionCounts}>
					{counts.map(([label, count]) => (
						<span key={label}>
							{label} {count}
						</span>
					))}
				</div>
			) : null}
			{hasDetails ? (
				<details className={styles.extensionDetails}>
					<summary>查看注册项</summary>
					{renderNameList("命令", extension.commands)}
					{renderNameList("工具", extension.tools)}
					{renderNameList("事件", extension.handlers)}
					{renderNameList("Flags", extension.flags)}
					{renderNameList("快捷键", extension.shortcuts)}
				</details>
			) : null}
			{extension.location ? <div className={styles.itemPath}>{extension.location}</div> : null}
		</div>
	);
}

function ExtensionGroup({
	title,
	items,
	emptyText,
	togglingPath,
	disabled,
	onToggle,
}: {
	title: string;
	items: ExtensionInfo[];
	emptyText: string;
	togglingPath: string | null;
	disabled: boolean;
	onToggle: (path: string, enabled: boolean) => void;
}) {
	return (
		<section className={styles.extensionGroup}>
			<div className={styles.extensionGroupHeader}>
				<h3>{title}</h3>
				<span>{items.length} 个</span>
			</div>
			{items.length ? (
				<div className={styles.extensionGroupList}>
					{items.map((extension) => {
						const togglePath = extensionTogglePath(extension);
						return (
							<ExtensionCard
								key={togglePath || extension.location || extension.name}
								extension={extension}
								toggling={Boolean(togglePath && togglingPath === togglePath)}
								disabled={disabled}
								onToggle={(enabled) => {
									if (!togglePath) return;
									onToggle(togglePath, enabled);
								}}
							/>
						);
					})}
				</div>
			) : (
				<div className={`${styles.empty} ${styles.emptyCompact}`}>{emptyText}</div>
			)}
		</section>
	);
}

function McpToolItem({
	tool,
	serverEnabled,
	toggling,
	disabled,
	onToggle,
}: {
	tool: McpToolInfo;
	serverEnabled: boolean;
	toggling: boolean;
	disabled: boolean;
	onToggle: (enabled: boolean) => void;
}) {
	const params = Array.isArray(tool.parameters) ? tool.parameters : [];
	const required = new Set(Array.isArray(tool.required) ? tool.required : []);
	const description = String(tool.description || "").replace(/\s+/g, " ").trim();
	const shortDescription =
		description.length > 86 ? `${description.slice(0, 86).trimEnd()}...` : description;
	const title = [tool.name, description, params.length ? `参数: ${params.join(", ")}` : ""]
		.filter(Boolean)
		.join("\n");
	const enabled = tool.enabled !== false;
	const toolName = tool.name || "";

	return (
		<details
			className={`${styles.mcpToolItem} ${enabled ? "" : styles.mcpToolItemDisabled}`}
			title={title}
		>
			<summary className={styles.mcpToolSummary}>
				<span className={styles.mcpToolName}>{toolName || "未命名工具"}</span>
				<div className={styles.mcpToolSummaryActions}>
					{params.length ? <span className={styles.badge}>{params.length} 参数</span> : null}
					<label
						className={styles.skillToggle}
						onClick={(e) => e.stopPropagation()}
						onKeyDown={(e) => e.stopPropagation()}
					>
						<input
							type="checkbox"
							checked={enabled}
							disabled={!toolName || !serverEnabled || toggling || disabled}
							onChange={(e) => {
								if (!toolName || !serverEnabled) return;
								onToggle(e.target.checked);
							}}
						/>
						<span className={styles.skillToggleUi} aria-hidden />
						<span className={styles.skillToggleLabel}>{enabled ? "开" : "关"}</span>
					</label>
				</div>
			</summary>
			{shortDescription ? <div className={styles.mcpToolDesc}>{shortDescription}</div> : null}
			{params.length ? (
				<div className={styles.mcpParamList}>
					{params.map((param) => (
						<code key={param}>
							{param}
							{required.has(param) ? "*" : ""}
						</code>
					))}
				</div>
			) : null}
		</details>
	);
}

export function SettingsPage() {
	const { markRouteReady } = useBootstrap();
	const { updateAvatars } = useAvatars();
	const [activePane, setActivePane] = useState<SettingsPaneId>("prompt");
	const [systemPrompt, setSystemPrompt] = useState("");
	const [systemPromptPath, setSystemPromptPath] = useState("");
	const [systemPromptPreview, setSystemPromptPreview] = useState(false);
	const [modelsConfig, setModelsConfig] = useState("");
	const [modelsConfigPath, setModelsConfigPath] = useState("");
	const [userAvatarUrl, setUserAvatarUrl] = useState("");
	const [agentAvatarUrl, setAgentAvatarUrl] = useState("");
	const [skills, setSkills] = useState<SkillInfo[]>([]);
	const [mcpTools, setMcpTools] = useState<McpServerInfo[]>([]);
	const [extensions, setExtensions] = useState<ExtensionInfo[]>([]);
	const [extensionsPath, setExtensionsPath] = useState("");
	const [statusText, setStatusText] = useState("");
	const [statusKind, setStatusKind] = useState<"" | "ok" | "error">("");
	const [loading, setLoading] = useState(true);
	const [saving, setSaving] = useState(false);
	const [savingModels, setSavingModels] = useState(false);
	const [savingAvatars, setSavingAvatars] = useState(false);
	const [reloading, setReloading] = useState(false);
	const [restarting, setRestarting] = useState(false);

	// 命令模板 state
	const [promptList, setPromptList] = useState<PromptFile[]>([]);
	const [loadingPrompts, setLoadingPrompts] = useState(false);
	const [promptEditMode, setPromptEditMode] = useState<"list" | "edit">("list");
	const [promptFilename, setPromptFilename] = useState(""); // .md 后缀的文件名
	const [promptOriginalName, setPromptOriginalName] = useState(""); // 编辑前的原始文件名（空串表示新建）
	const [promptContent, setPromptContent] = useState("");
	const [promptPreview, setPromptPreview] = useState(false);
	const [savingPrompt, setSavingPrompt] = useState(false);
	const [deletingPrompt, setDeletingPrompt] = useState(false);

	const [togglingSkillPath, setTogglingSkillPath] = useState<string | null>(null);
	const [togglingMcpServer, setTogglingMcpServer] = useState<string | null>(null);
	const [togglingMcpTool, setTogglingMcpTool] = useState<string | null>(null);
	const [togglingExtensionPath, setTogglingExtensionPath] = useState<string | null>(null);
	const [envInfo, setEnvInfo] = useState<EnvironmentInfo | null>(null);
	const [loadingEnv, setLoadingEnv] = useState(false);
	const [toolsInfo, setToolsInfo] = useState<EnvironmentToolsInfo | null>(null);
	const [loadingTools, setLoadingTools] = useState(false);
	const [terminalPlatform, setTerminalPlatform] = useState("");
	const [terminalRequested, setTerminalRequested] = useState(false);
	const sidebarRef = useRef<HTMLElement>(null);

	useEffect(() => {
		let initial: SettingsPaneId = "prompt";
		try {
			const saved = sessionStorage.getItem(SETTINGS_PANE_KEY);
			if (saved && PANE_IDS.includes(saved as SettingsPaneId)) {
				initial = saved as SettingsPaneId;
			}
		} catch {
			/* ignore */
		}
		setActivePane(initial);
	}, []);

	const setStatus = (text: string, kind: "" | "ok" | "error" = "") => {
		setStatusText(text);
		setStatusKind(kind);
	};

	const showPane = (paneId: SettingsPaneId) => {
		setActivePane(paneId);
		if (paneId !== "prompt") {
			setSystemPromptPreview(false);
		}
		try {
			sessionStorage.setItem(SETTINGS_PANE_KEY, paneId);
		} catch {
			/* ignore */
		}
	};

	const loadSettings = useCallback(async () => {
		setLoading(true);
		setStatus("");
		try {
			const data = await settingsApi.fetchSettings();
			setSystemPrompt(data.systemPrompt || "");
			setSystemPromptPath(data.systemPromptPath || "");
			setModelsConfig(data.modelsConfig || "");
			setModelsConfigPath(data.modelsConfigPath || "");
			setUserAvatarUrl(data.userAvatarUrl || "");
			setAgentAvatarUrl(data.agentAvatarUrl || "");
			updateAvatars({
				userAvatarUrl: data.userAvatarUrl || "",
				agentAvatarUrl: data.agentAvatarUrl || "",
			});
			setSkills(data.skills || []);
			setMcpTools(data.mcpTools || []);
			setExtensions(data.extensions || []);
			setExtensionsPath(data.extensionsPath || "");
		} catch (err) {
			setStatus(`加载设置失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setLoading(false);
			markRouteReady("settings");
		}
	}, [markRouteReady, updateAvatars]);

	const reloadSettings = async () => {
		setReloading(true);
		setStatus("正在重新加载 Agent 资源...");
		try {
			await settingsApi.reloadSettings();
			await loadSettings();
			setStatus("已重新加载并刷新设置", "ok");
		} catch (err) {
			setStatus(`重新加载失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setReloading(false);
		}
	};

	const loadTerminalInfo = async () => {
		try {
			const data = await settingsApi.fetchTerminalInfo();
			if (data.platform) {
				setTerminalPlatform(data.platform);
			}
		} catch (err) {
			setStatus(`加载终端信息失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		}
	};

	const loadEnvInfo = async () => {
		setLoadingEnv(true);
		try {
			const data = await settingsApi.fetchEnvironment();
			setEnvInfo(data);
		} catch (err) {
			setStatus(`加载环境信息失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setLoadingEnv(false);
		}
	};

	const loadToolsInfo = async () => {
		setLoadingTools(true);
		try {
			const data = await settingsApi.fetchEnvironmentTools();
			setToolsInfo(data);
		} catch (err) {
			setStatus(`检测工具失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setLoadingTools(false);
		}
	};

	const loadPrompts = async () => {
		setLoadingPrompts(true);
		try {
			const data = await promptsApi.fetchPrompts();
			setPromptList(data.prompts || []);
		} catch (err) {
			setStatus(`加载命令模板失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setLoadingPrompts(false);
		}
	};

	const openPromptEditor = async (file: PromptFile) => {
		try {
			const data = await promptsApi.fetchPromptFile(file.name);
			setPromptOriginalName(file.name);
			setPromptFilename(file.name);
			setPromptContent(data.content);
			setPromptPreview(false);
			setPromptEditMode("edit");
		} catch (err) {
			setStatus(`加载文件失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		}
	};

	const openNewPrompt = () => {
		setPromptOriginalName("");
		setPromptFilename("新模板.md");
		setPromptContent("");
		setPromptPreview(false);
		setPromptEditMode("edit");
	};

	const savePrompt = async () => {
		let name = promptFilename.trim();
		if (!name) { setStatus("文件名不能为空", "error"); return; }
		if (!name.endsWith(".md")) name += ".md";
		setSavingPrompt(true);
		setStatus("保存中...");
		try {
			await promptsApi.savePromptFile(name, promptContent);
			// 如果改名了，删除旧文件
			if (promptOriginalName && promptOriginalName !== name) {
				await promptsApi.deletePromptFile(promptOriginalName);
			}
			setStatus("已保存", "ok");
			setPromptOriginalName(name);
			setPromptFilename(name);
			await loadPrompts();
		} catch (err) {
			setStatus(`保存失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setSavingPrompt(false);
		}
	};

	const deletePrompt = async () => {
		if (!promptOriginalName) {
			setPromptEditMode("list");
			return;
		}
		if (!confirm(`确认删除「${promptOriginalName}」？`)) return;
		setDeletingPrompt(true);
		try {
			await promptsApi.deletePromptFile(promptOriginalName);
			setStatus("已删除", "ok");
			setPromptEditMode("list");
			await loadPrompts();
		} catch (err) {
			setStatus(`删除失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setDeletingPrompt(false);
		}
	};

	useEffect(() => {
		if (activePane === "env") {
			if (!envInfo && !loadingEnv) void loadEnvInfo();
			if (!toolsInfo && !loadingTools) void loadToolsInfo();
		}
		if (activePane === "prompts" && promptList.length === 0 && !loadingPrompts) {
			void loadPrompts();
		}
		if (activePane !== "prompts") {
			setPromptEditMode("list");
		}
		if (activePane === "terminal" && !terminalPlatform) {
			void loadTerminalInfo();
		}
		if (activePane === "terminal") {
			setTerminalRequested(true);
		}
	}, [activePane]);

	useEffect(() => {
		const activeTab = sidebarRef.current?.querySelector<HTMLElement>('[aria-selected="true"]');
		activeTab?.scrollIntoView({ behavior: "smooth", inline: "nearest", block: "nearest" });
	}, [activePane]);

	const toggleSkillEnabled = async (path: string, enabled: boolean) => {
		setTogglingSkillPath(path);
		setStatus(enabled ? "正在启用 skill…" : "正在禁用 skill…");
		try {
			const data = await settingsApi.toggleSkill(path, enabled);
			if (data.skill) {
				setSkills((prev) =>
					prev.map((skill) => (skill.path === path ? { ...skill, ...data.skill } : skill)),
				);
			} else {
				await loadSettings();
			}
			setStatus(enabled ? "Skill 已启用" : "Skill 已禁用", "ok");
		} catch (err) {
			setStatus(`切换失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setTogglingSkillPath(null);
		}
	};

	const updateMcpServer = (entry: McpServerInfo) => {
		setMcpTools((prev) =>
			prev.map((server) => (server.name === entry.name ? { ...server, ...entry } : server)),
		);
	};

	const toggleMcpServerEnabled = async (serverName: string, enabled: boolean) => {
		setTogglingMcpServer(serverName);
		setStatus(enabled ? "正在启用 MCP Server…" : "正在禁用 MCP Server…");
		try {
			const data = await settingsApi.toggleMcpServer(serverName, enabled);
			if (data.server) {
				updateMcpServer(data.server);
			} else {
				await loadSettings();
			}
			setStatus(
				enabled
					? "MCP Server 已启用，必要时可运行 /mcp reconnect 刷新连接"
					: "MCP Server 已禁用",
				"ok",
			);
		} catch (err) {
			setStatus(`切换失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setTogglingMcpServer(null);
		}
	};

	const toggleMcpToolEnabled = async (serverName: string, toolName: string, enabled: boolean) => {
		const toggleKey = `${serverName}/${toolName}`;
		setTogglingMcpTool(toggleKey);
		setStatus(enabled ? "正在启用 MCP工具…" : "正在禁用 MCP工具…");
		try {
			const data = await settingsApi.toggleMcpTool(serverName, toolName, enabled);
			if (data.tool) {
				setMcpTools((prev) =>
					prev.map((server) => {
						if (server.name !== serverName || !Array.isArray(server.tools)) return server;
						const tools = server.tools.map((tool) =>
							tool.name === toolName ? { ...tool, ...data.tool } : tool,
						);
						const enabledToolCount = tools.filter((tool) => tool.enabled !== false).length;
						return { ...server, tools, enabledToolCount };
					}),
				);
			} else {
				await loadSettings();
			}
			setStatus(enabled ? "MCP工具已启用" : "MCP工具已禁用", "ok");
		} catch (err) {
			setStatus(`切换失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setTogglingMcpTool(null);
		}
	};

	const toggleExtensionEnabled = async (path: string, enabled: boolean) => {
		setTogglingExtensionPath(path);
		setStatus(enabled ? "正在启用扩展…" : "正在禁用扩展…");
		try {
			const data = await settingsApi.toggleExtension(path, enabled);
			if (data.extension) {
				setExtensions((prev) =>
					prev.map((extension) => {
						const key = extensionTogglePath(extension);
						return key === path ? { ...extension, ...data.extension } : extension;
					}),
				);
			} else {
				await loadSettings();
			}
			setStatus(enabled ? "扩展已启用，Agent 已重新加载" : "扩展已禁用，Agent 已重新加载", "ok");
		} catch (err) {
			setStatus(`切换失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setTogglingExtensionPath(null);
		}
	};

	const saveSystemPrompt = async () => {
		setSaving(true);
		setStatus("保存中...");
		try {
			const data = await settingsApi.saveSystemPrompt(systemPrompt);
			if (data.systemPromptPath) setSystemPromptPath(data.systemPromptPath);
			setStatus("已保存，Agent 已重新加载", "ok");
		} catch (err) {
			setStatus(`保存失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setSaving(false);
		}
	};

	const saveModelsConfig = async () => {
		const validationError = validateModelsConfigJson(modelsConfig);
		if (validationError) {
			setStatus(validationError, "error");
			return;
		}

		setSavingModels(true);
		setStatus("保存中...");
		try {
			const data = await settingsApi.saveModelsConfig(modelsConfig);
			if (data.modelsConfigPath) setModelsConfigPath(data.modelsConfigPath);
			const refreshed = await settingsApi.fetchSettings();
			setModelsConfig(refreshed.modelsConfig || "");
			setStatus("已保存，Agent 已重新加载", "ok");
		} catch (err) {
			setStatus(`保存失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setSavingModels(false);
		}
	};

	const restartBackend = async () => {
		setRestarting(true);
		setStatus("正在重启后端...");
		try {
			await settingsApi.restartBackend();
		} catch {
			// 后端重启时连接断开属正常现象，忽略错误
		}
		// 轮询直到后端恢复
		const poll = async () => {
			try {
				await settingsApi.fetchSettings();
				setStatus("后端已重启，正在刷新...", "ok");
				setTimeout(() => window.location.reload(), 800);
			} catch {
				setTimeout(poll, 1000);
			}
		};
		setTimeout(poll, 1500);
	};

	const saveAvatars = async () => {
		setSavingAvatars(true);
		setStatus("保存中...");
		try {
			const data = await settingsApi.saveAvatars({ userAvatarUrl, agentAvatarUrl });
			const next = {
				userAvatarUrl: data.userAvatarUrl || "",
				agentAvatarUrl: data.agentAvatarUrl || "",
			};
			setUserAvatarUrl(next.userAvatarUrl);
			setAgentAvatarUrl(next.agentAvatarUrl);
			updateAvatars(next);
			setStatus("头像设置已保存", "ok");
		} catch (err) {
			setStatus(`保存失败: ${err instanceof Error ? err.message : String(err)}`, "error");
		} finally {
			setSavingAvatars(false);
		}
	};

	useEffect(() => {
		void loadSettings();
	}, [loadSettings]);

	const toolTotal = mcpTools.reduce((sum, server) => sum + (server.toolCount || 0), 0);
	const enabledMcpServerCount = mcpTools.filter((server) => server.enabled !== false).length;
	const enabledMcpToolCount = mcpTools.reduce(
		(sum, server) => sum + (server.enabled !== false ? server.enabledToolCount ?? 0 : 0),
		0,
	);
	const localExtensions = extensions.filter((extension) => extensionCategory(extension) === "local");
	const npmExtensions = extensions.filter((extension) => extensionCategory(extension) === "npm");
	const toggleableSkills = skills.filter(isSkillToggleable);
	const enabledSkillCount = toggleableSkills.filter((skill) => skill.enabled !== false).length;
	const enabledExtensionCount = extensions.filter((extension) => extension.enabled !== false).length;
	const modelsConfigError = useMemo(() => validateModelsConfigJson(modelsConfig), [modelsConfig]);
	const systemPromptHtml = useMemo(
		() => (systemPromptPreview ? renderMarkdown(systemPrompt) : ""),
		[systemPromptPreview, systemPrompt],
	);
	const statusClass =
		statusKind === "ok" ? styles.statusOk : statusKind === "error" ? styles.statusError : "";

	return (
		<div className={styles.page}>
			<main className={styles.main}>
				<header className={styles.pageHeader}>
					<div className={styles.pageTitle}>
						<img className={styles.logo} src="/logo.png" width={34} height={34} alt="" decoding="async" />
						<div>
							<h1>设置</h1>
						</div>
					</div>
					<Link to="/" className={`${styles.secondaryBtn} ${styles.linkBtn}`}>
						返回
					</Link>
				</header>

				<div className={styles.shell}>
					<div className={styles.layout}>
						<nav className={styles.sidebar} ref={sidebarRef} role="tablist" aria-label="设置分区">
							{(
								[
									["prompt", "系统提示词"],
									["models", "模型配置"],
									["skills", "Skills"],
									["mcp", "MCP工具"],
									["extensions", "插件扩展"],
									["prompts", "命令模板"],
									["other", "其他设置"],
									["terminal", "终端"],
									["env", "环境信息"],
								] as const
							).map(([id, label]) => (
								<button
									key={id}
									type="button"
									role="tab"
									aria-selected={activePane === id}
									className={`${styles.navBtn} ${activePane === id ? styles.navBtnActive : ""}`}
									onClick={() => showPane(id)}
								>
									{label}
								</button>
							))}
						</nav>

						<div className={styles.panes}>
							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "prompt" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "prompt"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>系统提示词</h2>
										<p>{systemPromptPath}</p>
									</div>
									<div className={styles.panelHeaderActions}>
										<button
											type="button"
											className={`${styles.secondaryBtn} ${systemPromptPreview ? styles.headerBtnActive : ""}`}
											onClick={() => setSystemPromptPreview((preview) => !preview)}
										>
											{systemPromptPreview ? "编辑" : "预览"}
										</button>
										<button
											type="button"
											className={styles.primaryBtn}
											disabled={saving}
											onClick={() => void saveSystemPrompt()}
										>
											保存
										</button>
									</div>
								</div>
								{systemPromptPreview ? (
									<div className={styles.markdownPreview}>
										{systemPrompt.trim() ? (
											<div
												className={`markdown-body ${styles.markdownBody}`}
												dangerouslySetInnerHTML={{ __html: systemPromptHtml }}
											/>
										) : (
											<div className={`${styles.empty} ${styles.emptyCompact}`}>暂无内容</div>
										)}
									</div>
								) : (
									<textarea
										className={styles.textarea}
										spellCheck={false}
										aria-label="系统提示词"
										value={systemPrompt}
										onChange={(e) => setSystemPrompt(e.target.value)}
									/>
								)}
								{statusText && activePane === "prompt" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "models" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "models"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>模型配置</h2>
										<p>{modelsConfigPath}</p>
									</div>
									<button
										type="button"
										className={styles.primaryBtn}
										disabled={savingModels || Boolean(modelsConfigError)}
										onClick={() => void saveModelsConfig()}
									>
										保存
									</button>
								</div>
								<textarea
									className={`${styles.textarea} ${modelsConfigError ? styles.textareaInvalid : ""}`}
									spellCheck={false}
									aria-label="模型配置"
									aria-invalid={modelsConfigError ? true : undefined}
									value={modelsConfig}
									onChange={(e) => setModelsConfig(e.target.value)}
								/>
								{modelsConfigError ? (
									<div className={styles.jsonError} role="alert">
										{modelsConfigError}
									</div>
								) : null}
								{statusText && activePane === "models" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "skills" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "skills"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>Skills</h2>
										<p>
											{enabledSkillCount} / {toggleableSkills.length} 个已启用
										</p>
									</div>
									<button
										type="button"
										className={styles.secondaryBtn}
										disabled={reloading}
										onClick={() => void reloadSettings()}
									>
										刷新
									</button>
								</div>
								<div className={styles.list}>
									{loading ? (
										<div className={styles.empty}>加载中...</div>
									) : !skills.length ? (
										<div className={styles.empty}>暂无已加载 skills</div>
									) : (
										skills.map((skill) => {
											const meta = formatSkillMeta(skill);
											const skillPath = skill.path || "";
											const enabled = skill.enabled !== false;
											const toggleable = isSkillToggleable(skill);
											const toggling = togglingSkillPath === skillPath;
											return (
												<div
													key={skillPath || skill.name || skill.command}
													className={`${styles.skillItem} ${enabled ? "" : styles.skillItemDisabled}`}
												>
													<div className={styles.titleRow}>
														<div className={styles.itemName}>
															{skill.name || skill.command || "未命名"}
														</div>
														<div className={styles.skillActions}>
															{meta ? <div className={styles.itemMeta}>{meta}</div> : null}
															{toggleable ? (
																<label className={styles.skillToggle}>
																	<input
																		type="checkbox"
																		checked={enabled}
																		disabled={!skillPath || toggling || reloading}
																		onChange={(e) => {
																			if (!skillPath) return;
																			void toggleSkillEnabled(skillPath, e.target.checked);
																		}}
																	/>
																	<span className={styles.skillToggleUi} aria-hidden />
																	<span className={styles.skillToggleLabel}>
																		{enabled ? "已启用" : "已禁用"}
																	</span>
																</label>
															) : null}
														</div>
													</div>
													{skill.description ? (
														<div className={styles.itemDesc}>{skill.description}</div>
													) : null}
													{skill.path ? <div className={styles.itemPath}>{skill.path}</div> : null}
												</div>
											);
										})
									)}
								</div>
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "mcp" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "mcp"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>MCP工具</h2>
										<p>
											{enabledMcpServerCount} / {mcpTools.length} 个 Server · {enabledMcpToolCount} /{" "}
											{toolTotal} 个工具已启用
										</p>
									</div>
									<button
										type="button"
										className={styles.secondaryBtn}
										disabled={reloading}
										onClick={() => void reloadSettings()}
									>
										刷新
									</button>
								</div>
								<div className={styles.list}>
									{loading ? (
										<div className={styles.empty}>加载中...</div>
									) : !mcpTools.length ? (
										<div className={styles.empty}>暂无 MCP server 配置</div>
									) : (
										mcpTools.map((server) => {
											const tools = Array.isArray(server.tools) ? server.tools : [];
											const serverName = server.name || "";
											const serverEnabled = server.enabled !== false;
											const togglingServer = togglingMcpServer === serverName;
											const status = server.cached ? "已缓存" : "未连接";
											const cachedAt = server.cachedAt
												? new Date(server.cachedAt).toLocaleString()
												: "";
											return (
												<div
													key={serverName || "unnamed"}
													className={`${styles.mcpServerItem} ${serverEnabled ? "" : styles.skillItemDisabled}`}
												>
													<div className={styles.mcpServerHead}>
														<div className={styles.mcpServerMain}>
															<div className={styles.titleRow}>
																<div className={styles.itemName}>
																	{serverName || "未命名 Server"}
																</div>
																<label className={styles.skillToggle}>
																	<input
																		type="checkbox"
																		checked={serverEnabled}
																		disabled={!serverName || togglingServer || reloading}
																		onChange={(e) => {
																			if (!serverName) return;
																			void toggleMcpServerEnabled(serverName, e.target.checked);
																		}}
																	/>
																	<span className={styles.skillToggleUi} aria-hidden />
																	<span className={styles.skillToggleLabel}>
																		{serverEnabled ? "已启用" : "已禁用"}
																	</span>
																</label>
															</div>
															{cachedAt ? (
																<div className={styles.mcpServerSub}>更新 {cachedAt}</div>
															) : null}
														</div>
														<div className={styles.mcpServerBadges}>
															<span className={styles.badge}>{status}</span>
															<span className={styles.badge}>
																{serverEnabled
																	? `${server.enabledToolCount ?? tools.filter((tool) => tool.enabled !== false).length} / ${server.toolCount || tools.length} 工具`
																	: `${server.toolCount || tools.length} 工具`}
															</span>
															{server.resourceCount ? (
																<span className={styles.badge}>{server.resourceCount} 资源</span>
															) : null}
														</div>
													</div>
													{tools.length ? (
														<div className={styles.mcpToolList}>
															{tools.map((tool) => {
																const toolName = tool.name || "";
																const toggleKey = `${serverName}/${toolName}`;
																return (
																	<McpToolItem
																		key={toolName || "unnamed"}
																		tool={tool}
																		serverEnabled={serverEnabled}
																		toggling={togglingMcpTool === toggleKey}
																		disabled={reloading || togglingServer}
																		onToggle={(enabled) => {
																			if (!serverName || !toolName) return;
																			void toggleMcpToolEnabled(serverName, toolName, enabled);
																		}}
																	/>
																);
															})}
														</div>
													) : (
														<div className={`${styles.empty} ${styles.emptyCompact}`}>
															暂无已缓存工具，运行 /mcp reconnect 后刷新。
														</div>
													)}
												</div>
											);
										})
									)}
								</div>
								{statusText && activePane === "mcp" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "extensions" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "extensions"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>插件扩展</h2>
										<p>
											{enabledExtensionCount} / {extensions.length} 个已启用 · {localExtensions.length}{" "}
											个本地 · {npmExtensions.length} 个 npm
											{extensionsPath ? ` · ${extensionsPath}` : ""}
										</p>
									</div>
									<button
										type="button"
										className={styles.secondaryBtn}
										disabled={reloading}
										onClick={() => void reloadSettings()}
									>
										刷新
									</button>
								</div>
								<div className={styles.list}>
									{loading ? (
										<div className={styles.empty}>加载中...</div>
									) : !extensions.length ? (
										<div className={styles.empty}>暂无已加载插件扩展</div>
									) : (
										<>
											<ExtensionGroup
												title="本地扩展"
												items={localExtensions}
												emptyText="暂无本地扩展"
												togglingPath={togglingExtensionPath}
												disabled={reloading}
												onToggle={(path, enabled) => void toggleExtensionEnabled(path, enabled)}
											/>
											<ExtensionGroup
												title="npm 扩展"
												items={npmExtensions}
												emptyText="暂无 npm 扩展"
												togglingPath={togglingExtensionPath}
												disabled={reloading}
												onToggle={(path, enabled) => void toggleExtensionEnabled(path, enabled)}
											/>
										</>
									)}
								</div>
								{statusText && activePane === "extensions" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>

							{/* ===== 命令模板 ===== */}
							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "prompts" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "prompts"}
							>
								{promptEditMode === "list" ? (
									<>
										<div className={styles.panelHeader}>
											<div>
												<h2>命令模板</h2>
												<p>{promptList.length} 个模板 · 存储于 prompts/ 目录</p>
											</div>
											<div className={styles.panelHeaderActions}>
												<button
													type="button"
													className={styles.secondaryBtn}
													disabled={loadingPrompts}
													onClick={() => void loadPrompts()}
												>
													刷新
												</button>
												<button
													type="button"
													className={styles.primaryBtn}
													onClick={openNewPrompt}
												>
													新建
												</button>
											</div>
										</div>
										<div className={styles.list}>
											{loadingPrompts ? (
												<div className={styles.empty}>加载中...</div>
											) : promptList.length === 0 ? (
												<div className={styles.empty}>暂无命令模板，点击「新建」创建第一个</div>
											) : (
												<div className={styles.promptGrid}>
													{promptList.map((p) => (
														<button
															key={p.name}
															type="button"
															className={styles.promptCard}
															onClick={() => void openPromptEditor(p)}
														>
															<div className={styles.promptCardTitle}>{p.title}</div>
															{p.description ? (
																<div className={styles.promptCardDesc}>{p.description}</div>
															) : null}
															{p.argumentHint ? (
																<div className={styles.promptCardHint}>参数：{p.argumentHint}</div>
															) : null}
															<div className={styles.promptCardName}>{p.name}</div>
														</button>
													))}
												</div>
											)}
										</div>
									</>
								) : (
									<>
										<div className={styles.panelHeader}>
											<div className={styles.promptEditorTitle}>
												<button
													type="button"
													className={styles.backBtn}
													onClick={() => setPromptEditMode("list")}
													aria-label="返回列表"
												>
													←
												</button>
												<input
													className={styles.promptFilenameInput}
													value={promptFilename}
													onChange={(e) => setPromptFilename(e.target.value)}
													placeholder="文件名.md"
													spellCheck={false}
												/>
											</div>
											<div className={styles.panelHeaderActions}>
												<button
													type="button"
													className={`${styles.secondaryBtn} ${promptPreview ? styles.headerBtnActive : ""}`}
													onClick={() => setPromptPreview((v) => !v)}
												>
													{promptPreview ? "编辑" : "预览"}
												</button>
												{promptOriginalName ? (
													<button
														type="button"
														className={styles.dangerBtn}
														disabled={deletingPrompt}
														onClick={() => void deletePrompt()}
													>
														删除
													</button>
												) : null}
												<button
													type="button"
													className={styles.primaryBtn}
													disabled={savingPrompt}
													onClick={() => void savePrompt()}
												>
													保存
												</button>
											</div>
										</div>
										{promptPreview ? (
											<div className={styles.markdownPreview}>
												{promptContent.trim() ? (
													<div
														className={`markdown-body ${styles.markdownBody}`}
														dangerouslySetInnerHTML={{ __html: renderMarkdown(promptContent) }}
													/>
												) : (
													<div className={`${styles.empty} ${styles.emptyCompact}`}>暂无内容</div>
												)}
											</div>
										) : (
											<textarea
												className={styles.textarea}
												spellCheck={false}
												aria-label="命令模板内容"
												value={promptContent}
												onChange={(e) => setPromptContent(e.target.value)}
												placeholder={"在此输入提示词内容...\n\n支持 YAML frontmatter：\n---\ndescription: 描述\nargument-hint: <参数说明>\n---"}
											/>
										)}
										{statusText && activePane === "prompts" ? (
											<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
										) : null}
									</>
								)}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "other" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "other"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>其他设置</h2>
										<p>聊天头像使用网页图片链接</p>
									</div>
									<div className={styles.panelHeaderActions}>
										<button
											type="button"
											className={styles.secondaryBtn}
											disabled={restarting}
											onClick={() => void restartBackend()}
										>
											{restarting ? "重启中..." : "重启后端"}
										</button>
										<button
											type="button"
											className={styles.primaryBtn}
											disabled={savingAvatars}
											onClick={() => void saveAvatars()}
										>
											保存
										</button>
									</div>
								</div>
								<div className={styles.formBody}>
									<label className={styles.field}>
										<span className={styles.fieldLabel}>用户头像链接</span>
										<input
											className={styles.textInput}
											type="url"
											inputMode="url"
											placeholder="https://example.com/user-avatar.png"
											value={userAvatarUrl}
											onChange={(e) => setUserAvatarUrl(e.target.value)}
										/>
										{userAvatarUrl.trim() ? (
											<div className={styles.avatarPreviewRow}>
												<img
													className={styles.avatarPreview}
													src={userAvatarUrl.trim()}
													alt=""
													decoding="async"
												/>
												<span className={styles.previewHint}>预览</span>
											</div>
										) : null}
									</label>
									<label className={styles.field}>
										<span className={styles.fieldLabel}>Agent 头像链接</span>
										<input
											className={styles.textInput}
											type="url"
											inputMode="url"
											placeholder="https://example.com/agent-avatar.png"
											value={agentAvatarUrl}
											onChange={(e) => setAgentAvatarUrl(e.target.value)}
										/>
										{agentAvatarUrl.trim() ? (
											<div className={styles.avatarPreviewRow}>
												<img
													className={styles.avatarPreview}
													src={agentAvatarUrl.trim()}
													alt=""
													decoding="async"
												/>
												<span className={styles.previewHint}>预览</span>
											</div>
										) : null}
									</label>
									<p className={styles.fieldHint}>
										留空则不显示对应头像。仅支持 http / https 图片链接。
									</p>
								</div>
								{statusText && activePane === "other" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${styles.terminalPanel} ${activePane === "terminal" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "terminal"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>终端</h2>
									</div>
								</div>
								{terminalRequested ? (
									<Suspense fallback={<div className={styles.status}>正在加载终端…</div>}>
										<WebTerminal active={activePane === "terminal"} platform={terminalPlatform} />
									</Suspense>
								) : null}
							</section>

							<section
								className={`${styles.pane} ${styles.panel} ${activePane === "env" ? styles.paneActive : ""}`}
								role="tabpanel"
								hidden={activePane !== "env"}
							>
								<div className={styles.panelHeader}>
									<div>
										<h2>环境信息</h2>
		
									</div>
									<button
										type="button"
										className={styles.secondaryBtn}
										disabled={loadingEnv || loadingTools}
										onClick={() => { void loadEnvInfo(); void loadToolsInfo(); }}
									>
										刷新
									</button>
								</div>
								<div className={styles.formBody}>
									{loadingEnv ? (
										<div className={styles.empty}>加载中...</div>
									) : !envInfo ? (
										<div className={styles.empty}>暂无数据</div>
									) : (
										<div className={styles.envGrid}>
											<div className={styles.envSectionHeader}>系统信息</div>
											{(
												[
													["操作系统", envInfo.osRelease || envInfo.platform],
													["平台 / 架构", envInfo.platform && envInfo.arch ? `${envInfo.platform} / ${envInfo.arch}` : undefined],
													["主机名", envInfo.hostname],
													["CPU 核心数", envInfo.numCPU !== undefined ? `${envInfo.numCPU} 核` : undefined],
													["系统总内存", envInfo.totalMemMb ? `${envInfo.totalMemMb} MB` : undefined],
													["可用内存", envInfo.freeMemMb !== undefined && envInfo.totalMemMb
														? `${envInfo.freeMemMb} MB${envInfo.memUsagePct ? `（已用 ${envInfo.memUsagePct}）` : ""}`
														: undefined],
												] as [string, string | undefined][]
											)
												.filter(([, v]) => v !== undefined && v !== "")
												.map(([label, value]) => (
													<div key={label} className={styles.envRow}>
														<span className={styles.envLabel}>{label}</span>
														<code className={styles.envValue}>{value}</code>
													</div>
												))}
											<div className={styles.envSectionHeader}>进程信息</div>
											{(
												[
													["Go 版本", envInfo.goVersion || envInfo.nodeVersion],
													["进程 PID", envInfo.pid !== undefined ? String(envInfo.pid) : undefined],
													["运行时长", envInfo.uptime !== undefined ? formatUptime(envInfo.uptime) : undefined],
													["Goroutine 数", envInfo.goroutines !== undefined ? String(envInfo.goroutines) : undefined],
													["Go 堆内存", envInfo.goHeapMb !== undefined ? `${envInfo.goHeapMb} MB` : undefined],
													["可执行文件", envInfo.execPath],
												] as [string, string | undefined][]
											)
												.filter(([, v]) => v !== undefined && v !== "")
												.map(([label, value]) => (
													<div key={label} className={styles.envRow}>
														<span className={styles.envLabel}>{label}</span>
														<code className={styles.envValue}>{value}</code>
													</div>
												))}
											<div className={styles.envSectionHeader}>路径与配置</div>
											{(
												[
													["监听端口", envInfo.port !== undefined ? String(envInfo.port) : undefined],
													["Agent 目录", envInfo.agentDir],
													["数据目录", envInfo.dataDir],
													["项目根目录", envInfo.repoRoot],
													["工作目录", envInfo.cwd],
													["Pi 命令", envInfo.piCmd],
												] as [string, string | undefined][]
											)
												.filter(([, v]) => v !== undefined && v !== "")
												.map(([label, value]) => (
													<div key={label} className={styles.envRow}>
														<span className={styles.envLabel}>{label}</span>
														<code className={styles.envValue}>{value}</code>
													</div>
												))}
											<div className={styles.envSectionHeader}>
												Agent 工具{loadingTools ? <span className={styles.envSectionLoading}> · 检测中...</span> : null}
											</div>
											{loadingTools && !toolsInfo ? (
												<div className={`${styles.envRow} ${styles.envLoadingRow}`}>正在检测 Agent 工具...</div>
											) : Array.isArray(toolsInfo?.agentTools) ? (
												toolsInfo!.agentTools.map((tool: AgentToolInfo) => (
													<div key={tool.name} className={styles.envRow}>
														<span className={styles.envLabel}>{tool.name}</span>
														{tool.found ? (
															<span className={styles.envToolFound}>
																<code className={styles.envValue}>{tool.version || "已安装"}</code>
																{tool.path ? <span className={styles.envToolPath}>{tool.path}</span> : null}
															</span>
														) : (
															<span className={styles.envToolMissing}>未安装</span>
														)}
													</div>
												))
											) : null}
											<div className={styles.envSectionHeader}>
												运行时环境{loadingTools ? <span className={styles.envSectionLoading}> · 检测中...</span> : null}
											</div>
											{loadingTools && !toolsInfo ? (
												<div className={`${styles.envRow} ${styles.envLoadingRow}`}>正在检测语言运行时...</div>
											) : Array.isArray(toolsInfo?.runtimes) ? (
												toolsInfo!.runtimes.map((rt: RuntimeInfo) => (
													<div key={rt.name} className={styles.envRow}>
														<span className={styles.envLabel}>{rt.name}</span>
														{rt.found ? (
															<span className={styles.envToolFound}>
																<code className={styles.envValue}>{rt.version || "已安装"}</code>
																{rt.path ? <span className={styles.envToolPath}>{rt.path}</span> : null}
															</span>
														) : (
															<span className={styles.envToolMissing}>未安装</span>
														)}
													</div>
												))
											) : null}
										</div>
									)}
								</div>
								{statusText && activePane === "env" ? (
									<div className={`${styles.status} ${statusClass}`}>{statusText}</div>
								) : null}
							</section>
						</div>
					</div>
				</div>
			</main>
		</div>
	);
}

export default SettingsPage;
