import React, { useEffect, useState } from "react"; import { StyleSheet, Text, View } from "react-native"; import { radius, text } from "../../theme/tokens"; import { useTheme } from "../../theme/useTheme"; import { Button } from "../../components/Button"; import { ErrorNotice, Field, ManageShell } from "../../components/ManageShell"; import { Select } from "../../components/Select"; import { TextField } from "../../components/TextField"; import { Card, Mono, SectionLabel } from "../../components/primitives"; import { useAppState } from "../../state/AppState"; import { useResource } from "../../state/useResource"; import type { ClaudePermissions } from "../../api/client"; /** * Permissions — the stored overrides merged over the server's env baseline into * every generated settings.json, the mobile counterpart of the web dashboard's * PermissionsPanel. Headless runs auto-deny anything that would prompt, so * allow rules are what let work proceed. */ const MODE_OPTIONS = [ { value: "", label: "(keep server baseline)" }, { value: "default", label: "default" }, { value: "acceptEdits", label: "acceptEdits" }, { value: "plan", label: "plan" }, { value: "bypassPermissions", label: "bypassPermissions" }, ]; interface Draft { mode: string; allow: string; deny: string; ask: string; } /* One rule per line → trimmed, blank-free list. */ function parseLines(s: string): string[] { return s .split("\n") .map((l) => l.trim()) .filter(Boolean); } export function PermissionsScreen() { const { colors } = useTheme(); const { client } = useAppState(); const { data, error: loadError, loading, reload } = useResource("/claude/permissions"); const [form, setForm] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); // Seed the form from the loaded permissions once; afterwards the operator's draft wins. useEffect(() => { if (form === null && data !== null) { setForm({ mode: data.default_mode ?? "", allow: data.allow.join("\n"), deny: data.deny.join("\n"), ask: data.ask.join("\n"), }); } }, [data, form]); async function save() { if (!client || !form) return; setError(null); setBusy(true); try { await client.api("/claude/permissions", { method: "PUT", body: { default_mode: form.mode || null, allow: parseLines(form.allow), deny: parseLines(form.deny), ask: parseLines(form.ask), }, }); reload(); } catch (e) { setError(e instanceof Error ? e.message : "Couldn’t save permissions."); } finally { setBusy(false); } } return ( {data === null || form === null ? ( {loading ? "Loading permissions…" : "Permissions unavailable."} ) : ( <> Server baseline (env) {`mode: ${data.base_mode}`} {data.base_allow.length > 0 ? ( data.base_allow.map((rule) => ( {rule} )) ) : ( (no baseline allow rules) )}