Skip to content

desktop: side panel tabs can't be closed and New tab launchers ignore clicks (2.2.2 regression) #60381

Closed

Description

@timrekelj

Summary

Since updating the desktop app from 2.2.1 to 2.2.2, the side panel's New tab view and tab strip stop responding to most clicks: launcher rows (except Terminal and Open file) do nothing, and side-panel tabs can't be closed. Adding tabs with "+" still works. It persists after a window reload, in idle sessions, and with local plugins disabled.

Environment

  • opencode version: Desktop 2.2.2 (bundled CLI 2.2.2); worked in 2.2.1
  • OS: macOS, Darwin 27.2.0 arm64
  • Terminal: Unavailable: desktop app, not a terminal session
  • Shell: /bin/zsh
  • Install/channel: Desktop, release channel latest (auto-updated 2.2.1 → 2.2.2)
  • Active plugins: three local GUI plugins (a GitHub PR/branches panel, a usage panel, an x.com launcher) plus opencode-claude-auth. Reproduced with the GitHub panel plugin disabled; the x.com launcher only registers a menu item and a command.

Reproduction

  1. Run OpenCode Desktop 2.2.2 on macOS and open any session (an idle one reproduces it too).
  2. Open the side panel and click "+" to open a New tab view.
  3. Click a launcher row such as Usage, Review, X or Pull requests. Nothing happens.
  4. Click Terminal or Open file. These work.
  5. Try to close a side-panel tab with its ×, or the "New tab" ×. Nothing happens.
  6. Type in the composer while the New tab view is open. The launcher rows flicker, and an extra row briefly appears under "Open file".

Expected Behavior

Same as 2.2.1: every launcher row opens its tab and the New tab view goes away, and side-panel tabs close with ×.

Actual Behavior

Clicks on most launcher rows and on tab close buttons have no effect. renderer.log shows no errors at the time, only the usual ResizeObserver loop completed with undelivered notifications and Rendering was performed in a subtree hidden by content-visibility messages, which also appear in earlier versions. The renderer isn't hung (CPU idles between clicks).

Additional Context

  • Consistently reproducible since the 2.2.2 update; a reload doesn't help.
  • The rows that work (Terminal, Open file) are the ones that only add a tab. Rows that fail need the pane's active tab to change before the New tab view is dismissed, and closing a tab changes the active tab too.
  • Combined with the flicker while typing, this suggests the strip and launcher rows are re-rendered between pointerdown and pointerup, so the click never lands. This is inferred from the symptoms, not confirmed.
  • v2.2.1...v2.2.2 touches exactly this area: packages/app/src/runtime/plugin/side-region.tsx adds a controlled, non-modal right-click Menu (Close tab / Close others) on the tab strip, plus a closable memo, and changes the empty-pane/launcher rendering. panels.tsx changes dismissNewTab/closeNewTab to call onEmptied. panel-trigger.tsx adds min-w-0 to the trigger wrapper. The new tab-strip Menu and its dismiss/interact-outside handling are a likely first suspect.
  • Possibly caused by the tab-strip context menu added for Desktop UI has some ui/ux/interaction issues regarding Tabs in the Sidebar (/btw, browser, etc) #60233 (that issue is about close-button layout on 2.2.1; this one is a click regression on 2.2.2).
  • Workaround: downgrade to 2.2.1.

Activity

  1. opencodejr commented on Oct 29, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report and for pointing at the v2.2.1...v2.2.2 changes.

    I couldn't reproduce this here: it needs OpenCode Desktop, and the environment I run in can't run the desktop app or load the side panel UI. A maintainer with the desktop app will need to look at it.

    I read the 2.2.2 changes without running them. They match what you found: side-region.tsx adds a right-click menu (Close tab / Close others) for the tab strip with modal={false}, and panels.tsx changes how closing a New tab works (dismissNewTab/closeNewTab now call onEmptied). Either could plausibly stop these clicks from landing, but I haven't confirmed that.

    Related issues:

  2. GETYann commented on Oct 29, 2026

    @GETYann

    hi, me too

  3. spenceryg99 commented on Oct 30, 2026

    @spenceryg99

    me too wait for fix

  4. FsmMeng commented on Oct 30, 2026

    @FsmMeng

    me too

  5. Veras-D commented on Oct 30, 2026

    @Veras-D

    Confirming this on Linux, so it isn't macOS-specific.

    Environment

    • opencode version: Desktop 2.2.2 (amd64 .deb, bundled CLI 2.2.2)
    • OS: Linux, Pop!_OS with the COSMIC desktop (Wayland)
    • Install/channel: Desktop, latest, installed manually with dpkg (the in-app updater can't elevate on this desktop)
    • Plugins: none beyond the bundled ones

    Reproduction

    1. Open a session in a git repo with uncommitted changes.
    2. Open the side panel and click + to get the New tab view.
    3. Click the Review row (shows 8 files changed and CTRL ALT R). The row flashes — along with the Open file row below it — then the New tab view collapses and the panel is left open with an empty content area.
    4. Press Ctrl+Alt+R instead. The panel opens and renders the changed files and their diffs correctly.

    So the review data path is healthy; only the click is lost. Step 4 also rules out the review panel itself, and it persists across an app restart and a fresh session.

    Log signature (same as yours)

    renderer.log has no JS errors at the time, only:

    • ResizeObserver loop completed with undelivered notifications
    • Rendering was performed in a subtree hidden by content-visibility. (debug)

    Consistent with your analysis

    Both entry points run the identical function. The command (id: "open", bind: "ctrl+alt+r") and the menu item (menu: "session.panel", order: 10) both use run: show, where

    const show = () => {
      const session = sessions.current()
      if (session?.project && !layout.narrow()) layout.open("review:changes", session, { tab: "select" })
    }

    The failing row is LauncherRow in side-region.tsx, with onClick={() => { props.onChoose(); props.item.run() }}, so run() is wired correctly and the click simply never reaches it.

    The liveness predicate in panels.tsx makes that permanent:

    const live = (value: NewTab) =>
      value.session === input.view().key && value.over === panes[value.pane].active()
    
    createEffect(() => {
      if (state.adding.some((value) => !live(value))) setState("adding", (list) => list.filter(live))
    })

    Once the pane's active tab moves, the New tab view is filtered out of state.adding for good, so the row unmounts between pointerdown and pointerup and no click is dispatched. Terminal and Open file survive because they only append a tab and don't need the active tab to change before the view is dismissed.

    That matches your read of the v2.2.1...v2.2.2 diff: the non-modal right-click Menu on the strip plus the capture-phase pointerdown/click/keydown listener added in side-region.tsx is the prime suspect.

    Workarounds confirmed here

    • The Ctrl+Alt+R keybind runs the same show() and works.
    • Downgrading to 2.2.1 is reported to fix it; 2.2.2 is still the newest release.
  6. Albert-Deng commented on Oct 30, 2026

    @Albert-Deng

    Confirming this on Windows with desktop 2.2.2, and narrowing the failing surface a bit further.

    The × on a terminal side-panel tab does nothing, but the same tab closes fine through the other three paths:

    Interaction Result
    × (left click) on the tab nothing (hover tooltip shows, so the button is hit-testable)
    Middle-click the tab closes (tab + PTY)
    Right-click → Close tab closes (tab + PTY)
    Ctrl+W with focus in the terminal closes (tab + PTY)

    So the strip, layout state and PTY teardown are all healthy here — only the <IconButton> click never lands, which matches the re-render-between-pointerdown-and-pointerup cause in #60436.

    Launcher rows behave the same as reported: Terminal and Open file work, Review/others do not.

    Environment:

    • opencode: Desktop 2.2.2 (bundled CLI 2.2.2, latest channel, no plugins configured)
    • OS: Windows 10.0.19044 x64
    • renderer.log: no errors around the clicks, only ResizeObserver loop completed with undelivered notifications and Rendering was performed in a subtree hidden by content-visibility

    Workaround for anyone hitting this before 2.2.3: middle-click the tab, or Ctrl+W with the terminal focused.

  7. Albert-Deng commented on Oct 30, 2026

    @Albert-Deng

    Confirming this regression on macOS as well, with one scope difference that may matter for verifying #60436.

    Environment: OpenCode Desktop 2.2.2 (Electron 44.9.1 / Chrome 152.0.7977.167), macOS 26.4.1 (Darwin 25.4.0, arm64), no plugins.

    1. All three launchers fail together, not only factory-registered ones. In our case Review, Terminal and Open file all stopped responding in the same renderer, across 7 consecutive real clicks. Desktop 2.2.2: right pane New tab launchers lose mouse clicks when their menu item is registered by a factory #60540 describes only Review breaking while Terminal / Open file still work, so the affected set may be broader than the factory-registration condition.

    2. Any pointerdown rebuilds all three rows. With a MutationObserver on [data-slot=tabs-content], a single press anywhere in the window — on a launcher row, on empty panel space, or in the chat area — produces 6 childList mutations that remove and re-insert the three launcher <button> nodes. The press itself invalidates the list, independent of which row is targeted.

    3. Raw event trace from a failing click (capture-phase listeners, CDP attached):

      {k:pointerdown, x:694.4, y:220.5, target:BUTTON, inRow:true,  trusted:true}
      {k:click,       x:694,   y:220,   target:DIV,    inRow:false, hasHandler:false, trusted:true}
      

      The down lands on the row <button>; the click is delivered to the row container <div>, so $$click (onChoose(); item.run()) never runs. The same coordinates in a freshly restarted renderer behave correctly (down/up/click all on the same BUTTON, downConnected:true, same:true), and synthetic trusted input via Input.dispatchMouseEvent always works — including a 900 ms press and ±1 px jitter — so it needs a real pointer sequence plus a render inside the press/release window.

    Since the rebuild hits all three rows regardless of how the menu item is registered, the stable ${plugin}:${id} keying in #60436 looks right; just flagging that "only factory-registered launchers break" is likely too narrow a description of the symptom.

    Reproduced on 2.2.2; happy to retest once a build including #60436 ships.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions