Skip to content

fix(frontend): render Settings icon as a link so it supports Open in new tab#802

Merged
hieptl merged 3 commits into
mainfrom
hieptl/app-1970
May 27, 2026
Merged

fix(frontend): render Settings icon as a link so it supports Open in new tab#802
hieptl merged 3 commits into
mainfrom
hieptl/app-1970

Conversation

@hieptl
Copy link
Copy Markdown
Contributor

@hieptl hieptl commented May 27, 2026

  • A human has tested these changes.

Why

Summary

Image

Right-clicking the Settings (gear) icon in the sidebar shows the browser's generic page context menu — there's no "Open Link in New Tab" option. Cmd/Ctrl-click and middle-click also fail to open Settings in a new tab. This affects both the expanded sidebar (gear next to the "Local" backend status) and the collapsed sidebar (gear at the bottom of the rail).

Steps to reproduce

  1. Clone agent-canvas and run npm run dev.
  2. Open http://localhost:3001 and ensure the sidebar is visible.
  3. Right-click the Settings gear icon at the bottom of the sidebar.

Actual behavior

The browser shows the generic page context menu (Back, Forward, Reload, Save As…, Print…, etc.). "Open Link in New Tab" is not present. Cmd/Ctrl-click and middle-click do nothing useful either.

Expected behavior

Right-clicking the Settings icon should expose the standard link context menu, including "Open Link in New Tab". Cmd/Ctrl-click and middle-click should open /settings in a new tab, matching the behavior of the other sidebar entries (New Chat, Customize, Automate).

Root cause

Both Settings entry points were rendered as <button onClick={() => navigate("/settings")}>. Browsers only expose link-style context menu options and honor modifier-clicks for <a> elements with an href. Other sidebar items don't have this issue because they go through SidebarNavLinkNavigationLink, which renders a proper <a href="…"> while still intercepting plain left-clicks for SPA navigation.

Affected elements:

  • src/components/features/backends/backend-selector.tsx — Settings gear shown next to the backend dropdown (expanded sidebar).
  • src/components/features/sidebar/sidebar-rail-body.tsx — Settings icon at the bottom of the collapsed sidebar.

Acceptance criteria

  • Right-click on either Settings icon shows "Open Link in New Tab" in the browser context menu.
  • Cmd-click (macOS) / Ctrl-click (Linux/Windows) opens /settings in a new tab without disrupting the current tab.
  • Middle-click opens /settings in a new tab.
  • Plain left-click still navigates in-place to /settings via SPA routing (no full reload).
  • Active-state styling on /settings and its sub-routes is preserved.
  • Existing sidebar test suite passes (the one test that asserts the navigate call signature is updated to match NavigationLink's (to, { replace }) shape).

Summary

  • Right-clicking the Settings gear in the sidebar did not show "Open Link in New Tab", and modifier/middle clicks couldn't open Settings in a new tab.
  • Both Settings entry points (the expanded-sidebar gear next to the backend dropdown, and the collapsed-sidebar Settings icon) were rendered as <button onClick={navigate(...)}>. Browsers only treat <a href> elements as links for context-menu and modifier-click purposes.
  • Replaced both buttons with the existing NavigationLink component (already used by New Chat / Customize / Automate). It renders a real <a href="/settings"> while intercepting plain left-clicks for SPA navigation, so the click-to-navigate behavior, active styling, and tooltip wrapping are unchanged — only right-click / modifier-click / middle-click now do the right thing.
  • Removed the now-unused navigate prop from SidebarRailBody (and the pass-through in Sidebar).

Files changed

  • src/components/features/backends/backend-selector.tsx
  • src/components/features/sidebar/sidebar-rail-body.tsx
  • src/components/features/sidebar/sidebar.tsx

Issue Number

Resolves #801

How to Test

  1. Clone the agent-server-gui repository and start the development server using npm run dev.
  2. Open the sidebar.
  3. Right-click the Settings icon.

Video/Screenshots

output

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

🐳 Docker images for this PR

GHCR package: https://github.com/OpenHands/agent-canvas/pkgs/container/agent-canvas

Component Value
Image ghcr.io/openhands/agent-canvas
Architectures amd64, arm64
Agent Server ghcr.io/openhands/agent-server:1.23.1-python
Automation openhands-automation==1.0.0a5
Commit be4a38b0fcb04aaee681e7861c6ff6b7495825b8

Pull (multi-arch manifest)

# Multi-arch manifest — Docker automatically pulls the correct architecture
docker pull ghcr.io/openhands/agent-canvas:sha-be4a38b

Run

docker run -it --rm \
  -p 8000:8000 \
  ghcr.io/openhands/agent-canvas:sha-be4a38b

All tags pushed for this build

ghcr.io/openhands/agent-canvas:sha-be4a38b-amd64
ghcr.io/openhands/agent-canvas:hieptl-app-1970-amd64
ghcr.io/openhands/agent-canvas:pr-802-amd64
ghcr.io/openhands/agent-canvas:sha-be4a38b-arm64
ghcr.io/openhands/agent-canvas:hieptl-app-1970-arm64
ghcr.io/openhands/agent-canvas:pr-802-arm64
ghcr.io/openhands/agent-canvas:sha-be4a38b
ghcr.io/openhands/agent-canvas:hieptl-app-1970
ghcr.io/openhands/agent-canvas:pr-802

About Multi-Architecture Support

  • Each tag (e.g., sha-be4a38b) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., sha-be4a38b-amd64) are also available if needed

@hieptl hieptl self-assigned this May 27, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

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

Project Deployment Actions Updated (UTC)
agent-canvas Ready Ready Preview, Comment May 27, 2026 10:21am

Request Review

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

Good fix for a real UX issue. The code changes are clean and use the correct component pattern.

🟠 Important - Test needs updating:

The test at __tests__/components/features/sidebar/sidebar.test.tsx:313 expects navigate to be called with one argument:

expect(navigate).toHaveBeenCalledWith("/settings");

But NavigationLink (which replaced the button in this PR) calls navigate(to, { replace }) with two arguments. Update to:

expect(navigate).toHaveBeenCalledWith("/settings", { replace: false });

This test will fail in CI until fixed.


Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26504976910

Copy link
Copy Markdown
Contributor

all-hands-bot commented May 27, 2026

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

Clean fix for a real UX issue. The implementation correctly uses NavigationLink (which renders <a href>) to enable browser link behaviors (right-click context menu, Cmd/Ctrl-click, middle-click) while preserving SPA navigation on plain clicks.

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW
    Isolated UX fix that replaces buttons with proper link elements. Uses an existing, well-tested component (NavigationLink) that other sidebar items already rely on. No breaking changes to API surface or data flow. Test coverage updated correctly.

Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26505658240

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

Clean, minimal fix for a genuine UX limitation — replacing <button onClick={navigate(...)}> with NavigationLink is exactly the right approach since browsers only expose link context menus and honor modifier/middle-clicks for <a href> elements.

What I reviewed:

  • backend-selector.tsx — Settings gear in the expanded sidebar. NavigationLink to="/settings" correctly renders an <a href="/settings">. Active styling is preserved via the existing isSettingsActive conditional className and data-active attribute.
  • sidebar-rail-body.tsx — Settings icon in the collapsed rail. Clean replacement; navigate prop correctly removed from both the interface and the parent (sidebar.tsx) since it is no longer needed.
  • sidebar.tsxnavigate properly dropped from the useNavigation() destructure and the SidebarRailBody prop spread.
  • Testexpect(navigate).toHaveBeenCalledWith("/settings", { replace: false }) correctly matches NavigationLink's actual call site (navigate(to, { replace } satisfies NavigationOptions) with replace defaulting to false).

Minor observation (non-blocking): In backend-selector.tsx, active state is now tracked twice — isSettingsActive (via useMatch) drives the conditional className string, while NavigationLink also computes its own isActive internally (used only for aria-current). Both should always agree for /settings, so this is not a correctness issue. If the useMatch hooks are cleaned up in a future pass, NavigationLink's className function prop (className={({ isActive }) => ...}) could take over the active-class decision — but that would also require preserving data-active separately. Fine to leave as-is.

This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

github-actions Bot added a commit that referenced this pull request May 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📸 Snapshot Test Report

✅ No baseline found on main — all 1 snapshot are new and will become the baseline once this PR merges.

Category Count
🔴 Changed 0
🆕 New 1
✅ Unchanged 0
Total 1
🆕 New snapshots (1)

These snapshots have no baseline on main and will become the new baseline once this PR merges.

settings-page

add-backend-modal

new snapshot


Generated by the Snapshot Tests workflow. This comment was created by an AI agent (OpenHands) on behalf of the repo maintainers.

@hieptl hieptl merged commit 4061723 into main May 27, 2026
21 of 22 checks passed
@hieptl hieptl deleted the hieptl/app-1970 branch May 27, 2026 11:35
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.

Sidebar Settings icon doesn't expose "Open Link in New Tab"

2 participants