Skip to content

feat(desktop): background terminal sessions in v2 tab bar + restore move-to-background button#4459

Merged
Kitenite merged 2 commits into
mainfrom
background-terminal-tabs
May 12, 2026
Merged

feat(desktop): background terminal sessions in v2 tab bar + restore move-to-background button#4459
Kitenite merged 2 commits into
mainfrom
background-terminal-tabs

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 12, 2026

What

  • New BackgroundTerminalsButton in the v2-workspace tab bar. Surfaces running terminal daemon sessions for the workspace that have no pane attached. Renders nothing when there are none; otherwise a single button — "N background terminal session(s)" — with a dropdown. Selecting an entry re-opens it as a new terminal tab (re-attaches to the existing terminalId); the hover trash icon kills the session.
  • Restored the "Move terminal to background" archive button in the v2 terminal pane header (it was removed in feat(desktop): move v2 terminal ws reconnect notices to a pane-side log #3888). Calls markTerminalForBackground(terminalId) then context.actions.close(), so the pane closes but the daemon session keeps running — that's how you produce a background session in the first place.
  • @superset/panes: new renderTabBarTrailing render-prop on <Workspace>, threaded into TabBar and rendered at the trailing edge of the tab-bar row.

Notes

  • Background sessions get their name from the host-service-tracked OSC title (listSessionssession.title), falling back to "Terminal" when the shell never emitted one — same as a paned terminal would show.

Test plan

  • Open a v2 workspace, click the archive icon in a terminal pane header → pane closes, session keeps running.
  • Within ~5s the "1 background terminal session" button appears at the right of the tab bar.
  • Click it → dropdown lists the session; click the entry → re-opens as a tab and re-attaches (scrollback intact).
  • Trash icon → session killed, button disappears.
  • bun run typecheck and bun run lint pass.

Summary by cubic

Surfaces background terminal sessions in the v2 workspace tab bar and restores the “move to background” control to manage running terminals. Adds a renderTabBarTrailing slot to @superset/panes to host the new button.

  • New Features

    • New BackgroundTerminalsButton in the v2 tab bar: shows “N background terminal session(s)”, dropdown to reopen as a new tab (re-attaches to existing terminalId), and a trash icon to kill sessions.
    • Restored “Move terminal to background” button in the terminal pane header: calls markTerminalForBackground(terminalId) and closes the pane, keeping the daemon running.
    • @superset/panes: added renderTabBarTrailing render-prop on <Workspace> (threaded into TabBar) to render trailing tab bar content.
  • Bug Fixes

    • Kill button disabled state is scoped per session row while a kill is pending, not for the whole list.

Written for commit 609c2a4. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Move terminal sessions to the background for later use.
    • Background terminals menu in the workspace tab bar showing session titles and relative creation times.
    • Re-open (adopt) or kill background terminals from the menu.
    • Tab bar now supports custom trailing content, allowing the background terminals button to be shown.

Review Change Stack

…store move-to-background button

- Add renderTabBarTrailing slot to @superset/panes Workspace/TabBar
- New BackgroundTerminalsButton in v2-workspace tab bar: lists daemon
  terminal sessions with no pane attached; click to re-open as a tab,
  trash icon to kill
- Restore the 'Move terminal to background' archive button in the v2
  terminal pane header (removed in #3888)
@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 12, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7afa81e1-672c-4b22-9ad7-ab6315a65cc4

📥 Commits

Reviewing files that changed from the base of the PR and between 27eef42 and 609c2a4.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx

📝 Walkthrough

Walkthrough

Adds background terminal session management: terminal header action to move to background, a tab-bar trailing BackgroundTerminalsButton listing unattached sessions, and workspace/TabBar props to render trailing content.

Changes

Background Terminals Feature

Layer / File(s) Summary
Tab bar render prop infrastructure
packages/panes/src/react/types.ts, packages/panes/src/react/components/Workspace/Workspace.tsx, packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx
WorkspaceProps<TData> gains optional renderTabBarTrailing?: () => ReactNode; Workspace forwards it to TabBar, which renders trailing content in empty and non-empty tab layouts.
Terminal header move-to-background button
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx
Adds tooltip and Archive icon imports, a handleMoveToBackground handler that marks the terminal for background and closes the pane, and a tooltip-wrapped "Move terminal to background" button.
Background terminals list and management
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/index.ts
BackgroundTerminalsButton queries workspaceTrpc.terminal.listSessions, computes unattached sessions vs. current panes, shows a dropdown with session titles and relative times, and supports "adopt" (adds a terminal tab) and "kill" (mutation) with query invalidation.
Page component wiring
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
Imports BackgroundTerminalsButton and mounts it via the Workspace component's renderTabBarTrailing prop with workspaceId and store.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nudged a shell into the shade,
Closed its pane where echoes fade,
A little button counts them now,
Adopt or end with gentle bow,
Rabbity cheers for background play 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: adding background terminal sessions UI to the v2 tab bar and restoring the move-to-background button.
Description check ✅ Passed The description comprehensively covers all required template sections: clear explanation of changes, related issues context, type of change (New feature), detailed testing instructions, and additional technical notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ 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 background-terminal-tabs

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 12, 2026

Greptile Summary

This PR adds background terminal session support to the v2 workspace tab bar and restores the "Move to background" archive button in the terminal pane header that was removed in #3888.

  • BackgroundTerminalsButton: a new tab-bar trailing control that polls listSessions (every 5 s idle, 2 s while open), filters out currently-attached terminal IDs from the Zustand store, and renders a dropdown to re-open or kill each orphaned session. The @superset/panes Workspace / TabBar types receive a new optional renderTabBarTrailing render-prop to accommodate it.
  • TerminalHeaderExtras: the archive button calls markTerminalForBackground(terminalId) then context.actions.close(), keeping the daemon alive while closing the pane.
  • TabBar: renderTabBarTrailing is correctly wired in both the empty-tabs path and the normal path.

Confidence Score: 4/5

Safe to merge; changes are additive and self-contained with no data-loss paths.

The new BackgroundTerminalsButton is well-structured and the TabBar prop threading is correct in both render paths. The shared killSession mutation instance causing all trash buttons to freeze during an in-flight kill is a minor UX rough edge rather than a functional defect.

BackgroundTerminalsButton.tsx deserves a quick look for the shared mutation pending state.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx New component that polls listSessions and surfaces unattached terminal sessions; minor UX issue with shared killSession.isPending disabling all trash buttons simultaneously.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx Restores archive button in terminal header; calls markTerminalForBackground then closes pane — straightforward and correct.
packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx Adds renderTabBarTrailing slot in both the empty-tabs and normal rendering paths; layout change is correct in both branches.
packages/panes/src/react/types.ts Adds optional renderTabBarTrailing to WorkspaceProps with a JSDoc comment; clean, additive change.
packages/panes/src/react/components/Workspace/Workspace.tsx Threads renderTabBarTrailing prop through to TabBar; no logic changes.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx Wires BackgroundTerminalsButton into the workspace via renderTabBarTrailing; integration is straightforward.

Sequence Diagram

sequenceDiagram
    participant User
    participant TerminalHeader as TerminalHeaderExtras
    participant Intents as markTerminalForBackground
    participant PaneStore as WorkspaceStore
    participant Server as terminal.listSessions
    participant BTBtn as BackgroundTerminalsButton

    User->>TerminalHeader: click Archive button
    TerminalHeader->>Intents: markTerminalForBackground(terminalId)
    TerminalHeader->>PaneStore: context.actions.close()
    PaneStore-->>BTBtn: tabs updated (terminalId removed from panes)
    Note over BTBtn: polls every 5s (2s while open)
    BTBtn->>Server: "listSessions({ workspaceId })"
    Server-->>BTBtn: sessions[]
    BTBtn->>BTBtn: filter out attachedTerminalIds → backgroundSessions
    BTBtn-->>User: renders N background terminal session(s) button

    User->>BTBtn: click session in dropdown
    BTBtn->>PaneStore: "addTab({ kind: terminal, data: { terminalId } })"
    BTBtn->>Server: invalidate listSessions

    User->>BTBtn: click trash icon on session
    BTBtn->>Server: "killSession({ terminalId, workspaceId })"
    Server-->>BTBtn: success / error
    BTBtn->>Server: invalidate listSessions
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx:131-135
**Single mutation disables all kill buttons**

`killSession` is one shared tRPC mutation instance. While any kill is in-flight, `killSession.isPending` is `true`, so the `disabled` prop on every trash `<button>` across every row fires simultaneously — the user can't initiate a second kill until the first one completes. If the kill RPC is slow, this creates a confusing frozen state for all other sessions.

Reviews (1): Last reviewed commit: "feat(desktop): surface background termin..." | Re-trigger Greptile

Comment on lines +131 to +135
<button
type="button"
aria-label="Close terminal session"
title="Close terminal session"
disabled={killSession.isPending}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Single mutation disables all kill buttons

killSession is one shared tRPC mutation instance. While any kill is in-flight, killSession.isPending is true, so the disabled prop on every trash <button> across every row fires simultaneously — the user can't initiate a second kill until the first one completes. If the kill RPC is slow, this creates a confusing frozen state for all other sessions.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
Line: 131-135

Comment:
**Single mutation disables all kill buttons**

`killSession` is one shared tRPC mutation instance. While any kill is in-flight, `killSession.isPending` is `true`, so the `disabled` prop on every trash `<button>` across every row fires simultaneously — the user can't initiate a second kill until the first one completes. If the kill RPC is slow, this creates a confusing frozen state for all other sessions.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx (1)

39-42: 💤 Low value

Consider the refetch intervals for production performance.

The query refetches every 2 seconds when the dropdown is open and every 5 seconds when closed. While this provides a responsive UI, it may generate significant network/CPU load in workspaces with many background sessions or when multiple workspace tabs are open.

Consider increasing the intervals (e.g., 5s open, 15s closed) or using a WebSocket/subscription pattern for real-time updates if this becomes a performance concern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
around lines 39 - 42, The current sessionsQuery created via
workspaceTrpc.terminal.listSessions.useQuery({ workspaceId }, { refetchInterval:
isOpen ? 2_000 : 5_000, refetchOnWindowFocus: true }) polls very frequently;
change the refetchInterval values to reduce load (for example use isOpen ? 5_000
: 15_000) or swap to a real-time update approach (e.g., replace polling with a
WebSocket/subscription handler) and keep the same call site (sessionsQuery /
workspaceTrpc.terminal.listSessions.useQuery / workspaceId / isOpen) so the
component continues to receive session updates with lower CPU/network costs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx:
- Around line 39-42: The current sessionsQuery created via
workspaceTrpc.terminal.listSessions.useQuery({ workspaceId }, { refetchInterval:
isOpen ? 2_000 : 5_000, refetchOnWindowFocus: true }) polls very frequently;
change the refetchInterval values to reduce load (for example use isOpen ? 5_000
: 15_000) or swap to a real-time update approach (e.g., replace polling with a
WebSocket/subscription handler) and keep the same call site (sessionsQuery /
workspaceTrpc.terminal.listSessions.useQuery / workspaceId / isOpen) so the
component continues to receive session updates with lower CPU/network costs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86c6a349-520a-456e-b76d-6b2af6ae41aa

📥 Commits

Reviewing files that changed from the base of the PR and between bb31359 and 27eef42.

📒 Files selected for processing (7)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
  • packages/panes/src/react/components/Workspace/Workspace.tsx
  • packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx
  • packages/panes/src/react/types.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx:135">
P2: Use a per-session pending condition for the trash button instead of a shared mutation pending flag; the current check disables every row whenever any kill request is in flight.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Kitenite Kitenite merged commit 0e81f77 into main May 12, 2026
17 checks 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.

1 participant