Spaces:
Sleeping
Sleeping
| import { AlertTriangle } from "lucide-react" | |
| import { useDashboard } from "@/lib/dashboard" | |
| import { Label } from "@/components/ui/label" | |
| import { Input } from "@/components/ui/input" | |
| import { Button } from "@/components/ui/button" | |
| import { Switch } from "@/components/ui/switch" | |
| import { Separator } from "@/components/ui/separator" | |
| import { cn } from "@/lib/utils" | |
| import type { CompilerProvider } from "@/lib/types" | |
| const PROVIDERS: { id: CompilerProvider; label: string; placeholder: string }[] = [ | |
| { id: "replicate", label: "Replicate", placeholder: "r8_..." }, | |
| { id: "openai", label: "OpenAI", placeholder: "sk-..." }, | |
| { id: "anthropic", label: "Claude", placeholder: "sk-ant-..." }, | |
| ] | |
| const MODELS = { | |
| openai: [ | |
| { value: "gpt-5.5", label: "GPT-5.5" }, | |
| { value: "gpt-5.4", label: "GPT-5.4" }, | |
| { value: "gpt-5.4-mini", label: "GPT-5.4 mini" }, | |
| ], | |
| anthropic: [ | |
| { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" }, | |
| { value: "claude-opus-4-8", label: "Claude Opus 4.8" }, | |
| { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5" }, | |
| ], | |
| } | |
| export function SettingsPanel() { | |
| const { | |
| params, | |
| setParam, | |
| compiler, | |
| setCompilerProvider, | |
| setCompilerApiKey, | |
| setCompilerModel, | |
| } = useDashboard() | |
| const activeProvider = PROVIDERS.find((provider) => provider.id === compiler.provider) ?? PROVIDERS[0] | |
| const apiKey = | |
| compiler.provider === "replicate" | |
| ? compiler.replicateApiKey | |
| : compiler.provider === "openai" | |
| ? compiler.openaiApiKey | |
| : compiler.anthropicApiKey | |
| const model = | |
| compiler.provider === "openai" | |
| ? compiler.openaiModel | |
| : compiler.provider === "anthropic" | |
| ? compiler.anthropicModel | |
| : "" | |
| const modelProvider = | |
| compiler.provider === "openai" || compiler.provider === "anthropic" | |
| ? compiler.provider | |
| : null | |
| return ( | |
| <div className="space-y-5"> | |
| {/* webhook dispatch */} | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <Label className="text-foreground">Webhook dispatch</Label> | |
| <p className="mt-0.5 text-[0.6875rem] text-muted-foreground"> | |
| Off by default — actions only simulate. | |
| </p> | |
| </div> | |
| <Switch | |
| checked={params.enableWebhooks} | |
| onCheckedChange={(v) => setParam("enableWebhooks", v)} | |
| /> | |
| </div> | |
| <div | |
| className={cn( | |
| "transition-opacity", | |
| params.enableWebhooks ? "opacity-100" : "pointer-events-none opacity-40", | |
| )} | |
| > | |
| <Label htmlFor="webhook">Webhook URL</Label> | |
| <Input | |
| id="webhook" | |
| value={params.webhookUrl} | |
| onChange={(e) => setParam("webhookUrl", e.target.value)} | |
| className="mt-1.5 font-mono text-xs" | |
| placeholder="http://127.0.0.1:8123/api/webhook/example" | |
| /> | |
| </div> | |
| {params.enableWebhooks && ( | |
| <div className="flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[0.6875rem] text-warning reveal"> | |
| <AlertTriangle className="mt-0.5 size-3.5 shrink-0" /> | |
| Live POSTs are enabled. Matching rules will hit the URL above for real. | |
| </div> | |
| )} | |
| </div> | |
| <Separator /> | |
| <div className="space-y-1.5"> | |
| <div className="font-mono text-[0.625rem] uppercase tracking-[0.16em] text-muted-foreground"> | |
| rule compiler | |
| </div> | |
| <div className="grid grid-cols-3 gap-1 rounded-lg border border-border bg-black/20 p-1"> | |
| {PROVIDERS.map((provider) => ( | |
| <Button | |
| key={provider.id} | |
| type="button" | |
| size="sm" | |
| variant={compiler.provider === provider.id ? "default" : "ghost"} | |
| onClick={() => setCompilerProvider(provider.id)} | |
| > | |
| {provider.label} | |
| </Button> | |
| ))} | |
| </div> | |
| <div className="space-y-1.5"> | |
| <Label htmlFor="compiler-api-key">{activeProvider.label} API key</Label> | |
| <Input | |
| id="compiler-api-key" | |
| type="password" | |
| value={apiKey} | |
| onChange={(e) => setCompilerApiKey(compiler.provider, e.target.value)} | |
| placeholder={activeProvider.placeholder} | |
| className="font-mono text-xs" | |
| /> | |
| </div> | |
| {modelProvider && ( | |
| <div className="space-y-1.5"> | |
| <Label htmlFor="compiler-model">{activeProvider.label} model</Label> | |
| <select | |
| id="compiler-model" | |
| value={model} | |
| onChange={(e) => setCompilerModel(modelProvider, e.target.value)} | |
| className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" | |
| > | |
| {MODELS[modelProvider].map((option) => ( | |
| <option key={option.value} value={option.value}> | |
| {option.label} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| )} | |
| <p className="text-[0.6875rem] text-muted-foreground"> | |
| Keys pasted here are sent only when compiling. Space owners can also set provider | |
| secrets as environment variables. | |
| </p> | |
| </div> | |
| </div> | |
| ) | |
| } | |