Skip to content

feat(frontend): redesign sidebar layout with improved UX - #12829

Closed
Abhi1992002 wants to merge 29 commits into
devfrom
Abhi1992002/update-layout
Closed

feat(frontend): redesign sidebar layout with improved UX#12829
Abhi1992002 wants to merge 29 commits into
devfrom
Abhi1992002/update-layout

Conversation

@Abhi1992002

Copy link
Copy Markdown
Member

Why / What / How

Why: The current sidebar layout needed a UX refresh — the collapsed state lacked discoverability (no trigger swap), the mobile nav used a separate MobileNavBar instead of the sidebar, and there was no collapsible task list.

What: Redesigns the AppSidebar with a cleaner collapsed/expanded experience, replaces the mobile MobileNavBar with a SidebarTrigger-based approach, and adds an animated accordion for the "All tasks" section.

How:

  • Collapsed state: logo swaps to a SidebarTrigger on hover (overlay pattern)
  • Expanded state: full logo + AgentActivityDropdown + SidebarTrigger in header
  • Dynamic content wrapped in a collapsible "All tasks" accordion with Framer Motion
  • Footer hides NotificationToggle and Feedback when collapsed (only Usage + Settings remain)
  • Mobile: replaced MobileNavBar/MobileDrawer/MobileHeader with a simple SidebarTrigger + Wallet fixed bar
  • Removed old copilot MobileDrawer and MobileHeader renders

Changes 🏗️

  • AppSidebar.tsx — Redesigned header (logo/trigger swap on collapse), filtered nav links by showWhenCollapsed, added animated "All tasks" accordion, simplified footer for collapsed state
  • Navbar.tsx — Replaced MobileNavBar with SidebarTrigger + Wallet for mobile; removed old nav imports
  • CopilotPage.tsx — Removed MobileHeader and MobileDrawer renders and related state
  • ChatSessionList.tsx — Removed standalone "All tasks" title (now in AppSidebar accordion)
  • use-get-flag.ts — No changes (clean)

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • Verify sidebar opens/closes correctly on desktop
    • Verify collapsed state shows logo that swaps to trigger on hover
    • Verify tooltips appear on collapsed nav items
    • Verify "All tasks" accordion expands/collapses with animation
    • Verify mobile shows SidebarTrigger + Wallet in fixed top-right bar
    • Verify sidebar works on mobile (opens/closes via trigger)
    • Verify all nav links route correctly (Workflows, Explore, Builder)
    • Verify footer shows only Usage + Settings when collapsed

Abhi1992002 and others added 29 commits March 28, 2026 09:42
… content

- Create AppSidebar component with logo, collapsible nav links (Home, Workflow, Explore, Builder, Settings), and dynamic content slot
- Move navigation links and logo from Navbar into the sidebar
- Add ChatSessionList extracted from ChatSidebar for copilot route
- Add route-based SidebarDynamicContent (chat list on copilot, placeholder on other pages)
- Simplify Navbar to only show search input with dynamic placeholder, nav arrows, and right-side items (activity, wallet, account)
- Remove SidebarProvider and ChatSidebar from CopilotPage (now handled at layout level)
- Wrap platform layout in SidebarProvider with AppSidebar
…om navbar, move to sidebar

- Remove AutoGPT logo and navigation links from the top navbar
- Add border-bottom to navbar matching sidebar border color
- Remove bell icon (AgentActivityDropdown) from sidebar header
- Remove Workspace Files navlink from sidebar
- Remove generic text labels from sidebar dynamic content section
- Make page content scrollable (overflow-auto instead of overflow-hidden)
…ing, fix sidebar

- Replace hardcoded bg colors with bg-background design token in CopilotPage and globals.css
- Make sidebar nav item conditional: show "Library" instead of "Copilot" when chat flag disabled
- Add ErrorCard for session query failures instead of collapsing to empty state
- Remove redundant refetch-on-selection useEffect (mutations + polling already handle freshness)
- Forward className and remaining props in SidebarTrigger to avoid silently dropping consumer props
PulseLoader was removed in dev (500b345). Use CircleNotch from
Phosphor with animate-spin to match the ChatSidebar pattern.
The activity badge hook fetched executions only once on mount, missing
runs started after initial load. Added refetchInterval (5s) and
increased test badge timeout to account for polling delay.
Use inline type instead of imported LibraryAgent to avoid mismatch
between generated API types and legacy backend API client types.
Move page-specific sidebar content to live with its page instead of in the shared AppSidebar directory.
… code

Move navigation links from navbar to sidebar, show logo in navbar only
when sidebar is collapsed. Fix layout to use flex-based height instead
of calc(100vh). Remove dead NavbarLink component, MenuIcon, loggedOutLinks,
and accountMenuItems. Enable CHAT flag by default. Update profile/settings
layout with proper padding and overflow handling.
Nav links moved from navbar to AppSidebar. Added data-testid attributes
to sidebar links and updated test selectors accordingly. Updated
logged-out navigation test to reflect that nav links are no longer
visible when logged out.
- Revert body overflow-hidden to min-h-screen so non-platform routes scroll
- Revert Flag.CHAT default back to false (coordinate with LaunchDarkly first)
- Conditionally render drop overlay instead of opacity toggle for accessibility
- Add scroll position reset on route changes in platform layout
- Refactor SidebarTrigger to use Button component from design system
- Update ActivityDropdown header height to match NAVBAR_HEIGHT_PX (65px)
- Guard empty library state in scrollLibraryContainer test helper

https://claude.ai/code/session_01TPw8kd7p8qwsuNa5qBRcHc
… sidebar components

Remove dark:bg-zinc-800 from legacy Sidebar and hard-coded text-neutral-600/hover:bg-zinc-100
from SidebarTrigger, letting the design system and variant="ghost" handle theming.
- Track failed graph ID lookups and exclude after 3 failures
- Stabilize missingGraphIds array reference to prevent useEffect re-fire
  on every 5s refetch when actual IDs haven't changed
… height

Match collapsed sidebar header height to navbar (65px) and style the
trigger icon consistently with nav link icons using sidebar-accent hover.
Return the previous array ref when IDs are unchanged instead of
creating a new array instance, preventing redundant API calls on
every 5-second refetch interval.
…side effects

Use a ref for combinedAgentInfoMap in the WebSocket callback to prevent
stale closures and unnecessary re-subscriptions. Replace ref mutations
inside useMemo with a pure key computation + useEffect for stabilization.
…oute

Only render DeleteChatDialog in ChatSessionList on desktop. On mobile,
CopilotPage already renders its own instance, avoiding two overlapping
dialogs controlled by the same global state.
# Conflicts:
#	autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx
#	autogpt_platform/frontend/src/app/layout.tsx
#	autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx
#	autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/AgentActivityDropdown.tsx
#	autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx
- Reorganize sidebar: move agent activity dropdown and feedback button into sidebar
- Add date-grouped chat sessions (Today, Yesterday, etc.) with inline edit/delete actions
- Add framer-motion slide animations for chat action icons on hover
- Add rename dialog for chat sessions replacing inline editing
- Add loading spinners on navlink navigation
- Update sidebar styling: zinc-100 background, zinc-200 active states, dynamic tooltips
- Update wallet/earn credits design with violet theme
- Remove LayoutContext and LayoutSwitcher (unused)
- Add medium button size variant
- Add Search navlink below New Task
- Collapse sidebar to show only New Task, Search, Workflows, Usage, Settings
- Swap logo to sidebar trigger on hover when collapsed
- Replace mobile hamburger menu with sidebar trigger + wallet on top right
- Remove copilot page mobile drawer in favor of main sidebar
- Add accordion toggle for All tasks section
- Add enter animations using framer-motion
- Fix chat session date grouping using calendar day comparison
- Fix unused imports in AgentActivityDropdown
Add LaunchDarkly feature flag to allow gradual rollout of the redesigned
sidebar. When disabled, the original sidebar behavior is preserved.
Clean up feature flag gating — new sidebar is now the only behavior.
Remove unused MobileNavBar, MobileDrawer, MobileHeader references
and the Search navlink from the sidebar.
@Abhi1992002
Abhi1992002 requested a review from a team as a code owner April 17, 2026 03:07
@github-actions github-actions Bot added platform/frontend AutoGPT Platform - Front end conflicts Automatically applied to PRs with merge conflicts size/xl labels Apr 17, 2026
@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR refactors the frontend's navigation and layout architecture. It introduces a new sidebar system with AppSidebar and SidebarProvider, adds a ChatSessionList component for session management, simplifies the CopilotPage by removing drawer-based UI, modernizes the navbar with collapsible behavior, and updates styling/theme constants and test selectors to align with these structural changes.

Changes

Cohort / File(s) Summary
Chat Session Management
useSaveGraph.ts, ChatSessionList.tsx
New ChatSessionList component fetches and renders chat sessions with inline rename/delete actions via dialogs and hooks. useSaveGraph now invalidates library agents query on graph creation/update success.
Sidebar Architecture & Layout
layout.tsx, AppSidebar/AppSidebar.tsx, AppSidebar/SidebarDynamicContent.tsx, profile/(user)/layout.tsx, profile/(user)/settings/*.tsx
New AppSidebar component with collapsible state and dynamic content; SidebarDynamicContent wraps ChatSessionList. Platform layout converted to client component using SidebarProvider. Profile layouts updated with responsive padding/width constraints.
Navigation Refactor
Navbar/Navbar.tsx, Navbar/components/NavbarLink.tsx, Navbar/components/MenuIcon/MenuIcon.tsx, Navbar/helpers.tsx, Navbar/components/Wallet/Wallet.tsx
Navbar simplified: removed legacy navigation links and UI elements; now shows SidebarTrigger and Wallet on mobile/desktop. NavbarLink and menu icons removed entirely. Wallet styling updated to violet theme.
Activity Dropdown & Polling
Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts, Navbar/components/AgentActivityDropdown/AgentActivityDropdown.tsx, Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx
useAgentActivityDropdown now fetches agent info on-demand with polling (5s refetch), tracks failed lookups, and merges resolved agent data. AgentActivityDropdown adds tooltip/sidebar-aware positioning and resized badge. Header height adjusted.
UI Components & Icons
components/atoms/Button/helpers.ts, components/__legacy__/ui/icons.tsx, components/ui/sidebar.tsx, components/__legacy__/Sidebar.tsx
Added medium button size variant. New IconAutoGPTLogoMinimal icon. Sidebar width reduced to 18rem; SidebarTrigger restyled with improved spacing. Legacy Sidebar repositioned to sticky/top-aligned layout.
Styling & Theme
globals.css, lib/constants.ts
--sidebar-background updated to 240 4.8% 95.9%; body background now uses theme variable. NAVBAR_HEIGHT_PX increased from 60 to 65.
CopilotPage Refactor
CopilotPage.tsx
Removed SidebarProvider, drawer, and mobile header components. Simplified to single top-level div with notification banner, chat container, and artifact panel; changed drop overlay to render only when dragging.
E2E Tests & Selectors
tests/agent-activity.spec.ts, tests/pages/navbar.page.ts, tests/pages/library.page.ts, tests/signin.spec.ts, tests/utils/signup.ts
Updated test selectors from navbar to sidebar links; increased agent-activity badge visibility timeout to 15s; refactored library page scroll helpers; adjusted login page assertions.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Navbar as Navbar
    participant Sidebar as AppSidebar
    participant Sessions as ChatSessionList
    participant QueryCache as React Query Cache
    
    User->>Navbar: Click SidebarTrigger (mobile/desktop)
    Navbar->>Sidebar: Toggle collapse state via useSidebar()
    
    alt Sidebar Expanded
        Sidebar->>Sessions: Render dynamic content
        Sessions->>QueryCache: useGetV2ListSessions (refetch every 10s)
        QueryCache-->>Sessions: Chat sessions (limit 50)
        Sessions->>Sessions: Group by relative date (Today/Yesterday/etc)
        User->>Sessions: Select/Click session
        Sessions->>QueryCache: Invalidate sessions query on delete
        Sessions-->>User: Update active session or navigate
    else Sidebar Collapsed
        Sidebar->>User: Show icon-only nav (Workflows, Explore, Builder)
        Sidebar-->>Navbar: Show tooltip on hover
    end
    
    User->>Sessions: Rename/Delete session
    Sessions->>QueryCache: usePatchV2UpdateSessionTitle / useDeleteV2DeleteSession
    QueryCache->>Sessions: Revalidate sessions list
    Sessions-->>User: Toast notification + UI update
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

size/l, platform/frontend

Suggested reviewers

  • Bentlybro
  • kcze
  • 0ubbe

Poem

🐰 A sidebar springs to life, neat and trim,
Sessions now dance where drawers once dimmed,
The navbar shrank to icons sleek and small,
Chat flows through collapsing walls,
What once was scattered now stands tall! 🏗️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective of the PR: redesigning the sidebar layout with improved UX, which aligns with the comprehensive changes across sidebar, navbar, and mobile navigation components.
Description check ✅ Passed The description clearly explains the motivation (collapsed sidebar lacked discoverability, mobile had separate navbar, no collapsible task list), summarizes the redesign approach, lists specific component changes, and includes a test plan checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Abhi1992002/update-layout
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch Abhi1992002/update-layout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Abhi1992002
Abhi1992002 marked this pull request as draft April 17, 2026 03:08
Comment on lines +73 to +75
<SidebarTrigger className="flex size-10 items-center justify-center rounded-full border border-zinc-200 bg-white [&>svg]:!size-5">
<List className="!size-5" weight="bold" />
</SidebarTrigger>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Children passed to SidebarTrigger are ignored because the component hardcodes its own JSX children, overriding the children in ...props.
Severity: LOW

Suggested Fix

Remove the explicit JSX children from SidebarTrigger's render in sidebar.tsx, and instead conditionally render them: use props.children ?? <>_SidebarSimpleIcon className="size-5 shrink-0" />_span className="sr-only">Toggle Sidebar</span>_/>. Or remove the custom <List> children from the Navbar call if the SidebarSimpleIcon default is acceptable.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx#L73-L75

Potential issue: In `Navbar.tsx` (lines 73-75), `<List>` is passed as children to
`SidebarTrigger`. In `sidebar.tsx`, `SidebarTrigger` destructures `({ className,
onClick, ...props })` — so `children` (the `<List>` icon) ends up in `...props`.
However, inside `SidebarTrigger`, the `Button` is rendered with `{...props}` spread AND
explicit JSX children (`<SidebarSimpleIcon>` and `<span>`). In React, explicit JSX
children are placed after the spread and override any `children` key in `...props`. As a
result, the `List` icon passed by `Navbar` is never rendered — the mobile trigger always
shows the `SidebarSimpleIcon` instead. This is a functional discrepancy between the
intended and actual mobile UX.

Did we get this right? 👍 / 👎 to inform future reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx (1)

1705-1715: ⚠️ Potential issue | 🟡 Minor

Orphaned JSDoc block for IconAutoGPTLogo.

The JSDoc at lines 1705–1711 documents IconAutoGPTLogo but now sits directly above IconAutoGPTLogoMinimal, while IconAutoGPTLogo (line 1813) is left without its doc block. Move the new minimal variant below IconAutoGPTLogo, or swap the doc blocks so each component keeps its description.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx` around
lines 1705 - 1715, Orphaned JSDoc: the JSDoc for IconAutoGPTLogo lives above
IconAutoGPTLogoMinimal, leaving IconAutoGPTLogo undocumented; fix by moving the
JSDoc block so it sits immediately above IconAutoGPTLogo (or alternatively swap
the positions of the IconAutoGPTLogo and IconAutoGPTLogoMinimal component
declarations) ensuring the JSDoc that references IconAutoGPTLogo is directly
above the IconAutoGPTLogo component and the minimal variant has its own
appropriate comment.
autogpt_platform/frontend/src/components/layout/Navbar/components/Wallet/Wallet.tsx (1)

299-323: ⚠️ Potential issue | 🟡 Minor

Flash overlay rounded-md no longer matches the new rounded-xl button.

The button was updated to rounded-xl on line 299, but the flash overlay <div> at line 319–322 still uses rounded-md. On credit change the violet flash will bleed past the button's corners.

✏️ Proposed fix
         <div
           className={cn(
-            "pointer-events-none absolute inset-0 rounded-md bg-violet-400 duration-2000 ease-in-out",
+            "pointer-events-none absolute inset-0 rounded-xl bg-violet-400 duration-2000 ease-in-out",
             flash ? "opacity-50 duration-0" : "opacity-0",
           )}
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/components/layout/Navbar/components/Wallet/Wallet.tsx`
around lines 299 - 323, The flash overlay div in the Wallet component uses class
"rounded-md" which no longer matches the button's "rounded-xl" (see the button
element and the overlay div using cn); update the overlay's class to
"rounded-xl" so the violet flash respects the button's corners (adjust the
className inside the cn call on the overlay div to replace "rounded-md" with
"rounded-xl").
🧹 Nitpick comments (8)
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts (1)

69-71: LGTM — cache invalidation on save.

Invalidating getGetV2ListLibraryAgentsQueryKey() after create/update ensures the library agents list reflects newly saved graphs. Awaiting the invalidation before cleanup/toast is reasonable.

One optional consideration: if onSuccess?.(data) callers don't depend on the library list being refreshed, you could fire-and-forget the invalidation (drop the await) to shave perceived latency on the save flow. Not a blocker.

Also applies to: 109-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/`(platform)/build/hooks/useSaveGraph.ts
around lines 69 - 71, The cache invalidation currently awaits
queryClient.invalidateQueries({ queryKey: getGetV2ListLibraryAgentsQueryKey() })
in the useSaveGraph hook; if callers of onSuccess?.(data) don't rely on the
library list being refreshed, remove the await to make the invalidation
fire-and-forget so the save flow (cleanup/toast/onSuccess) isn't delayed—update
both places where invalidateQueries is awaited (the call around lines 69-71 and
the similar call around 109-111) to drop the await and leave the
invalidateQueries call unawaited.
autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts (2)

83-154: Naming: use fully-capitalized ID per repo convention.

New identifiers missingGraphIds, missingGraphIdsKey, graphIds, and the local graphId callback param should be missingGraphIDs, missingGraphIDsKey, graphIDs, graphID to match the graphID/useBackendAPI convention the repo enforces (and mirrors the generated GraphID type).

As per coding guidelines: "Fully capitalize acronyms in symbols, e.g. graphID, useBackendAPI".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts`
around lines 83 - 154, Rename all identifiers and occurrences to use
fully-capitalized "ID" per repo convention: change missingGraphIdsKey →
missingGraphIDsKey, missingGraphIds → missingGraphIDs (state and setter),
graphIds → graphIDs, and the callback param graphId → graphID; update their
usages in useMemo, useState, useEffect, resolveMissingAgents mapping
(api.getLibraryAgentByGraphID), and anywhere else in this file (including Set
and Array conversions and split/join calls) so types like GraphID and helper
refs (combinedAgentInfoMapRef) remain consistent and TypeScript types still
match; ensure the dependency arrays and useCallback signature
(handleExecutionEvent) reference the renamed symbols.

15-38: Extract the agent info map value type to a shared export from store.ts.

The local AgentInfo type duplicates the same shape ({ name, description, library_agent_id? }) that is inlined in buildAgentInfoMap and repeated across helpers and other hooks. Extract this to a named export in store.ts (e.g., AgentInfoEntry or similar) and import it in useAgentActivityDropdown.ts and other places that reference agentInfoMap values. This eliminates duplication and provides a single source of truth for the Map value shape.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts`
around lines 15 - 38, The local AgentInfo type duplicates the map value shape;
instead export a shared type (e.g., AgentInfoEntry) from store.ts and use it
here: remove the local AgentInfo declaration, import the new AgentInfoEntry from
store.ts, and update the return type of toAgentInfo and any references to
agentInfoMap/related helpers (e.g., buildAgentInfoMap, useAgentActivityDropdown)
to use the imported type; ensure the store.ts export matches the chosen name and
update all other files that previously inlined the shape to import the shared
type.
autogpt_platform/frontend/src/app/(platform)/profile/(user)/layout.tsx (1)

66-68: Asymmetric mobile padding from unconditional pl-4 on children wrapper.

Outer container now applies px-4 (and lg:px-6), but the children wrapper still uses pl-4 unconditionally. In the mobile flex-col layout this stacks extra left padding under the sidebar section, producing asymmetric horizontal whitespace (~32px left vs 16px right). Consider gating pl-4 to lg: so it only applies in the row layout where the sidebar sits to the left.

♻️ Proposed diff
-      <div className="min-w-0 flex-1 pl-4">{children}</div>
+      <div className="min-w-0 flex-1 lg:pl-4">{children}</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/`(platform)/profile/(user)/layout.tsx
around lines 66 - 68, The children wrapper div currently uses an unconditional
"pl-4" causing extra left padding on mobile; change the padding utility on the
children wrapper to be responsive (use "lg:pl-4" instead of "pl-4") so the outer
container's "px-4" governs mobile spacing and the left padding only appears in
the desktop row layout where the Sidebar (Sidebar, sidebarLinkGroups) sits to
the left.
autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx (4)

4-7: Legacy import: @/components/__legacy__/ui/icons.

The guidelines forbid src/components/__legacy__/* imports. If IconAutoGPTLogo / IconAutoGPTLogoMinimal don't yet have a design-system equivalent, consider either (a) moving the SVGs into an atom in src/components/atoms/ under the brand asset folder, or (b) inlining them directly in this component. Worth addressing in this PR given the sidebar is a user-facing entry point touching brand.

As per coding guidelines: "Never use src/components/__legacy__/*".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`
around lines 4 - 7, AppSidebar.tsx currently imports IconAutoGPTLogo and
IconAutoGPTLogoMinimal from the forbidden __legacy__ path; replace that by
moving the SVG components into a design-system atom (e.g., create new files
exporting IconAutoGPTLogo and IconAutoGPTLogoMinimal under
src/components/atoms/brand or, alternatively, inline the SVG JSX directly in
AppSidebar) and update the import in AppSidebar.tsx to the new atom path or
remove the legacy import if inlined; keep the exported component names
(IconAutoGPTLogo, IconAutoGPTLogoMinimal) so usages in AppSidebar.tsx remain
unchanged and ensure any props/typing are preserved.

103-352: Split the sidebar into smaller sub-components.

At 353 lines, the file exceeds the guideline and mixes header (collapsed/expanded branches), nav-links, tasks accordion, and footer (usage/notification/feedback/settings). Extracting SidebarHeaderSection, SidebarNavLinks, TasksAccordion, and SidebarFooterSection into local components/ files, plus moving navLinks + isActive/loadingHref into a useAppSidebar.ts hook, would bring this under the threshold and make each piece individually testable.

As per coding guidelines: "Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this" and "Structure React components as: ComponentName/ComponentName.tsx + useComponentName.ts + helpers.ts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`
around lines 103 - 352, This file is over the size guideline and mixes multiple
UI responsibilities; split AppSidebar into smaller components and a hook by
extracting the header, nav links, tasks accordion and footer into their own
files (e.g., SidebarHeaderSection ->
SidebarHeaderSection/SidebarHeaderSection.tsx, SidebarNavLinks ->
SidebarNavLinks/SidebarNavLinks.tsx, TasksAccordion ->
TasksAccordion/TasksAccordion.tsx, SidebarFooterSection ->
SidebarFooterSection/SidebarFooterSection.tsx) and move navigation state and
helpers (navLinks, isActive, loadingHref, setLoadingHref, isTasksOpen,
setIsTasksOpen, dynamicContent related logic) into a useAppSidebar.ts hook;
update AppSidebar to import and compose SidebarHeaderSection, SidebarNavLinks,
TasksAccordion and SidebarFooterSection and read UI state from useAppSidebar so
each piece is < ~200 lines and individually testable.

129-131: Dead ternary — this branch is always expanded.

This TooltipContent is inside the !isCollapsed && block at line 118, so isCollapsed is always false here. Simplify:

🔁 Proposed change
-                <TooltipContent side={isCollapsed ? "right" : "bottom"}>
+                <TooltipContent side="bottom">
                   Close sidebar
                 </TooltipContent>

The same applies at lines 294 and 329 (both inside !isCollapsed && blocks, always "top").

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`
around lines 129 - 131, The TooltipContent instances use a dead ternary on prop
side (e.g., TooltipContent side={isCollapsed ? "right" : "bottom"}) while
already inside a !isCollapsed block, so replace the ternary with the constant
branch ("bottom" for the instance inside the !isCollapsed block at the Close
sidebar location) and similarly replace the ternaries at the other two
TooltipContent uses (lines referenced in the comment) with the constant "top"
where they are also inside !isCollapsed blocks; update only the TooltipContent
props (no other logic) for the components named TooltipContent and the
surrounding gating that uses isCollapsed.

136-146: Ancestor already has group—the hover swap will work, but confirm the interaction scope.

The Sidebar component (ancestor at line 236 of sidebar.tsx) already has className="group peer...", so group-hover:opacity-0/100 will activate on hover. The logo won't be permanently invisible in collapsed mode.

However, with the group scoped to the entire Sidebar, hovering anywhere within the collapsed sidebar triggers the logo→trigger swap—not just the logo area. The PR description says "Collapsed state now swaps the logo to a SidebarTrigger on hover", which is ambiguous about whether this wide hover scope is intentional. If you want the swap to trigger only on logo hover (narrower UX), add group to the wrapper div at line 137:

Scope the group to the wrapper if logo-only hover is intended
-          <div className="relative flex flex-col items-center">
+          <div className="group relative flex flex-col items-center">
             <Link
               href={homeHref}
               className="transition-opacity group-hover:opacity-0"
             >
               <IconAutoGPTLogoMinimal className="h-6 w-6" />
             </Link>
             <SidebarTrigger className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100 hover:bg-sidebar-accent [&>svg]:!size-5" />
           </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`
around lines 136 - 146, The hover swap is currently scoped to the whole Sidebar
because the ancestor Sidebar has className="group", so constrain the hover to
just the logo area by adding the group to the wrapper div that contains Link and
SidebarTrigger (the element around IconAutoGPTLogoMinimal and SidebarTrigger) —
e.g., update that div's className to include "group" (or merge with its existing
classes "relative flex flex-col items-center") so the existing
"group-hover:opacity-0" and "group-hover:opacity-100" rules only apply when
hovering that wrapper rather than the entire Sidebar.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx:
- Around line 59-81: The mutation success handler is comparing
sessionToDelete?.id to the raw nuqs state sessionId which can be non-null even
when not on /copilot; change the comparison to use the computed activeSessionId
instead so we only clear the URL param when the deleted session is the actively
displayed one. Update the onSuccess block inside useDeleteV2DeleteSession (the
mutate returned as deleteSession) to check sessionToDelete?.id ===
activeSessionId and then call setSessionId(null) accordingly, leaving the rest
of the invalidate and setSessionToDelete(null) logic unchanged.
- Around line 32-106: ChatSessionList is doing too much presentation + business
logic; extract the session fetching, mutations, dialog/state handlers,
hover/loading/completed-session effects, and grouping logic into a new
useChatSessionList hook and move date helpers (getDateGroup, groupSessions) into
a helpers.ts; keep ChatSessionList.tsx as a thin render-only component that uses
useChatSessionList (retain symbols like useGetV2ListSessions,
useDeleteV2DeleteSession, usePatchV2UpdateSessionTitle, sessionToDelete,
setSessionToDelete, editingSessionId, setEditingSessionId, renameInputRef,
loadingSessionId, hoveredSessionId, completedSessionIDs); also extract the
rename dialog UI into a local sub-component RenameSessionDialog under components
and have ChatSessionList render it while the hook provides renameSession,
isRenaming and editingTitle handlers.
- Around line 323-340: The code is directly writing to the internal Tailwind
variable "--tw-gradient-to" and hardcoding "rgb(235 235 238)" in the div that
renders the gradient (the element with className "pointer-events-none h-full w-8
bg-gradient-to-r from-transparent" and the sibling div whose backgroundColor is
set when session.id === activeSessionId). Replace these inline style assignments
by using Tailwind utilities or your own design token: remove the
["--tw-gradient-to" as string] inline style and instead conditionally apply
Tailwind "to-..." utility classes or a class that reads a custom CSS variable
(e.g., via a component-scoped CSS var) when session.id === activeSessionId, and
replace the hardcoded rgb color with an existing design token Tailwind class (or
your custom var) so styling follows the codebase pattern (use the conditional on
session.id === activeSessionId to toggle the appropriate utility/class).

In `@autogpt_platform/frontend/src/app/layout.tsx`:
- Line 66: Update the stale inline comment that references "Navbar" to
accurately state that the Tally feedback button was moved to the Sidebar footer
(or remove the comment entirely); locate the comment mentioning TallyPopupSimple
in layout.tsx and replace the text "feedback button is now in the Navbar" with
"feedback button is now in the Sidebar footer (rendered in AppSidebar.tsx)" so
it matches AppSidebar.tsx's conditional rendering.

In `@autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx`:
- Around line 1715-1723: The IconAutoGPTLogoMinimal component currently sets
aria-label="AutoGPT Logo", which duplicates IconAutoGPTLogo and prevents
assistive tech from distinguishing them; update the inline aria-label on
IconAutoGPTLogoMinimal to a unique value like "AutoGPT Logo (minimal)" or remove
the inline aria-label so createIcon uses the component displayName, and ensure
the component displayName (IconAutoGPTLogoMinimal) or the explicit aria-label is
updated accordingly to provide distinct accessible names between
IconAutoGPTLogoMinimal and IconAutoGPTLogo.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`:
- Around line 46-48: Replace the exported interface Props with an unexported
type alias: change "interface Props { dynamicContent?: ReactNode }" to "type
Props = { dynamicContent?: ReactNode }" in AppSidebar.tsx; ensure the
component's props annotation (where Props is referenced) uses this new type and
remove any export on Props so it remains local to the component.

In `@autogpt_platform/frontend/src/tests/pages/library.page.ts`:
- Around line 19-67: The scrollLibraryContainer helper currently always calls
lastAgentCard.scrollIntoViewIfNeeded(), which makes the "page" (scrollDown)
behavior act like "bottom" (scrollToBottom); modify scrollLibraryContainer so
that when position === "page" you do not call
lastAgentCard.scrollIntoViewIfNeeded() (or alternatively compute and apply an
incremental scroll based on the current scrollTop and clientHeight of the found
scrollable ancestor rather than first forcing the last card into view). Locate
the position parameter and the lastAgentCard variable in scrollLibraryContainer,
remove/guard the scrollIntoViewIfNeeded() call for the "page" branch (or change
the inner evaluation to read currentElement.scrollTop and add
currentElement.clientHeight instead of clamping after an initial scroll), and
keep the existing "bottom" branch behavior unchanged.

---

Outside diff comments:
In `@autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx`:
- Around line 1705-1715: Orphaned JSDoc: the JSDoc for IconAutoGPTLogo lives
above IconAutoGPTLogoMinimal, leaving IconAutoGPTLogo undocumented; fix by
moving the JSDoc block so it sits immediately above IconAutoGPTLogo (or
alternatively swap the positions of the IconAutoGPTLogo and
IconAutoGPTLogoMinimal component declarations) ensuring the JSDoc that
references IconAutoGPTLogo is directly above the IconAutoGPTLogo component and
the minimal variant has its own appropriate comment.

In
`@autogpt_platform/frontend/src/components/layout/Navbar/components/Wallet/Wallet.tsx`:
- Around line 299-323: The flash overlay div in the Wallet component uses class
"rounded-md" which no longer matches the button's "rounded-xl" (see the button
element and the overlay div using cn); update the overlay's class to
"rounded-xl" so the violet flash respects the button's corners (adjust the
className inside the cn call on the overlay div to replace "rounded-md" with
"rounded-xl").

---

Nitpick comments:
In `@autogpt_platform/frontend/src/app/`(platform)/build/hooks/useSaveGraph.ts:
- Around line 69-71: The cache invalidation currently awaits
queryClient.invalidateQueries({ queryKey: getGetV2ListLibraryAgentsQueryKey() })
in the useSaveGraph hook; if callers of onSuccess?.(data) don't rely on the
library list being refreshed, remove the await to make the invalidation
fire-and-forget so the save flow (cleanup/toast/onSuccess) isn't delayed—update
both places where invalidateQueries is awaited (the call around lines 69-71 and
the similar call around 109-111) to drop the await and leave the
invalidateQueries call unawaited.

In `@autogpt_platform/frontend/src/app/`(platform)/profile/(user)/layout.tsx:
- Around line 66-68: The children wrapper div currently uses an unconditional
"pl-4" causing extra left padding on mobile; change the padding utility on the
children wrapper to be responsive (use "lg:pl-4" instead of "pl-4") so the outer
container's "px-4" governs mobile spacing and the left padding only appears in
the desktop row layout where the Sidebar (Sidebar, sidebarLinkGroups) sits to
the left.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`:
- Around line 4-7: AppSidebar.tsx currently imports IconAutoGPTLogo and
IconAutoGPTLogoMinimal from the forbidden __legacy__ path; replace that by
moving the SVG components into a design-system atom (e.g., create new files
exporting IconAutoGPTLogo and IconAutoGPTLogoMinimal under
src/components/atoms/brand or, alternatively, inline the SVG JSX directly in
AppSidebar) and update the import in AppSidebar.tsx to the new atom path or
remove the legacy import if inlined; keep the exported component names
(IconAutoGPTLogo, IconAutoGPTLogoMinimal) so usages in AppSidebar.tsx remain
unchanged and ensure any props/typing are preserved.
- Around line 103-352: This file is over the size guideline and mixes multiple
UI responsibilities; split AppSidebar into smaller components and a hook by
extracting the header, nav links, tasks accordion and footer into their own
files (e.g., SidebarHeaderSection ->
SidebarHeaderSection/SidebarHeaderSection.tsx, SidebarNavLinks ->
SidebarNavLinks/SidebarNavLinks.tsx, TasksAccordion ->
TasksAccordion/TasksAccordion.tsx, SidebarFooterSection ->
SidebarFooterSection/SidebarFooterSection.tsx) and move navigation state and
helpers (navLinks, isActive, loadingHref, setLoadingHref, isTasksOpen,
setIsTasksOpen, dynamicContent related logic) into a useAppSidebar.ts hook;
update AppSidebar to import and compose SidebarHeaderSection, SidebarNavLinks,
TasksAccordion and SidebarFooterSection and read UI state from useAppSidebar so
each piece is < ~200 lines and individually testable.
- Around line 129-131: The TooltipContent instances use a dead ternary on prop
side (e.g., TooltipContent side={isCollapsed ? "right" : "bottom"}) while
already inside a !isCollapsed block, so replace the ternary with the constant
branch ("bottom" for the instance inside the !isCollapsed block at the Close
sidebar location) and similarly replace the ternaries at the other two
TooltipContent uses (lines referenced in the comment) with the constant "top"
where they are also inside !isCollapsed blocks; update only the TooltipContent
props (no other logic) for the components named TooltipContent and the
surrounding gating that uses isCollapsed.
- Around line 136-146: The hover swap is currently scoped to the whole Sidebar
because the ancestor Sidebar has className="group", so constrain the hover to
just the logo area by adding the group to the wrapper div that contains Link and
SidebarTrigger (the element around IconAutoGPTLogoMinimal and SidebarTrigger) —
e.g., update that div's className to include "group" (or merge with its existing
classes "relative flex flex-col items-center") so the existing
"group-hover:opacity-0" and "group-hover:opacity-100" rules only apply when
hovering that wrapper rather than the entire Sidebar.

In
`@autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts`:
- Around line 83-154: Rename all identifiers and occurrences to use
fully-capitalized "ID" per repo convention: change missingGraphIdsKey →
missingGraphIDsKey, missingGraphIds → missingGraphIDs (state and setter),
graphIds → graphIDs, and the callback param graphId → graphID; update their
usages in useMemo, useState, useEffect, resolveMissingAgents mapping
(api.getLibraryAgentByGraphID), and anywhere else in this file (including Set
and Array conversions and split/join calls) so types like GraphID and helper
refs (combinedAgentInfoMapRef) remain consistent and TypeScript types still
match; ensure the dependency arrays and useCallback signature
(handleExecutionEvent) reference the renamed symbols.
- Around line 15-38: The local AgentInfo type duplicates the map value shape;
instead export a shared type (e.g., AgentInfoEntry) from store.ts and use it
here: remove the local AgentInfo declaration, import the new AgentInfoEntry from
store.ts, and update the return type of toAgentInfo and any references to
agentInfoMap/related helpers (e.g., buildAgentInfoMap, useAgentActivityDropdown)
to use the imported type; ensure the store.ts export matches the chosen name and
update all other files that previously inlined the shape to import the shared
type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 580a450b-97d3-478a-b8b5-e1c7ea34f239

📥 Commits

Reviewing files that changed from the base of the PR and between 60b8564 and c4cf7e2.

📒 Files selected for processing (29)
  • autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx
  • autogpt_platform/frontend/src/app/(platform)/layout.tsx
  • autogpt_platform/frontend/src/app/(platform)/profile/(user)/layout.tsx
  • autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/loading.tsx
  • autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx
  • autogpt_platform/frontend/src/app/globals.css
  • autogpt_platform/frontend/src/app/layout.tsx
  • autogpt_platform/frontend/src/components/__legacy__/Sidebar.tsx
  • autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx
  • autogpt_platform/frontend/src/components/atoms/Button/helpers.ts
  • autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx
  • autogpt_platform/frontend/src/components/layout/AppSidebar/SidebarDynamicContent.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/AgentActivityDropdown.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/components/ActivityDropdown/ActivityDropdown.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/AgentActivityDropdown/useAgentActivityDropdown.ts
  • autogpt_platform/frontend/src/components/layout/Navbar/components/MenuIcon/MenuIcon.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/Wallet/Wallet.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/helpers.tsx
  • autogpt_platform/frontend/src/components/ui/sidebar.tsx
  • autogpt_platform/frontend/src/lib/constants.ts
  • autogpt_platform/frontend/src/tests/agent-activity.spec.ts
  • autogpt_platform/frontend/src/tests/pages/library.page.ts
  • autogpt_platform/frontend/src/tests/pages/navbar.page.ts
  • autogpt_platform/frontend/src/tests/signin.spec.ts
  • autogpt_platform/frontend/src/tests/utils/signup.ts
💤 Files with no reviewable changes (3)
  • autogpt_platform/frontend/src/components/layout/Navbar/helpers.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/MenuIcon/MenuIcon.tsx
  • autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx

Comment on lines +32 to +106
export function ChatSessionList() {
const isMobile = useIsMobile();
const pathname = usePathname();
const router = useRouter();
const isCopilotPage = pathname === "/" || pathname.startsWith("/copilot");
const [sessionId, setSessionId] = useQueryState("sessionId", parseAsString);
const activeSessionId = isCopilotPage ? sessionId : null;
const [loadingSessionId, setLoadingSessionId] = useState<string | null>(null);
const [hoveredSessionId, setHoveredSessionId] = useState<string | null>(null);
const {
sessionToDelete,
setSessionToDelete,
completedSessionIDs,
clearCompletedSession,
} = useCopilotUIStore();

const queryClient = useQueryClient();

const {
data: sessionsResponse,
isLoading: isLoadingSessions,
isError: isSessionsError,
} = useGetV2ListSessions(
{ limit: 50 },
{ query: { refetchInterval: 10_000 } },
);

const { mutate: deleteSession, isPending: isDeleting } =
useDeleteV2DeleteSession({
mutation: {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getGetV2ListSessionsQueryKey(),
});
if (sessionToDelete?.id === sessionId) {
setSessionId(null);
}
setSessionToDelete(null);
},
onError: (error: unknown) => {
toast({
title: "Failed to delete chat",
description:
error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
setSessionToDelete(null);
},
},
});

const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
const [editingTitle, setEditingTitle] = useState("");
const renameInputRef = useRef<HTMLInputElement>(null);

const { mutate: renameSession, isPending: isRenaming } =
usePatchV2UpdateSessionTitle({
mutation: {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getGetV2ListSessionsQueryKey(),
});
setEditingSessionId(null);
},
onError: (error: unknown) => {
toast({
title: "Failed to rename chat",
description:
error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
setEditingSessionId(null);
},
},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract logic to a useChatSessionList.ts hook and split sub-components.

This file is 439 lines and owns: session fetching, rename mutation + dialog state, delete mutation + confirmation, hover state, loading state, completed-session effects, date grouping, and two rendered dialogs. Per the component.tsx + useComponent.ts + helpers.ts pattern, the mutations, effects, and grouping/handler functions belong in a hook; getDateGroup / groupSessions belong in helpers.ts; and the rename dialog is a good candidate for a local sub-component (e.g. components/RenameSessionDialog).

As per coding guidelines: "Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this" and "Separate render logic (.tsx) from business logic (use*.ts hooks)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx
around lines 32 - 106, ChatSessionList is doing too much presentation + business
logic; extract the session fetching, mutations, dialog/state handlers,
hover/loading/completed-session effects, and grouping logic into a new
useChatSessionList hook and move date helpers (getDateGroup, groupSessions) into
a helpers.ts; keep ChatSessionList.tsx as a thin render-only component that uses
useChatSessionList (retain symbols like useGetV2ListSessions,
useDeleteV2DeleteSession, usePatchV2UpdateSessionTitle, sessionToDelete,
setSessionToDelete, editingSessionId, setEditingSessionId, renameInputRef,
loadingSessionId, hoveredSessionId, completedSessionIDs); also extract the
rename dialog UI into a local sub-component RenameSessionDialog under components
and have ChatSessionList render it while the hook provides renameSession,
isRenaming and editingTitle handlers.

Comment on lines +59 to +81
const { mutate: deleteSession, isPending: isDeleting } =
useDeleteV2DeleteSession({
mutation: {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getGetV2ListSessionsQueryKey(),
});
if (sessionToDelete?.id === sessionId) {
setSessionId(null);
}
setSessionToDelete(null);
},
onError: (error: unknown) => {
toast({
title: "Failed to delete chat",
description:
error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
setSessionToDelete(null);
},
},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Delete-match should use activeSessionId, not raw sessionId.

Line 66 compares sessionToDelete?.id === sessionId, but sessionId here is the nuqs state which can be non-null even when the user isn't on /copilot (the route just won't read it). You already compute activeSessionId above specifically to guard this. Using activeSessionId makes the intent ("only clear the URL param when deleting the currently active session") explicit and avoids surprising clears.

🔁 Proposed change
-          if (sessionToDelete?.id === sessionId) {
+          if (sessionToDelete?.id === activeSessionId) {
             setSessionId(null);
           }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { mutate: deleteSession, isPending: isDeleting } =
useDeleteV2DeleteSession({
mutation: {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getGetV2ListSessionsQueryKey(),
});
if (sessionToDelete?.id === sessionId) {
setSessionId(null);
}
setSessionToDelete(null);
},
onError: (error: unknown) => {
toast({
title: "Failed to delete chat",
description:
error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
setSessionToDelete(null);
},
},
});
const { mutate: deleteSession, isPending: isDeleting } =
useDeleteV2DeleteSession({
mutation: {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: getGetV2ListSessionsQueryKey(),
});
if (sessionToDelete?.id === activeSessionId) {
setSessionId(null);
}
setSessionToDelete(null);
},
onError: (error: unknown) => {
toast({
title: "Failed to delete chat",
description:
error instanceof Error ? error.message : "An error occurred",
variant: "destructive",
});
setSessionToDelete(null);
},
},
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx
around lines 59 - 81, The mutation success handler is comparing
sessionToDelete?.id to the raw nuqs state sessionId which can be non-null even
when not on /copilot; change the comparison to use the computed activeSessionId
instead so we only clear the URL param when the deleted session is the actively
displayed one. Update the onSuccess block inside useDeleteV2DeleteSession (the
mutate returned as deleteSession) to check sessionToDelete?.id ===
activeSessionId and then call setSessionId(null) accordingly, leaving the rest
of the invalidate and setSessionToDelete(null) logic unchanged.

Comment on lines +323 to +340
<div
className="pointer-events-none h-full w-8 bg-gradient-to-r from-transparent"
style={{
["--tw-gradient-to" as string]:
session.id === activeSessionId
? "rgb(235 235 238)"
: "hsl(var(--sidebar-accent))",
}}
/>
<div
className="flex h-full items-center gap-0.5 rounded-r-xl pr-2"
style={{
backgroundColor:
session.id === activeSessionId
? "rgb(235 235 238)"
: "hsl(var(--sidebar-accent))",
}}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Does Tailwind CSS v4 still expose the --tw-gradient-to CSS custom property for gradient utilities, or was the gradient variable model changed?

💡 Result:

Yes, Tailwind CSS v4 still exposes the --tw-gradient-to CSS custom property for gradient utilities. The gradient variable model has been enhanced but not fundamentally changed in a way that removes this property. In v4, gradient utilities like from-{color}, to-{color}, and via-{color} set CSS custom properties including --tw-gradient-from, --tw-gradient-to, --tw-gradient-from-position, --tw-gradient-to-position, and --tw-gradient-stops. These are registered with @property for animatability, with defaults like --tw-gradient-to-position: 100%. Examples from official docs and GitHub issues confirm: - .to-green-500 sets --tw-gradient-to: var(--color-green-500); - background-image uses linear-gradient(..., var(--tw-gradient-stops)); The primary changes are: - Renamed bg-gradient-to-* to bg-linear-to-* (though bg-gradient-to-* may still work for compatibility). - Added @property registration: @property --tw-gradient-from { syntax: ""; inherits: false; initial-value: #0000; } - Position variables separated (e.g., --tw-gradient-to-position) for better control and animation. - Default positions explicit (e.g., via at 50%), differing from v3 behavior where unset via used computed positions. This evolution supports gradient animations, OKLCH interpolation by default, and radial/conic gradients, but the core --tw-gradient-to variable remains for the ending color stop.

Citations:


🏁 Script executed:

# Check for design token usage and similar patterns in the codebase
rg "sidebar-accent" autogpt_platform/frontend --type tsx --type ts -B 2 -A 2 | head -50

Repository: Significant-Gravitas/AutoGPT

Length of output: 99


🏁 Script executed:

# Look for how gradient/conditional styling is typically done in the codebase
rg "bg-gradient\|className.*\?" autogpt_platform/frontend/src/app/\(platform\) --type tsx -A 1 | head -60

Repository: Significant-Gravitas/AutoGPT

Length of output: 99


🏁 Script executed:

# Search for sidebar-accent usage in frontend
rg "sidebar-accent" autogpt_platform/frontend --glob "*.tsx" --glob "*.ts" -B 2 -A 2

Repository: Significant-Gravitas/AutoGPT

Length of output: 13696


🏁 Script executed:

# Look for similar conditional className patterns with gradients
rg "className.*gradient" autogpt_platform/frontend/src/app --glob "*.tsx" -A 2 -B 1 | head -80

Repository: Significant-Gravitas/AutoGPT

Length of output: 10844


🏁 Script executed:

# Check the actual file to understand the full context
cat -n autogpt_platform/frontend/src/app/\(platform\)/copilot/components/ChatSessionList/ChatSessionList.tsx | sed -n '320,345p'

Repository: Significant-Gravitas/AutoGPT

Length of output: 1593


Use Tailwind utilities with design tokens instead of manipulating internal gradient variables.

The --tw-gradient-to variable is an internal Tailwind implementation detail. Setting it via inline style bypasses the "Tailwind CSS only for styling" guideline and hardcodes color values instead of using design tokens. While --tw-gradient-to still exists in Tailwind v4, relying on it is fragile and inconsistent with the codebase pattern (e.g., hover:bg-sidebar-accent).

Replace the inline style approach with Tailwind utility classes: conditionally apply to-* color classes, or define a custom CSS variable you own and set it in the component's context. Also move the hardcoded "rgb(235 235 238)" to a design token or use an existing Tailwind color.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx
around lines 323 - 340, The code is directly writing to the internal Tailwind
variable "--tw-gradient-to" and hardcoding "rgb(235 235 238)" in the div that
renders the gradient (the element with className "pointer-events-none h-full w-8
bg-gradient-to-r from-transparent" and the sibling div whose backgroundColor is
set when session.id === activeSessionId). Replace these inline style assignments
by using Tailwind utilities or your own design token: remove the
["--tw-gradient-to" as string] inline style and instead conditionally apply
Tailwind "to-..." utility classes or a class that reads a custom CSS variable
(e.g., via a component-scoped CSS var) when session.id === activeSessionId, and
replace the hardcoded rgb color with an existing design token Tailwind class (or
your custom var) so styling follows the codebase pattern (use the conditional on
session.id === activeSessionId to toggle the appropriate utility/class).

<div className="flex h-full flex-col items-stretch justify-items-stretch">
{children}
<TallyPopupSimple />
{/* TallyPopupSimple removed — feedback button is now in the Navbar */}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale comment — feedback moved to the Sidebar, not the Navbar.

Per the PR description (and AppSidebar.tsx which now conditionally renders the Tally Feedback button), this relocated to the sidebar footer. Keeping the stray comment referencing "Navbar" will mislead the next reader; consider updating or dropping it.

✏️ Proposed fix
-              {/* TallyPopupSimple removed — feedback button is now in the Navbar */}
+              {/* TallyPopupSimple removed — feedback button is now in the AppSidebar footer */}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{/* TallyPopupSimple removed — feedback button is now in the Navbar */}
{/* TallyPopupSimple removed — feedback button is now in the AppSidebar footer */}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/layout.tsx` at line 66, Update the stale
inline comment that references "Navbar" to accurately state that the Tally
feedback button was moved to the Sidebar footer (or remove the comment
entirely); locate the comment mentioning TallyPopupSimple in layout.tsx and
replace the text "feedback button is now in the Navbar" with "feedback button is
now in the Sidebar footer (rendered in AppSidebar.tsx)" so it matches
AppSidebar.tsx's conditional rendering.

Comment on lines +1715 to +1723
export const IconAutoGPTLogoMinimal = createIcon((props) => (
<svg
width="42"
height="40"
viewBox="47 0 42 40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="AutoGPT Logo"
{...props}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate aria-label with the full logo.

aria-label="AutoGPT Logo" is identical to IconAutoGPTLogo. When both icons render on the same page (e.g. expanded vs. collapsed sidebar during transition), assistive tech can't distinguish them. Consider "AutoGPT Logo (minimal)" or similar, and note that createIcon already sets aria-label from displayName — the inline prop here will override it; make sure that's intentional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/__legacy__/ui/icons.tsx` around
lines 1715 - 1723, The IconAutoGPTLogoMinimal component currently sets
aria-label="AutoGPT Logo", which duplicates IconAutoGPTLogo and prevents
assistive tech from distinguishing them; update the inline aria-label on
IconAutoGPTLogoMinimal to a unique value like "AutoGPT Logo (minimal)" or remove
the inline aria-label so createIcon uses the component displayName, and ensure
the component displayName (IconAutoGPTLogoMinimal) or the explicit aria-label is
updated accordingly to provide distinct accessible names between
IconAutoGPTLogoMinimal and IconAutoGPTLogo.

Comment on lines +46 to +48
interface Props {
dynamicContent?: ReactNode;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use type Props instead of interface Props.

🔁 Proposed change
-interface Props {
-  dynamicContent?: ReactNode;
-}
+type Props = {
+  dynamicContent?: ReactNode;
+};

As per coding guidelines: "Component props should be type Props = { ... } (not exported) unless it needs to be used outside the component".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface Props {
dynamicContent?: ReactNode;
}
type Props = {
dynamicContent?: ReactNode;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx`
around lines 46 - 48, Replace the exported interface Props with an unexported
type alias: change "interface Props { dynamicContent?: ReactNode }" to "type
Props = { dynamicContent?: ReactNode }" in AppSidebar.tsx; ensure the
component's props annotation (where Props is referenced) uses this new type and
remove any export on Props so it remains local to the component.

Comment on lines +19 to +67
private async scrollLibraryContainer(
position: "bottom" | "page",
): Promise<void> {
const { getId } = getSelectors(this.page);
const agentCards = getId("library-agent-card");
const cardCount = await agentCards.count();

if (cardCount === 0) {
await this.page.evaluate((targetPosition) => {
if (targetPosition === "bottom") {
window.scrollTo(0, document.body.scrollHeight);
} else {
window.scrollBy(0, window.innerHeight);
}
}, position);
return;
}

const lastAgentCard = agentCards.nth(cardCount - 1);

await lastAgentCard.scrollIntoViewIfNeeded();
await lastAgentCard.evaluate((node, targetPosition) => {
let currentElement: HTMLElement | null = node.parentElement;

while (currentElement) {
const style = window.getComputedStyle(currentElement);
const canScrollVertically =
/(auto|scroll)/.test(style.overflowY) &&
currentElement.scrollHeight > currentElement.clientHeight;

if (canScrollVertically) {
if (targetPosition === "bottom") {
currentElement.scrollTop = currentElement.scrollHeight;
} else {
currentElement.scrollTop += currentElement.clientHeight;
}
return;
}

currentElement = currentElement.parentElement;
}

if (targetPosition === "bottom") {
window.scrollTo(0, document.body.scrollHeight);
} else {
window.scrollBy(0, window.innerHeight);
}
}, position);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

scrollDown() ("page") may behave like scrollToBottom() due to scrollIntoViewIfNeeded on the last card.

For both "bottom" and "page", the helper calls scrollIntoViewIfNeeded() on the last card first. Once the last card is visible, the scroll position is already near the bottom of the scrollable ancestor, so the subsequent scrollTop += clientHeight in "page" mode is essentially a no-op (or clamped). This collapses the distinction between scrollDown() and scrollToBottom(), which could reduce pagination coverage.

Consider skipping scrollIntoViewIfNeeded for the "page" branch, or computing the increment based on current scrollTop rather than first jumping to the last card.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/tests/pages/library.page.ts` around lines 19 -
67, The scrollLibraryContainer helper currently always calls
lastAgentCard.scrollIntoViewIfNeeded(), which makes the "page" (scrollDown)
behavior act like "bottom" (scrollToBottom); modify scrollLibraryContainer so
that when position === "page" you do not call
lastAgentCard.scrollIntoViewIfNeeded() (or alternatively compute and apply an
incremental scroll based on the current scrollTop and clientHeight of the found
scrollable ancestor rather than first forcing the last card into view). Locate
the position parameter and the lastAgentCard variable in scrollLibraryContainer,
remove/guard the scrollIntoViewIfNeeded() call for the "page" branch (or change
the inner evaluation to read currentElement.scrollTop and add
currentElement.clientHeight instead of clamping after an initial scroll), and
keep the existing "bottom" branch behavior unchanged.

@CLAassistant

CLAassistant commented May 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicts Automatically applied to PRs with merge conflicts platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants