import { useEffect, useState } from "react"; import { ExternalLink, Info, Keyboard, Monitor, Puzzle, Server, Shield, Sparkles, Table, Zap, type LucideIcon, } from "lucide-react"; import { driverStatus, health, type DriverStatusItem } from "./api"; import { OpsModal } from "./OpsModal"; import { AiSettings } from "./AiPanel"; import { McpSettings } from "./McpSettings"; import { DriverManager } from "./DriverManager"; import appPackage from "../package.json"; import { useWorkspaceStore } from "./store"; import { openExternal } from "./openExternal"; const IS_MAC = typeof navigator !== "undefined" && /Mac/i.test(navigator.platform); const MOD = IS_MAC ? "⌘" : "Ctrl"; const WECHAT_GROUP_URL = "https://docs.qq.com/doc/DTFpxcndud0xPd1hX"; const DRIVER_STORE_URL = "https://gitee.com/attacker/opsxdb-driver"; type TabId = "general" | "execution" | "results" | "ai" | "mcp" | "security" | "drivers" | "shortcuts" | "about"; const TABS: Array<{ id: TabId; icon: LucideIcon; label: string; desc: string }> = [ { id: "general", icon: Monitor, label: "常规", desc: "主题与界面" }, { id: "execution", icon: Zap, label: "执行", desc: "超时与多语句" }, { id: "results", icon: Table, label: "结果", desc: "面板与分页" }, { id: "ai", icon: Sparkles, label: "AI 助手", desc: "模型、API Key 与连接测试" }, { id: "mcp", icon: Server, label: "MCP", desc: "安装检测、只读与客户端配置" }, { id: "security", icon: Shield, label: "安全与数据", desc: "本机存储与执行保护" }, { id: "drivers", icon: Puzzle, label: "驱动", desc: "agent 驱动状态" }, { id: "shortcuts", icon: Keyboard, label: "快捷键", desc: "键盘操作一览" }, { id: "about", icon: Info, label: "关于", desc: "版本与资源" }, ]; function Row({ label, children, hint }: { label: string; children: React.ReactNode; hint?: string }) { return (
{label} {children}
{hint &&
{hint}
}
); } function Toggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) { return ( ); } function Seg({ value, options, onChange }: { value: T; options: Array<{ v: T; label: string }>; onChange: (v: T) => void }) { return (
{options.map((o) => ( ))}
); } function AboutLink({ label, description, onClick }: { label: string; description: string; onClick: () => void }) { return ( ); } /** 设置弹框:左分类导航 + 右面板(对标 opsxshell / DBeaver Preferences) */ export function SettingsModal({ onClose }: { onClose: () => void }) { const [tab, setTab] = useState("general"); const theme = useWorkspaceStore((s) => s.theme); const showAi = useWorkspaceStore((s) => s.showAi); const stopOnError = useWorkspaceStore((s) => s.stopOnError); const execTimeoutMs = useWorkspaceStore((s) => s.execTimeoutMs); const resultRatio = useWorkspaceStore((s) => s.resultRatio); const set = useWorkspaceStore((s) => s.set); const notify = useWorkspaceStore((s) => s.notify); const [showDriverManager, setShowDriverManager] = useState(false); const [driverError, setDriverError] = useState(""); const [drivers, setDrivers] = useState(null); const [navSearch, setNavSearch] = useState(""); const [version, setVersion] = useState(""); const reloadDrivers = () => { setDriverError(""); setDrivers(null); void driverStatus().then(setDrivers).catch(() => setDriverError("驱动状态加载失败,请确认本机服务已启动。")); }; useEffect(() => { reloadDrivers(); void health().then((h) => setVersion(String((h as { version?: string }).version ?? ""))).catch(() => {}); }, []); const resetLayout = () => { set("leftWidth", 260); set("aiWidth", 280); set("resultRatio", 0.38); notify({ kind: "success", title: "已重置", message: "面板布局恢复默认", mode: "toast", durationMs: 1400 }); }; const visibleTabs = TABS.filter((t) => (t.label + t.desc + (t.id === "general" ? "主题 深色 浅色 布局" : "") + (t.id === "mcp" ? "mcp cursor claude npm 只读 白名单" : "") + (t.id === "ai" ? "deepseek 模型 agent" : "")).toLowerCase().includes(navSearch.trim().toLowerCase())); const activeTab = visibleTabs.find((t) => t.id === tab) ?? visibleTabs[0]; const active = activeTab?.id; return ( <>
{/* 左导航:搜索 + 线框图标 + 主标题/副文案(对齐 opsxshell) */} {/* 右面板 */}
{activeTab ?

{activeTab.label}

:

没有匹配的设置

} {active === "ai" && setTab("general")} />} {active === "mcp" && } {active === "security" &&
始终开启本机存储按需发送
} {active === "general" && ( <> set("theme", v)} /> set("showAi", v)} /> )} {active === "execution" && ( <> set("execTimeoutMs", v)} /> set("stopOnError", v)} /> 始终开启 )} {active === "results" && ( <> set("resultRatio", Number(e.target.value) / 100)} style={{ accentColor: "var(--ops-accent)", width: 120 }} /> 1000 / 10000 )} {active === "drivers" && ( <>
{driverError &&
{driverError}
} {drivers?.length === 0 &&

暂无需要管理的驱动。

} {!driverError && drivers === null &&
加载中…
} {drivers?.map((d) => (
{d.id} {d.allInstalled ? "已安装" : d.license === "proprietary" ? "需自备 jar" : "未安装"}
))}
)} {active === "shortcuts" && (
{[ [`${MOD} + Enter`, "执行当前语句或选区"], [`${MOD} + T`, "新建查询"], [`${MOD} + W`, "关闭查询"], [`${MOD} + 1-9`, "切换到第 N 个查询"], ["Ctrl + Tab", "循环切换查询"], [`${MOD} + ⇧ + F`, "聚焦对象树过滤框"], [`${MOD} + B`, "显示/隐藏 AI"], [`${MOD} + /`, "注释 / 取消注释"], [`${MOD} + ⇧ + /`, "快捷键帮助"], ["双击表名", "打开前 100 行"], ["双击单元格", "进入行编辑"], ].map(([k, v]) => (
{k} {v}
))}
)} {active === "about" && (

OpsxDB {appPackage.version} 桌面客户端 本机服务 {version || "未连接"}

资源
void openExternal(WECHAT_GROUP_URL)} /> void openExternal(DRIVER_STORE_URL)} />
)}
{showDriverManager && { setShowDriverManager(false); reloadDrivers(); }} />} ); }