import { useMemo, useState } from "react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import type { AgentProfile, McpToolCatalogue, McpToolPolicy, NetworkPolicy, SystemPermissionSet, Template, } from "@/domain"; import { mcpToolGroupLabel, mcpToolGroupDescription, mcpToolGroupsForCatalogue, mcpToolLabel, } from "@/features/permissions/mcpToolGroups"; import { Button, Input, SmallDropdown, cn } from "@/shared"; import { useI18n } from "@/i18n"; import { profileLabel } from "./templatePresentation"; export interface TemplateEditorSaveInput { name: string; content: string; defaultProfileId: string; defaultAgentName: string | null; defaultContactable: boolean; systemPermissions: SystemPermissionSet | null; mcpToolPermissions: McpToolPolicy | null; } export interface TemplateEditorProps { template?: Template | null; profiles: AgentProfile[]; mcpCatalogue: McpToolCatalogue; onSave: (input: TemplateEditorSaveInput) => Promise; onClose: () => void; busy?: boolean; } type EditorTab = "edit" | "preview"; export function TemplateEditor({ template, profiles, mcpCatalogue, onSave, onClose, busy = false, }: TemplateEditorProps) { const { tr } = useI18n(); const networkOptions: { value: "" | NetworkPolicy; label: string }[] = [ { value: "", label: tr("Aucune permission système", "No system permission") }, { value: "ask", label: tr("Réseau sur demande", "Ask for network") }, { value: "allow", label: tr("Réseau autorisé", "Network allowed") }, { value: "deny", label: tr("Réseau refusé", "Network denied") }, ]; const [name, setName] = useState(template?.name ?? ""); const [content, setContent] = useState(template?.contentMd ?? ""); const [defaultAgentName, setDefaultAgentName] = useState( template?.defaultAgentName ?? "", ); const [defaultProfileId, setDefaultProfileId] = useState( template?.defaultProfileId ?? "", ); const [network, setNetwork] = useState<"" | NetworkPolicy>( template?.systemPermissions?.network ?? "", ); const [defaultContactable, setDefaultContactable] = useState( template?.defaultContactable ?? false, ); const [allowedTools, setAllowedTools] = useState( template?.mcpToolPermissions?.allowedTools ?? [], ); const [mcpOverrideActive, setMcpOverrideActive] = useState( template?.mcpToolPermissions != null, ); const [tab, setTab] = useState("edit"); const toolGroups = useMemo( () => mcpToolGroupsForCatalogue(mcpCatalogue), [mcpCatalogue], ); const allMcpTools = useMemo( () => toolGroups.flatMap((group) => group.tools), [toolGroups], ); const allowed = useMemo(() => new Set(allowedTools), [allowedTools]); const canSave = name.trim().length > 0 && !busy; function toggleTool(tool: string) { setAllowedTools((prev) => prev.includes(tool) ? prev.filter((candidate) => candidate !== tool) : [...prev, tool], ); } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!canSave) return; await onSave({ name: name.trim(), content, defaultProfileId, defaultAgentName: defaultAgentName.trim() || null, defaultContactable, systemPermissions: network ? { network } : null, mcpToolPermissions: mcpOverrideActive ? { allowedTools: [...allowedTools] } : null, }); } return (

{template ? tr(`Modifier le modèle — ${template.name}`, `Edit template — ${template.name}`) : tr("Nouveau modèle", "New template")}

void handleSubmit(e)} className="flex min-h-0 flex-1 flex-col overflow-hidden" >
setName(e.target.value)} disabled={busy} />
{(["edit", "preview"] as EditorTab[]).map((t) => ( ))}
{tab === "edit" ? (