feat(desktop): background terminal sessions in v2 tab bar + restore move-to-background button#4459
Conversation
…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 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. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds background terminal session management: terminal header action to move to background, a tab-bar trailing ChangesBackground Terminals Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis 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.
Confidence Score: 4/5Safe 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.
|
| 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
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
| <button | ||
| type="button" | ||
| aria-label="Close terminal session" | ||
| title="Close terminal session" | ||
| disabled={killSession.isPending} |
There was a problem hiding this 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.
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.There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsx (1)
39-42: 💤 Low valueConsider 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
📒 Files selected for processing (7)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/BackgroundTerminalsButton.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/BackgroundTerminalsButton/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalHeaderExtras/TerminalHeaderExtras.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsxpackages/panes/src/react/components/Workspace/Workspace.tsxpackages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsxpackages/panes/src/react/types.ts
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
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.
What
BackgroundTerminalsButtonin 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 existingterminalId); the hover trash icon kills the session.markTerminalForBackground(terminalId)thencontext.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: newrenderTabBarTrailingrender-prop on<Workspace>, threaded intoTabBarand rendered at the trailing edge of the tab-bar row.Notes
listSessions→session.title), falling back to "Terminal" when the shell never emitted one — same as a paned terminal would show.Test plan
bun run typecheckandbun run lintpass.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
renderTabBarTrailingslot to@superset/panesto host the new button.New Features
terminalId), and a trash icon to kill sessions.markTerminalForBackground(terminalId)and closes the pane, keeping the daemon running.@superset/panes: addedrenderTabBarTrailingrender-prop on<Workspace>(threaded intoTabBar) to render trailing tab bar content.Bug Fixes
Written for commit 609c2a4. Summary will update on new commits.
Summary by CodeRabbit