import { useState } from "react" import type { ReactNode } from "react" import { useTranslation } from "react-i18next" import { type CoreConfigForm, DM_SCOPE_OPTIONS, type LauncherForm, } from "@/components/config/form-model" import { Field, SwitchCardField } from "@/components/shared-form" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Textarea } from "@/components/ui/textarea" type UpdateCoreField = ( key: K, value: CoreConfigForm[K], ) => void type UpdateLauncherField = ( key: K, value: LauncherForm[K], ) => void interface ConfigSectionCardProps { title: string description?: string children: ReactNode } function ConfigSectionCard({ title, description, children, }: ConfigSectionCardProps) { return ( {title} {description && {description}}
{children}
) } interface AgentDefaultsSectionProps { form: CoreConfigForm onFieldChange: UpdateCoreField } export function AgentDefaultsSection({ form, onFieldChange, }: AgentDefaultsSectionProps) { const { t } = useTranslation() return ( onFieldChange("workspace", e.target.value)} placeholder="~/.picoclaw/workspace" /> onFieldChange("restrictToWorkspace", checked) } /> onFieldChange("splitOnMarker", checked)} /> onFieldChange("toolFeedbackEnabled", checked) } /> {form.toolFeedbackEnabled && ( onFieldChange("toolFeedbackMaxArgsLength", e.target.value) } /> )} onFieldChange("maxTokens", e.target.value)} /> onFieldChange("contextWindow", e.target.value)} placeholder="131072" /> onFieldChange("maxToolIterations", e.target.value)} /> onFieldChange("summarizeMessageThreshold", e.target.value) } /> onFieldChange("summarizeTokenPercent", e.target.value) } /> ) } interface ExecSectionProps { form: CoreConfigForm onFieldChange: UpdateCoreField } export function ExecSection({ form, onFieldChange }: ExecSectionProps) { const { t } = useTranslation() const [testCommand, setTestCommand] = useState("") const [testResult, setTestResult] = useState<{ allowed: boolean blocked: boolean matchedWhitelist: string | null matchedBlacklist: string | null } | null>(null) const [isLoading, setIsLoading] = useState(false) const testPatterns = async () => { if (!testCommand.trim()) { setTestResult(null) return } const allowPatterns = form.customAllowPatternsText .split("\n") .map((p) => p.trim()) .filter((p) => p.length > 0) const denyPatterns = form.enableDenyPatterns ? form.customDenyPatternsText .split("\n") .map((p) => p.trim()) .filter((p) => p.length > 0) : [] setIsLoading(true) try { const res = await fetch("/api/config/test-command-patterns", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ allow_patterns: allowPatterns, deny_patterns: denyPatterns, command: testCommand, }), }) const data = await res.json() setTestResult({ allowed: data.allowed, blocked: data.blocked, matchedWhitelist: data.matched_whitelist ?? null, matchedBlacklist: data.matched_blacklist ?? null, }) } catch { setTestResult(null) } finally { setIsLoading(false) } } return ( onFieldChange("execEnabled", checked)} /> {form.execEnabled && ( <> onFieldChange("allowRemote", checked)} /> onFieldChange("enableDenyPatterns", checked) } /> {form.enableDenyPatterns && (