From 6000bb4ee2a9d8968c3ff06ed7042f4e68383ff0 Mon Sep 17 00:00:00 2001 From: dotta Date: Wed, 18 Mar 2026 14:59:14 -0500 Subject: [PATCH] Fix new file creation on Instructions tab silently failing New files created via the "+" button were not appearing in the file tree because visibleFilePaths was derived solely from bundle API data. The selection reset effect would also immediately undo the file selection. Add pendingFiles state to track newly created files until they are saved to disk. Include pending files in visibleFilePaths, guard the selection reset effect, and clean up pending state after successful save. Fixes PAP-563 Co-Authored-By: Paperclip --- ui/src/pages/AgentDetail.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 40c08ca8..fa3d05b9 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -897,8 +897,8 @@ export function AgentDetail() { items={[ { value: "dashboard", label: "Dashboard" }, { value: "instructions", label: "Instructions" }, - { value: "configuration", label: "Configuration" }, { value: "skills", label: "Skills" }, + { value: "configuration", label: "Configuration" }, { value: "runs", label: "Runs" }, { value: "budget", label: "Budget" }, ]} @@ -1515,6 +1515,7 @@ function PromptsTab({ } | null>(null); const [newFilePath, setNewFilePath] = useState(""); const [showNewFileInput, setShowNewFileInput] = useState(false); + const [pendingFiles, setPendingFiles] = useState([]); const [expandedDirs, setExpandedDirs] = useState>(new Set()); const [filePanelWidth, setFilePanelWidth] = useState(260); const containerRef = useRef(null); @@ -1559,9 +1560,9 @@ function PromptsTab({ ); const visibleFilePaths = useMemo( () => bundleMatchesDraft - ? [...new Set([currentEntryFile, ...fileOptions])] - : [currentEntryFile], - [bundleMatchesDraft, currentEntryFile, fileOptions], + ? [...new Set([currentEntryFile, ...fileOptions, ...pendingFiles])] + : [currentEntryFile, ...pendingFiles], + [bundleMatchesDraft, currentEntryFile, fileOptions, pendingFiles], ); const fileTree = useMemo( () => buildFileTree(Object.fromEntries(visibleFilePaths.map((filePath) => [filePath, ""]))), @@ -1598,6 +1599,7 @@ function PromptsTab({ agentsApi.saveInstructionsFile(agent.id, data, companyId), onMutate: () => setAwaitingRefresh(true), onSuccess: (_, variables) => { + setPendingFiles((prev) => prev.filter((f) => f !== variables.path)); queryClient.invalidateQueries({ queryKey: queryKeys.agents.instructionsBundle(agent.id) }); queryClient.invalidateQueries({ queryKey: queryKeys.agents.instructionsFile(agent.id, variables.path) }); queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.id) }); @@ -1636,10 +1638,10 @@ function PromptsTab({ if (selectedFile !== bundle.entryFile) setSelectedFile(bundle.entryFile); return; } - if (!availablePaths.includes(selectedFile) && selectedFile !== currentEntryFile) { + if (!availablePaths.includes(selectedFile) && selectedFile !== currentEntryFile && !pendingFiles.includes(selectedFile)) { setSelectedFile(availablePaths.includes(bundle.entryFile) ? bundle.entryFile : availablePaths[0]!); } - }, [bundle, bundleMatchesDraft, currentEntryFile, selectedFile]); + }, [bundle, bundleMatchesDraft, currentEntryFile, pendingFiles, selectedFile]); useEffect(() => { const nextExpanded = new Set(); @@ -1998,6 +2000,7 @@ function PromptsTab({ onClick={() => { const candidate = newFilePath.trim(); if (!candidate || candidate.includes("..")) return; + setPendingFiles((prev) => prev.includes(candidate) ? prev : [...prev, candidate]); setSelectedFile(candidate); setDraft(""); setNewFilePath("");