Skip to content

fix: use onClick instead of onSelect for base-ui context menu items - #87

Merged
akemmanuel merged 11 commits into
masterfrom
fix/context-menu-onclick
Jun 13, 2026
Merged

akemmanuel merged 11 commits into
masterfrom
fix/context-menu-onclick

Conversation

@touch2be

Copy link
Copy Markdown
Collaborator

Problem

All right-click context menu actions were silently non-functional (pin, rename, color, move to project, delete, etc.).

Root Cause

The project migrated from Radix UI to @base-ui/react/context-menu, but the context menu components still used Radix UI's onSelect prop. In base-ui, ContextMenu.Item renders a <div> element — and <div>s don't fire select DOM events, so onSelect was silently ignored.

The three-dots dropdown (DropdownMenu) correctly used onClick and worked fine.

Fix

Replaced all onSelect with onClick in:

  • src/components/SessionContextMenu.tsx — session right-click menu (pin, rename, color, tags, move to project, delete)
  • src/components/SidebarItemMenus.tsx — project right-click menu (new session, collapse, pin, copy path, open in file browser/terminal, worktrees, remove)

touch2be and others added 11 commits June 11, 2026 11:20
# Conflicts:
#	src/components/AppSidebar.tsx
# Conflicts:
#	src/components/DiscoverPlugins.tsx
# Conflicts:
#	BetterSDK/src/sessions.ts
#	src/components/sidebar/use-sidebar-model.ts
base-ui's ContextMenu.Item uses onClick, not Radix UI's onSelect.
Passing onSelect to a <div> element fires no event, making all
right-click context menu actions silently non-functional.

Affected menus:
- SessionContextMenu: pin, rename, color, tags, move to project, delete
- SidebarItemMenus (context variant): new session, collapse, pin,
  copy path, open in file browser/terminal, worktrees, remove project
@github-actions

Copy link
Copy Markdown

Code Review: fix/context-menu-onclick

Scope Mismatch (Medium Severity)

PR title/description claims this is only about onSelect → onClick in two context menu files, but the diff touches 21 files with 4 independent functional changes:

  • ✅ Context menu onSelect → onClick (SessionContextMenu.tsx, SidebarItemMenus.tsx)
  • ✅ Session title cleaning (cleanSessionTitle in lib + BetterSDK)
  • ✅ Sidebar model refactor + preferred harness sorting
  • ✅ Opencode bridge: skills list merging + full server restart on skill install
  • ✅ _backendId legacy field migration support
  • ✅ PluginsSettings refreshProviders after plugin actions
  • ✅ Vite config task removal + electron package.json generation

These should have been separate PRs for reviewability.


1. Code Duplication — cleanSessionTitle (Medium)

The function is defined twice with the same logic:

File Line
src/lib/session-title.ts exported
BetterSDK/src/sessions.ts:140 local function

BetterSDK/src/sessions.ts should import cleanSessionTitle from src/lib/session-title.ts instead of maintaining a duplicate.


2. Aggressive Teardown on Skills Change (Low-Medium)

opencode-bridge.ts — refreshLocalOpenCodeAfterSkillsChange:

for (const state of windowStates.values()) {
  for (const conn of state.projectRegistry.values()) conn.teardown();
  state.projectRegistry.clear();
}

Tears down all connections across all windows after a single skill install/update/remove. This is unnecessarily broad — a skill change in one project directory shouldn't require restarting all other windows' connections. Consider scoping the teardown to only the affected directory's connections.


3. vite.config.ts Run Tasks Removed (Medium)

All run.tasks (dev, start, dist:linux, dist:mac, dist:win, mobile:*) were deleted from vite.config.ts. These are Vite+ task definitions used by vp dev, vp start, etc. If Vite+ doesn't provide built-in defaults for these tasks, this is a breaking change.

The package.json start script was changed from pnpm exec to vp exec, which only makes sense if the task still exists. Verify these tasks are either built-in to Vite+ or defined elsewhere.


4. Type Safety — Test Uses as Session (Low)

src/components/sidebar/use-sidebar-model.test.ts:

function session(id: string, harnessId: HarnessId, updated: number): Session {
  return {
    id,
    title: id,
    // ...
  } as Session;  // <-- type assertion masks missing required fields
}

This won't catch future required fields added to Session. Consider satisfies Session return type annotation on the factory function, a proper builder, or using Partial<Session> and letting the test fail at compile time if new required fields appear.


5. Duplicated Regex Pattern (Low)

BetterSDK/src/sessions.ts uses an inline regex literal:

/^(?:human|assistant|user)\s*:\s*/i

src/lib/session-title.ts uses a module-level const:

const ROLE_PREFIX_PATTERN = /^(?:human|assistant|user)\s*:\s*/i;

If importing from lib isn't possible (different build target), at least the regex should use the same pattern definition to avoid accidental divergence.


6. Minor: undefined vs null Semantics (Informational)

In App.tsx → AppSidebar:

highlightedSessionId={activeView === "chat" ? sessionActiveId : null}

And in AppSidebar:

const visibleActiveSessionId =
    highlightedSessionId === undefined ? activeSessionId : highlightedSessionId;

When activeView !== "chat", null is passed. Since null !== undefined, this overrides activeSessionId with null. If this behavior is intended (no highlighted session outside chat view), it's fine, but the null → override activeSessionId logic is non-obvious. A more explicit check (highlightedSessionId != null or a separate boolean) would be clearer.


Confidence Score: 4/5

The PR is functionally correct and the tests pass, but the scope creep (single-fix title, 21-file diff), duplicated code, and missing justification for the vite config changes reduce confidence. The core onSelect → onClick fix is sound.

New%20session%20-%202026-06-13T15%3A02%3A12.285Z
opencode session  |  github run

@akemmanuel
akemmanuel merged commit 07a4dc9 into master Jun 13, 2026
1 check passed
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.

2 participants