'use client'; import { useEffect, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Save, Download, Upload, RefreshCw, Check, X } from 'lucide-react'; import CodeMirror from '@uiw/react-codemirror'; import { json } from '@codemirror/lang-json'; import { oneDark } from '@codemirror/theme-one-dark'; export function ConfigEditor() { const [config, setConfig] = useState(null); const [editing, setEditing] = useState(false); const [editValue, setEditValue] = useState(''); const [saving, setSaving] = useState(false); const [loading, setLoading] = useState(true); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const fetchConfig = async () => { setLoading(true); try { const response = await fetch('/api/config'); const data = await response.json(); setConfig(data); setEditValue(JSON.stringify(data, null, 2)); } catch (error) { setMessage({ type: 'error', text: 'Failed to load config' }); } finally { setLoading(false); } }; useEffect(() => { fetchConfig(); }, []); const handleSave = async () => { setSaving(true); setMessage(null); try { // Validate JSON const parsed = JSON.parse(editValue); const response = await fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(parsed), }); const result = await response.json(); if (result.success) { setMessage({ type: 'success', text: result.message || 'Config saved successfully!' }); setConfig(parsed); setEditing(false); } else { setMessage({ type: 'error', text: result.message || 'Failed to save config' }); } } catch (error) { setMessage({ type: 'error', text: 'Invalid JSON format' }); } finally { setSaving(false); } }; const handleCancel = () => { setEditValue(JSON.stringify(config, null, 2)); setEditing(false); setMessage(null); }; const handleDownload = () => { const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'openclaw-config.json'; a.click(); URL.revokeObjectURL(url); }; const handleUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { try { const content = event.target?.result as string; JSON.parse(content); // Validate setEditValue(content); setEditing(true); setMessage(null); } catch { setMessage({ type: 'error', text: 'Invalid JSON file' }); } }; reader.readAsText(file); }; if (loading) { return (
); } return (
{/* Actions */}
{/* Message */} {message && ( {message.type === 'success' ? ( ) : ( )} {message.text} )} {/* Editor */} openclaw.json
{editing && (
)}

⚠️ Warning: Editing the config directly restarts the Gateway to apply changes.

); }