Skip to content

feat(frontend): @agenta/navigation-ui — the rail's renderers and switchers, antd-free - #5867

Draft
ardaerzin wants to merge 3 commits into
pkg/navigationfrom
pkg/navigation-ui
Draft

feat(frontend): @agenta/navigation-ui — the rail's renderers and switchers, antd-free#5867
ardaerzin wants to merge 3 commits into
pkg/navigationfrom
pkg/navigation-ui

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

The rendered half of the sidebar: the nav item renderers, the project and workspace switchers,
and the rail's own layout, rebuilt without antd so the mobile app can mount the same components.

Pairs with @agenta/navigation below it — that lane owns the model, this one owns the pixels.
Not run in a browser — static gates only (pnpm lint-fix 24/24, tsc --noEmit clean
for @agenta/shared, ui, entities, entity-ui, settings-ui, oss, ee, mobile).

Stacked on pkg/navigation; review only this lane's diff.

@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 10, 2026 2:57pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 70fc2f5b-7036-4377-86f2-841caa556a17

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added responsive mobile navigation with drawer and project switching.
    • Added persistent desktop navigation rail with expandable sections.
    • Added reusable project, organization, workflow, theme, and creation controls.
    • Added loading states, back navigation, and responsive sidebar layouts.
    • Mobile projects now open directly to their app home.
  • Bug Fixes

    • Improved project selection fallback and handling for unavailable or empty project lists.
    • Updated logo accents to adapt to light and dark themes.
  • Refactor

    • Standardized sidebar and navigation experiences across the application.

Walkthrough

The PR adds the @agenta/navigation-ui package, migrates OSS sidebar components to shared navigation views, and adds a responsive mobile navigation shell. Mobile context resolution now routes directly to project app pages.

Changes

Mobile context and navigation

Layer / File(s) Summary
Mobile layout and UI primitives
web/mobile/src/components/AgentaLogo.tsx, web/mobile/src/components/ContentRail.tsx, web/mobile/src/components/ui/sheet.tsx
The logo uses theme-specific mark colors. ContentRail provides centered content bounds. Radix Sheet primitives support responsive drawers.
Automatic project context resolution
web/mobile/src/features/context/ContextResolver.tsx, web/mobile/src/features/context/contextTarget.ts, web/mobile/src/features/context/useBindProjectContext.ts, web/mobile/tests/unit/contextTarget.test.ts
Context resolution selects project app routes, redirects unauthenticated results to /auth, and falls back to the first project. Tests cover the updated selection rules.
Responsive mobile navigation
web/mobile/src/features/nav/*
The mobile shell adds persistent and drawer navigation, session entries, project switching, logout handling, group persistence, and navigation callbacks.

Shared navigation UI package

Layer / File(s) Summary
Package setup and public exports
web/packages/agenta-navigation-ui/package.json, web/packages/agenta-navigation-ui/eslint.config.mjs, web/packages/agenta-navigation-ui/tsconfig.json, web/packages/agenta-navigation-ui/src/index.ts
The new package defines build, lint, TypeScript, dependency, import restriction, and public export configuration.
Shared navigation menu
web/packages/agenta-navigation-ui/src/NavMenu.tsx
NavMenu supports inline and collapsed navigation, selection, expansion, links, flyouts, states, tooltips, and keyboard controls.
Project, organization, and workflow views
web/packages/agenta-navigation-ui/src/ProjectOrgSwitcher.tsx, web/packages/agenta-navigation-ui/src/WorkflowPickerView.tsx, web/packages/agenta-navigation-ui/src/NamePromptModal.tsx
Reusable views provide project and organization switching, workflow selection, theme controls, creation actions, and name validation.
Sidebar controls and loading state
web/packages/agenta-navigation-ui/src/SidebarBackButton.tsx, web/packages/agenta-navigation-ui/src/SidebarSelectionButton.tsx, web/packages/agenta-navigation-ui/src/SidebarSkeletonLoader.tsx
Shared components provide sidebar back navigation, selection controls, and collapsed or expanded loading placeholders.

OSS sidebar migration

Layer / File(s) Summary
Project and organization switcher migration
web/oss/src/components/Sidebar/components/ProjectOrgSwitcher/index.tsx, web/oss/src/components/Sidebar/hooks/useProjectOrgSwitcher.ts
The OSS switcher uses shared views and name prompts. Creation controls now submit names directly without Ant Design forms.
Workflow picker migration
web/oss/src/components/Sidebar/components/WorkflowPicker.tsx, web/oss/src/components/Sidebar/hooks/useWorkflowSwitcher.tsx
Workflow data now uses shared picker entries and selection callbacks.
Sidebar shell and menu integration
web/oss/src/components/Sidebar/engine/SidebarShell.tsx, web/oss/src/components/Sidebar/engine/SidebarMenu.tsx
SidebarShell uses NavMenu and native layout elements. The local SidebarMenu implementation was removed.
Sidebar control integration
web/oss/src/components/Sidebar/components/SidebarBackButton.tsx, web/oss/src/components/Sidebar/components/SidebarSelectionButton.tsx, web/oss/src/components/Sidebar/components/SidebarSkeletonLoader.tsx
Existing wrappers now delegate rendering to shared sidebar components.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NavDrawer
  participant NavPanel
  participant NavMenu
  participant DrawerProjectSwitcher
  NavDrawer->>NavPanel: render the responsive navigation panel
  NavPanel->>NavMenu: provide Home and Sessions entries
  NavPanel->>DrawerProjectSwitcher: provide active project and workspace data
  NavMenu->>NavDrawer: report link navigation
  DrawerProjectSwitcher->>NavDrawer: navigate or log out
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: an Ant Design-free navigation UI package with rail renderers and switchers.
Description check ✅ Passed The description directly explains the navigation UI implementation, switchers, rail layout, Ant Design removal, and mobile reuse.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pkg/navigation-ui

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.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (5)
web/mobile/src/components/AgentaLogo.tsx (1)

21-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace hard-coded logo colors with supported theme values.

fill-[#1E1C1D] and dark:fill-[#F2F25C] bypass the theme token system. Use an approved semantic Tailwind color or a supported var(--ag-color*) value instead. Run pnpm lint-fix from web before committing.

Source: Coding guidelines

web/mobile/src/components/ContentRail.tsx (1)

5-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Shorten this implementation comment.

This comment describes ordinary layout behavior. Remove it or reduce it to one short line. Keep long comments only for surprising constraints.

Source: Coding guidelines

web/oss/src/components/Sidebar/engine/SidebarShell.tsx (1)

268-273: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a supported semantic color token for the sidebar background.

bg-[var(--ag-sidebar-bg)] bypasses the supported semantic-token contract. Replace it with an Ant Design semantic token, a Tailwind color utility, or a supported var(--ag-color*) value. Verify the replacement in both light and dark themes.

As per coding guidelines, consume theme colors through semantic tokens, Tailwind color utilities, or supported var(--ag-color*) variables. Based on learnings, validate color-token replacements in both appearances.

Sources: Coding guidelines, Learnings

web/mobile/src/features/nav/DrawerProjectSwitcher.tsx (1)

26-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move project fetching to an atomWithQuery atom.

This new API query uses useQuery directly. Put the query in the appropriate mobile Jotai store and consume it with an atom hook. Keep the query key, stale time, and fetch function in that atom.

As per coding guidelines, use atomWithQuery with TanStack Query for API data fetching.

Source: Coding guidelines

web/packages/agenta-navigation-ui/src/SidebarSelectionButton.tsx (1)

18-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the implementation comment.

This comment describes normal component behavior. Reduce it to one short line. Remove the migration history from the source file.

Proposed change
-/**
- * The rail's selection trigger (org/project, workflow): avatar + label + caret, collapsing
- * to the avatar alone with the rail. Ported from the OSS antd Button verbatim, styling and
- * transitions included.
- */
+/** Sidebar selection trigger for organization, project, and workflow. */

As per coding guidelines, “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 933c2c3a-a05e-413a-9dae-538b8c2a08f2

📥 Commits

Reviewing files that changed from the base of the PR and between 581d662 and 6d24e20.

📒 Files selected for processing (39)
  • web/mobile/src/components/AgentaLogo.tsx
  • web/mobile/src/components/ContentRail.tsx
  • web/mobile/src/components/ui/sheet.tsx
  • web/mobile/src/features/context/ContextResolver.tsx
  • web/mobile/src/features/context/ProjectList.tsx
  • web/mobile/src/features/context/ProjectSwitcher.tsx
  • web/mobile/src/features/context/WorkspaceSelector.tsx
  • web/mobile/src/features/context/contextTarget.ts
  • web/mobile/src/features/context/states/SignedOutNotice.tsx
  • web/mobile/src/features/context/useBindProjectContext.ts
  • web/mobile/src/features/nav/AppShell.tsx
  • web/mobile/src/features/nav/DrawerProjectSwitcher.tsx
  • web/mobile/src/features/nav/NavDrawer.tsx
  • web/mobile/src/features/nav/NavPanel.tsx
  • web/mobile/src/features/nav/NavRail.tsx
  • web/mobile/src/features/nav/useMobileNavItems.tsx
  • web/mobile/tests/unit/contextTarget.test.ts
  • web/oss/src/components/Sidebar/components/ProjectOrgSwitcher/index.tsx
  • web/oss/src/components/Sidebar/components/SidebarBackButton.tsx
  • web/oss/src/components/Sidebar/components/SidebarSelectionButton.tsx
  • web/oss/src/components/Sidebar/components/SidebarSkeletonLoader.tsx
  • web/oss/src/components/Sidebar/components/WorkflowPicker.tsx
  • web/oss/src/components/Sidebar/engine/SidebarMenu.tsx
  • web/oss/src/components/Sidebar/engine/SidebarShell.tsx
  • web/oss/src/components/Sidebar/hooks/useDropdownItems/index.tsx
  • web/oss/src/components/Sidebar/hooks/useDropdownItems/types.d.ts
  • web/oss/src/components/Sidebar/hooks/useProjectOrgSwitcher.ts
  • web/oss/src/components/Sidebar/hooks/useWorkflowSwitcher.tsx
  • web/packages/agenta-navigation-ui/eslint.config.mjs
  • web/packages/agenta-navigation-ui/package.json
  • web/packages/agenta-navigation-ui/src/NamePromptModal.tsx
  • web/packages/agenta-navigation-ui/src/NavMenu.tsx
  • web/packages/agenta-navigation-ui/src/ProjectOrgSwitcher.tsx
  • web/packages/agenta-navigation-ui/src/SidebarBackButton.tsx
  • web/packages/agenta-navigation-ui/src/SidebarSelectionButton.tsx
  • web/packages/agenta-navigation-ui/src/SidebarSkeletonLoader.tsx
  • web/packages/agenta-navigation-ui/src/WorkflowPickerView.tsx
  • web/packages/agenta-navigation-ui/src/index.ts
  • web/packages/agenta-navigation-ui/tsconfig.json
💤 Files with no reviewable changes (7)
  • web/oss/src/components/Sidebar/hooks/useDropdownItems/types.d.ts
  • web/mobile/src/features/context/states/SignedOutNotice.tsx
  • web/mobile/src/features/context/WorkspaceSelector.tsx
  • web/mobile/src/features/context/ProjectSwitcher.tsx
  • web/oss/src/components/Sidebar/engine/SidebarMenu.tsx
  • web/oss/src/components/Sidebar/hooks/useDropdownItems/index.tsx
  • web/mobile/src/features/context/ProjectList.tsx

Comment on lines +27 to +30
const query = useQuery({
queryKey: ["mobile", "projects"],
queryFn: () => fetchProjects(),
enabled: !shortcut,
enabled: !stored,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate persisted context against current project access.

readLastContext() only validates stored JSON structure. If the stored project was deleted, access was revoked, or the session expired, /m/ skips fetchProjects(), redirects to the stale URL, and cannot redirect to /auth or select the first valid project.

  • web/mobile/src/features/context/ContextResolver.tsx#L27-L30: fetch project state even when persisted context exists, and wait for that result before redirecting.
  • web/mobile/src/features/context/contextTarget.ts#L27-L29: return shortcut only when its workspace and project exist in groups; otherwise continue with desktop continuity and first-project fallback.
  • web/mobile/tests/unit/contextTarget.test.ts#L60-L77: add coverage for a persisted shortcut whose project is absent from groups.
📍 Affects 3 files
  • web/mobile/src/features/context/ContextResolver.tsx#L27-L30 (this comment)
  • web/mobile/src/features/context/contextTarget.ts#L27-L29
  • web/mobile/tests/unit/contextTarget.test.ts#L60-L77

Comment on lines +61 to +62
// There is no picker page — switching lives in the drawer, so the resolver always lands
// somewhere and the drawer corrects it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove or shorten this comment.

The test name and assertion already describe the fallback behavior. This two-line comment exceeds the configured one-short-line limit.

Source: Coding guidelines

Comment on lines +59 to +66
<label className="text-xs text-colorTextSecondary">{label}</label>
<input
autoFocus
value={name}
placeholder={placeholder}
onChange={(event) => setName(event.target.value)}
className="box-border h-8 w-full rounded-md border border-solid border-colorBorder bg-colorBgContainer px-2 text-[13px] text-colorText outline-none focus:border-colorPrimary"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

fd -a package.json web -x jq -r '"\(.name): react=\(.dependencies.react // .devDependencies.react // "inherited")"'
rg -n --glob '*.{ts,tsx}' '\buseId\s*\(' web/packages/agenta-navigation-ui web/oss | head -n 30

Repository: Agenta-AI/agenta

Length of output: 1405


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

sed -n '1,120p' web/packages/agenta-navigation-ui/src/NamePromptModal.tsx

Repository: Agenta-AI/agenta

Length of output: 2287


Associate the NamePromptModal label with the input.

The label has no htmlFor and the input has no id. Click the label to focus the input by generating a stable ID with useId, setting htmlFor on the label, and id on the input.

Comment on lines +98 to +99
onClick={(event) => {
event.stopPropagation()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow link clicks to reach the navigation host.

NavPanel uses the bubbled link click at web/mobile/src/features/nav/NavPanel.tsx Line 56 to call onNavigate. event.stopPropagation() prevents that callback, so NavDrawer stays open after inline link navigation. Remove the propagation stop after link handling.

Comment on lines +141 to +175
const FlyoutChildren = ({items, selectedKeys}: {items: NavItem[]; selectedKeys: string[]}) => (
<>
{items.map((child) =>
child.isPlaceholder ? (
<DropdownMenuLabel key={child.key} className="text-xs text-colorTextTertiary">
{child.title}
</DropdownMenuLabel>
) : child.divider ? (
<DropdownMenuSeparator key={child.key} />
) : (
<DropdownMenuItem
key={child.key}
disabled={child.disabled}
className={clsx(selectedKeys.includes(child.key) && "font-medium")}
asChild={Boolean(child.link)}
onSelect={child.link ? undefined : () => child.onClick?.(undefined as never)}
>
{child.link ? (
<Link
href={child.link}
className="!text-inherit no-underline"
target={isExternal(child.link) ? "_blank" : undefined}
rel={isExternal(child.link) ? "noopener noreferrer" : undefined}
onClick={(event) => child.onClick?.(event)}
>
{child.title}
</Link>
) : (
<span>{child.title}</span>
)}
</DropdownMenuItem>
),
)}
</>
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply controlled selection in flyout children.

FlyoutChildren does not receive onItemSelect. When a collapsed or vertical group contains a linked child, the child navigates directly instead of invoking controlled selection. It also ignores child.inert. Thread the selection callback into this component and apply the same inert and controlled-navigation rules as RowLabel.

Comment on lines +221 to +235
<button
type="button"
aria-label={item.title}
className={clsx(
"mx-auto flex cursor-pointer items-center rounded-md border-0 bg-transparent",
ROW_INTERACTIVE,
selected && ROW_SELECTED,
collapsed
? "size-8 justify-center"
: "h-9 w-[94%] justify-start gap-2 px-3 text-xs",
)}
>
{item.icon}
{!collapsed ? <span>{item.title}</span> : null}
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable disabled group triggers.

A disabled group still renders an enabled button with ROW_INTERACTIVE. The user can open its flyout. Set disabled={item.disabled} and use ROW_DISABLED when the group is disabled.

RowLabel's link handler opened with an unconditional stopPropagation. It was
dead defense — rowClickHandler already returns undefined whenever item.link is
set, so the ancestor row it guarded against has no handler — and it broke the
mobile nav drawer, which closes itself by listening for the link click on its
way up (NavPanel's notifyOnNavigate).

The three <Link> paths now share one linkClickHandler, so the flyout honors
child.inert and routes through controlled selection exactly like an inline row
instead of navigating past the host. A disabled group also stops opening its
flyout: the trigger takes item.disabled and ROW_DISABLED, matching the leaf.

NamePromptModal's label gets a useId htmlFor, giving the input an accessible
name and a clickable label.
The root resolver was the only thing that could route a signed-out session to
the sign-in page, and it is the one screen that never gets to: a remembered
pair skipped the projects fetch entirely (enabled: !stored) and redirected on
the first render, so the unauthenticated branch was dead for every returning
user. They landed on a screen that could not load, with no way to sign in.

AuthGate moves the verdict app-wide, next to ContextSync. It reads the same
projects query key and staleTime every screen already uses, so it costs no
request of its own, and it is off on /auth* so it cannot loop on the page it
redirects to.

The resolver keeps its fast path — a stored pair still forwards before any
network — but the fetch now always runs, and selectContextTarget drops a
shortcut the fetched tree no longer holds. "No longer holds" is gated on an
explicit groupsLoaded flag rather than groups.length, because an empty groups
means "not fetched yet" far more often than it means "no projects", and the
fast path must survive that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant