"use client" import { Moon, Sun } from "lucide-react" import { useEffect, useState } from "react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Switch } from "@/components/ui/switch" interface SettingsDialogProps { open: boolean onOpenChange: (open: boolean) => void onCloseProtectionChange?: (enabled: boolean) => void drawioUi: "min" | "sketch" onToggleDrawioUi: () => void darkMode: boolean onToggleDarkMode: () => void } export const STORAGE_ACCESS_CODE_KEY = "next-ai-draw-io-access-code" export const STORAGE_CLOSE_PROTECTION_KEY = "next-ai-draw-io-close-protection" const STORAGE_ACCESS_CODE_REQUIRED_KEY = "next-ai-draw-io-access-code-required" export const STORAGE_AI_PROVIDER_KEY = "next-ai-draw-io-ai-provider" export const STORAGE_AI_BASE_URL_KEY = "next-ai-draw-io-ai-base-url" export const STORAGE_AI_API_KEY_KEY = "next-ai-draw-io-ai-api-key" export const STORAGE_AI_MODEL_KEY = "next-ai-draw-io-ai-model" function getStoredAccessCodeRequired(): boolean | null { if (typeof window === "undefined") return null const stored = localStorage.getItem(STORAGE_ACCESS_CODE_REQUIRED_KEY) if (stored === null) return null return stored === "true" } export function SettingsDialog({ open, onOpenChange, onCloseProtectionChange, drawioUi, onToggleDrawioUi, darkMode, onToggleDarkMode, }: SettingsDialogProps) { const [accessCode, setAccessCode] = useState("") const [closeProtection, setCloseProtection] = useState(true) const [isVerifying, setIsVerifying] = useState(false) const [error, setError] = useState("") const [accessCodeRequired, setAccessCodeRequired] = useState( () => getStoredAccessCodeRequired() ?? false, ) const [provider, setProvider] = useState("") const [baseUrl, setBaseUrl] = useState("") const [apiKey, setApiKey] = useState("") const [modelId, setModelId] = useState("") useEffect(() => { // Only fetch if not cached in localStorage if (getStoredAccessCodeRequired() !== null) return fetch("/api/config") .then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) return res.json() }) .then((data) => { const required = data?.accessCodeRequired === true localStorage.setItem( STORAGE_ACCESS_CODE_REQUIRED_KEY, String(required), ) setAccessCodeRequired(required) }) .catch(() => { // Don't cache on error - allow retry on next mount setAccessCodeRequired(false) }) }, []) useEffect(() => { if (open) { const storedCode = localStorage.getItem(STORAGE_ACCESS_CODE_KEY) || "" setAccessCode(storedCode) const storedCloseProtection = localStorage.getItem( STORAGE_CLOSE_PROTECTION_KEY, ) // Default to true if not set setCloseProtection(storedCloseProtection !== "false") // Load AI provider settings setProvider(localStorage.getItem(STORAGE_AI_PROVIDER_KEY) || "") setBaseUrl(localStorage.getItem(STORAGE_AI_BASE_URL_KEY) || "") setApiKey(localStorage.getItem(STORAGE_AI_API_KEY_KEY) || "") setModelId(localStorage.getItem(STORAGE_AI_MODEL_KEY) || "") setError("") } }, [open]) const handleSave = async () => { if (!accessCodeRequired) return setError("") setIsVerifying(true) try { const response = await fetch("/api/verify-access-code", { method: "POST", headers: { "x-access-code": accessCode.trim(), }, }) const data = await response.json() if (!data.valid) { setError(data.message || "Invalid access code") return } localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim()) onOpenChange(false) } catch { setError("Failed to verify access code") } finally { setIsVerifying(false) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault() handleSave() } } return ( Settings Configure your application settings.
{accessCodeRequired && (
setAccessCode(e.target.value) } onKeyDown={handleKeyDown} placeholder="Enter access code" autoComplete="off" />

Required to use this application.

{error && (

{error}

)}
)}

Use your own API key to bypass usage limits. Your key is stored locally in your browser and is never stored on the server.

{provider && provider !== "default" && ( <>
{ setModelId(e.target.value) localStorage.setItem( STORAGE_AI_MODEL_KEY, e.target.value, ) }} placeholder={ provider === "openai" ? "e.g., gpt-4o" : provider === "anthropic" ? "e.g., claude-sonnet-4-5" : provider === "google" ? "e.g., gemini-2.0-flash-exp" : provider === "deepseek" ? "e.g., deepseek-chat" : "Model ID" } />
{ setApiKey(e.target.value) localStorage.setItem( STORAGE_AI_API_KEY_KEY, e.target.value, ) }} placeholder="Your API key" autoComplete="off" />

Overrides{" "} {provider === "openai" ? "OPENAI_API_KEY" : provider === "anthropic" ? "ANTHROPIC_API_KEY" : provider === "google" ? "GOOGLE_GENERATIVE_AI_API_KEY" : provider === "azure" ? "AZURE_API_KEY" : provider === "openrouter" ? "OPENROUTER_API_KEY" : provider === "deepseek" ? "DEEPSEEK_API_KEY" : provider === "siliconflow" ? "SILICONFLOW_API_KEY" : "server API key"}

{ setBaseUrl(e.target.value) localStorage.setItem( STORAGE_AI_BASE_URL_KEY, e.target.value, ) }} placeholder={ provider === "anthropic" ? "https://api.anthropic.com/v1" : provider === "siliconflow" ? "https://api.siliconflow.com/v1" : "Custom endpoint URL" } />
)}

Dark/Light mode for interface and DrawIO canvas.

Canvas style:{" "} {drawioUi === "min" ? "Minimal" : "Sketch"}

Show confirmation when leaving the page.

{ setCloseProtection(checked) localStorage.setItem( STORAGE_CLOSE_PROTECTION_KEY, checked.toString(), ) onCloseProtectionChange?.(checked) }} />
) }