"use client"; import { invoke } from "@tauri-apps/api/core"; import { Eye, EyeOff } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useWayfernTerms } from "@/hooks/use-wayfern-terms"; import { showErrorToast, showSuccessToast } from "@/lib/toast-utils"; import { CopyToClipboard } from "./ui/copy-to-clipboard"; interface AppSettings { api_enabled: boolean; api_port: number; api_token?: string; mcp_enabled: boolean; mcp_port?: number; mcp_token?: string; } interface McpConfig { port: number; token: string; } interface IntegrationsDialogProps { isOpen: boolean; onClose: () => void; } export function IntegrationsDialog({ isOpen, onClose, }: IntegrationsDialogProps) { const { t } = useTranslation(); const [settings, setSettings] = useState({ api_enabled: false, api_port: 10108, api_token: undefined, mcp_enabled: false, mcp_port: undefined, mcp_token: undefined, }); const [apiServerPort, setApiServerPort] = useState(null); const [mcpConfig, setMcpConfig] = useState(null); const [, setMcpRunning] = useState(false); const [showApiToken, setShowApiToken] = useState(false); const [showMcpToken, setShowMcpToken] = useState(false); const [isApiStarting, setIsApiStarting] = useState(false); const [isMcpStarting, setIsMcpStarting] = useState(false); const [mcpInClaudeDesktop, setMcpInClaudeDesktop] = useState(false); const [mcpInClaudeCode, setMcpInClaudeCode] = useState(false); const { termsAccepted } = useWayfernTerms(); const loadSettings = useCallback(async () => { try { const loaded = await invoke("get_app_settings"); setSettings(loaded); } catch (e) { console.error("Failed to load settings:", e); } }, []); const loadMcpConfig = useCallback(async () => { try { const config = await invoke("get_mcp_config"); setMcpConfig(config); } catch (e) { console.error("Failed to get MCP config:", e); } }, []); const loadMcpServerStatus = useCallback(async () => { try { const isRunning = await invoke("get_mcp_server_status"); setMcpRunning(isRunning); } catch (e) { console.error("Failed to get MCP server status:", e); } }, []); const loadApiServerStatus = useCallback(async () => { try { const port = await invoke("get_api_server_status"); setApiServerPort(port); } catch (e) { console.error("Failed to get API server status:", e); } }, []); const loadClaudeDesktopStatus = useCallback(async () => { try { const exists = await invoke("is_mcp_in_claude_desktop"); setMcpInClaudeDesktop(exists); } catch { // Not critical } }, []); const loadClaudeCodeStatus = useCallback(async () => { try { const exists = await invoke("is_mcp_in_claude_code"); setMcpInClaudeCode(exists); } catch { // Claude CLI may not be installed } }, []); useEffect(() => { if (isOpen) { void loadSettings(); void loadApiServerStatus(); void loadMcpConfig(); void loadMcpServerStatus(); void loadClaudeDesktopStatus(); void loadClaudeCodeStatus(); } }, [ isOpen, loadSettings, loadApiServerStatus, loadMcpConfig, loadMcpServerStatus, loadClaudeDesktopStatus, loadClaudeCodeStatus, ]); const handleApiToggle = async (enabled: boolean) => { setIsApiStarting(true); try { if (enabled) { const port = await invoke("start_api_server", { port: settings.api_port, }); setApiServerPort(port); const next = await invoke("save_app_settings", { settings: { ...settings, api_enabled: true }, }); setSettings(next); showSuccessToast(`API server started on port ${port}`); } else { await invoke("stop_api_server"); setApiServerPort(null); const next = await invoke("save_app_settings", { settings: { ...settings, api_enabled: false, api_token: null }, }); setSettings(next); showSuccessToast("API server stopped"); } } catch (e) { console.error("Failed to toggle API:", e); showErrorToast("Failed to toggle API server", { description: e instanceof Error ? e.message : "Unknown error", }); } finally { setIsApiStarting(false); } }; const handleMcpToggle = async (enabled: boolean) => { setIsMcpStarting(true); try { if (enabled) { const port = await invoke("start_mcp_server"); const next = await invoke("save_app_settings", { settings: { ...settings, mcp_enabled: true, mcp_port: port }, }); setSettings(next); void loadMcpConfig(); showSuccessToast(`MCP server started on port ${port}`); } else { await invoke("stop_mcp_server"); const next = await invoke("save_app_settings", { settings: { ...settings, mcp_enabled: false }, }); setSettings(next); setMcpConfig(null); showSuccessToast("MCP server stopped"); } } catch (e) { console.error("Failed to toggle MCP server:", e); showErrorToast("Failed to toggle MCP server", { description: e instanceof Error ? e.message : "Unknown error", }); } finally { setIsMcpStarting(false); } }; return ( { if (!open) onClose(); }} > Integrations
Local API MCP (AI Assistants)
void handleApiToggle(!!checked)} />

Allow managing profiles, groups, and proxies via REST API.

{settings.api_enabled && (
{ const val = Number.parseInt(e.target.value, 10); if (!Number.isNaN(val)) { setSettings({ ...settings, api_port: val }); } }} className="w-24 font-mono" min={1} max={65535} /> {apiServerPort && ( {t("common.status.running")} )}

Include in Authorization header: Bearer {""}

)}
void handleMcpToggle(!!checked)} />

Allow AI assistants like Claude Desktop to control browsers. {!termsAccepted && ( (Accept Wayfern terms in Settings first) )}

{mcpConfig && (

{t("integrations.mcp.claudeDesktopTitle")}

{mcpInClaudeDesktop ? ( ) : ( )}

{t("integrations.mcp.claudeCodeTitle")}

{mcpInClaudeCode ? ( ) : ( )}
)}
); }