diff --git a/frontend/src/agents/AgentsList.tsx b/frontend/src/agents/AgentsList.tsx index 4288ec21..079d7a56 100644 --- a/frontend/src/agents/AgentsList.tsx +++ b/frontend/src/agents/AgentsList.tsx @@ -400,6 +400,19 @@ function AgentSection({ onCreateFolder(name, currentFolderId || undefined); }; + // Must stay above the empty-state returns below: a hook after an early + // return is skipped on the render that takes it, which React rejects with + // "rendered fewer hooks than expected". Reachable now that each filter is + // its own route — landing straight on an empty one renders once while the + // data loads, then again once it arrives empty. + const breadcrumbItems = useMemo(() => { + if (!folders || folderPath.length === 0) return []; + return folderPath.map((folderId) => { + const folder = folders.find((f) => f.id === folderId); + return { id: folderId, name: folder?.name || '' }; + }); + }, [folders, folderPath]); + const hasNoAgentsAtAll = !isLoading && totalAgents === 0; const isSearchingWithNoResults = !isLoading && searchQuery && filteredAgents.length === 0 && totalAgents > 0; @@ -433,15 +446,6 @@ function AgentSection({ ); } - // Build breadcrumb items from folder path - const breadcrumbItems = useMemo(() => { - if (!folders || folderPath.length === 0) return []; - return folderPath.map((folderId) => { - const folder = folders.find((f) => f.id === folderId); - return { id: folderId, name: folder?.name || '' }; - }); - }, [folders, folderPath]); - return (
diff --git a/frontend/src/index.css b/frontend/src/index.css index d3bd0ffc..a9e6438d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -690,8 +690,11 @@ Avoid over-scrolling in mobile browsers font-family: var(--font-mono); } + /* Disclosure content, e.g. an expanded tool call. Short on purpose: the + panel's height appears at once, so a long fade reads as the content + lagging behind the layout rather than as a reveal. */ .fade-in { - animation: fadeIn 0.5s ease-in-out; + animation: fadeIn 0.16s ease-out; } @keyframes fadeIn { @@ -703,10 +706,13 @@ Avoid over-scrolling in mobile browsers } } + /* The start state lives in the keyframes, not here: with `opacity: 0` on + the element itself the content was only ever visible *because* an + animation had run to completion, so anything that stopped it running — + a cancelled animation, a reduced-motion reset — left the answer blank. + The element now rests visible and the animation is purely additive. */ .fade-in-bubble { - opacity: 0; - transform: translateY(10px); - animation: fadeInUp 0.5s forwards; + animation: fadeInUp 0.26s ease-out; } .shimmer-text { @@ -754,9 +760,19 @@ Avoid over-scrolling in mobile browsers .dark .shimmer-text { background-image: none; } + + /* Safe to drop outright now that both rest in their final state. */ + .fade-in, + .fade-in-bubble { + animation: none; + } } @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(6px); + } to { opacity: 1; transform: translateY(0);