{"repo": "aragonaragon/aragon-write", "files": {"frontend/src/components/Settings.jsx": {"0963d89ba3a7b54153700b6c57c236a117b4e102": {"t": 1785746435.0123992, "n": 20036, "h": "01706b9a88338fc6", "c": "import { useEffect, useState } from \"react\";\nimport { X, RefreshCw, CheckCircle, AlertCircle, Power, Play, Eye, EyeOff, Cloud, HardDrive, Plug } from \"lucide-react\";\n\n// Built-in presets for popular OpenAI-compatible providers.\n// Users can edit Base URL/Model after applying, or fill them in manually.\nconst PROVIDER_PRESETS = {\n groq: {\n label: \"Groq\",\n baseUrl: \"https://api.groq.com/openai/v1\",\n model: \"llama-3.3-70b-versatile\",\n hint: \"مجاني وسريع — احصل على المفتاح من console.groq.com\",\n },\n openrouter: {\n label: \"OpenRouter\",\n baseUrl: \"https://openrouter.ai/api/v1\",\n model: \"google/gemini-2.0-flash-exp:free\",\n hint: \"موديلات كثيرة بسعر موحّد — openrouter.ai/keys\",\n },\n deepseek: {\n label: \"DeepSeek\",\n baseUrl: \"https://api.deepseek.com/v1\",\n model: \"deepseek-chat\",\n hint: \"رخيص وقوي بالعربي — platform.deepseek.com\",\n },\n};\n\nexport default function Settings({ settings, onUpdate, onClose, apiUrl, onOllamaStatusChange }) {\n const [models, setModels] = useState([]);\n const [loadingModels, setLoadingModels] = useState(false);\n const [modelError, setModelError] = useState(null);\n const [ollamaUrl, setOllamaUrl] = useState(settings.ollamaUrl);\n const [testStatus, setTestStatus] = useState(null);\n const [ollamaAction, setOllamaAction] = useState(null); // \"killing\" | \"starting\" | null\n\n // External provider state\n const [showKey, setShowKey] = useState(false);\n const [extModels, setExtModels] = useState([]);\n const [loadingExtModels, setLoadingExtModels] = useState(false);\n const [extTest, setExtTest] = useState(null); // { ok, error } | null\n const [testingExt, setTestingExt] = useState(false);\n\n const fetchModels = async () => {\n setLoadingModels(true);\n setModelError(null);\n try {\n const res = await fetch(`${apiUrl}/models`);\n const data = await res.json();\n if (data.models?.length > 0) {\n setModels(data.models);\n setTestStatus(\"success\");\n } else {\n setModels([]);\n setModelError(\"لم يتم العثور على موديلات. تأكد من تشغيل Ollama وتحميل موديل.\");\n setTestStatus(\"error\");\n }\n } catch {\n setModelError(\"تعذر الاتصال بـ Ollama. تأكد من تشغيله على المنفذ الصحيح.\");\n setTestStatus(\"error\");\n } finally {\n setLoadingModels(false);\n }\n };\n\n useEffect(() => { fetchModels(); }, []); // eslint-disable-line\n\n const saveOllamaUrl = () => {\n onUpdate({ ollamaUrl });\n fetchModels();\n };\n\n const killOllama = async () => {\n setOllamaAction(\"killing\");\n try {\n await fetch(`${apiUrl}/ollama/kill`, { method: \"POST\" });\n setTestStatus(null);\n setModels([]);\n onOllamaStatusChange?.(\"error\");\n } finally {\n setOllamaAction(null);\n }\n };\n\n const startOllama = async () => {\n setOllamaAction(\"starting\");\n try {\n await fetch(`${apiUrl}/ollama/start`, { method: \"POST\" });\n setTimeout(() => {\n fetchModels();\n onOllamaStatusChange?.(\"online\");\n }, 3000);\n } finally {\n setTimeout(() => setOllamaAction(null), 3200);\n }\n };\n\n // ── External provider helpers ──\n const applyPreset = (key) => {\n const preset = PROVIDER_PRESETS[key];\n if (!preset) return;\n onUpdate({\n apiBaseUrl: preset.baseUrl,\n apiModel: preset.model,\n });\n setExtTest(null);\n setExtModels([]);\n };\n\n const fetchExternalModels = async () => {\n if (!settings.apiBaseUrl) {\n setExtTest({ ok: false, error: \"أدخل Base URL أولاً\" });\n return;\n }\n setLoadingExtModels(true);\n setExtTest(null);\n try {\n const res = await fetch(`${apiUrl}/models/external`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ baseUrl: settings.apiBaseUrl, apiKey: settings.apiKey }),\n });\n const data = await res.json();\n if (res.ok && data.models?.length > 0) {\n setExtModels(data.models);\n } else {\n setExtModels([]);\n setExtTest({ ok: false, error: data.error || \"لم يتم العثور على موديلات\" });\n }\n } catch (err) {\n setExtTest({ ok: false, error: err.message || \"فشل جلب الموديلات\" });\n } finally {\n setLoadingExtModels(false);\n }\n };\n\n const testExternalConnection = async () => {\n if (!settings.apiBaseUrl) {\n setExtTest({ ok: false, error: \"أدخل Base URL أولاً\" });\n return;\n }\n setTestingExt(true);\n setExtTest(null);\n try {\n const res = await fetch(`${apiUrl}/health/provider`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ baseUrl: settings.apiBaseUrl, apiKey: settings.apiKey }),\n });\n const data = await res.json();\n setExtTest(data);\n } catch (err) {\n setExtTest({ ok: false, error: err.message || \"تعذّر الاتصال\" });\n } finally {\n setTestingExt(false);\n }\n };\n\n return (\n