From 8954512dad5ab23e1c27ba56f3dd2b04a249af88 Mon Sep 17 00:00:00 2001 From: dotta Date: Tue, 17 Mar 2026 16:44:07 -0500 Subject: [PATCH] Fix prompts page render loop Stabilize prompt file tree expansion state so the prompts editor no longer loops into maximum update depth when loading the bundle. Also replace bundle and file loading placeholders with skeleton UI. Co-Authored-By: Paperclip --- ui/src/pages/AgentDetail.tsx | 74 ++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 554d271e..dd434ffb 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -34,6 +34,7 @@ import { ScrollToBottom } from "../components/ScrollToBottom"; import { formatCents, formatDate, relativeTime, formatTokens, visibleRunCostUsd } from "../lib/utils"; import { cn } from "../lib/utils"; import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; import { Tabs } from "@/components/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { @@ -217,6 +218,14 @@ function usageNumber(usage: Record | null, ...keys: string[]) { return 0; } +function setsEqual(left: Set, right: Set) { + if (left.size !== right.size) return false; + for (const value of left) { + if (!right.has(value)) return false; + } + return true; +} + function runMetrics(run: HeartbeatRun) { const usage = (run.usageJson ?? null) as Record | null; const result = (run.resultJson ?? null) as Record | null; @@ -1517,7 +1526,10 @@ function PromptsTab({ const currentMode = bundleDraft?.mode ?? bundle?.mode ?? "managed"; const currentEntryFile = bundleDraft?.entryFile ?? bundle?.entryFile ?? "AGENTS.md"; const currentRootPath = bundleDraft?.rootPath ?? bundle?.rootPath ?? ""; - const fileOptions = bundle?.files.map((file) => file.path) ?? []; + const fileOptions = useMemo( + () => bundle?.files.map((file) => file.path) ?? [], + [bundle], + ); const visibleFilePaths = useMemo( () => [...new Set([currentEntryFile, ...fileOptions])], [currentEntryFile, fileOptions], @@ -1606,7 +1618,7 @@ function PromptsTab({ nextExpanded.add(currentPath); } } - setExpandedDirs(nextExpanded); + setExpandedDirs((current) => (setsEqual(current, nextExpanded) ? current : nextExpanded)); }, [visibleFilePaths]); useEffect(() => { @@ -1712,7 +1724,7 @@ function PromptsTab({ } if (bundleLoading && !bundle) { - return
Loading instructions bundle…
; + return ; } return ( @@ -1922,7 +1934,7 @@ function PromptsTab({ {selectedFileExists && fileLoading && !selectedFileDetail ? ( -

Loading file…

+ ) : isMarkdown(selectedOrEntryFile) ? ( +
+
+
+ + +
+ +
+
+ {Array.from({ length: 3 }).map((_, index) => ( +
+ + +
+ ))} +
+
+
+
+
+ + +
+ +
+ {Array.from({ length: 5 }).map((_, index) => ( + + ))} +
+
+
+
+ + +
+ +
+
+ + ); +} + +function PromptEditorSkeleton() { + return ( +
+ + +
+ ); +} + function AgentSkillsTab({ agent, companyId,