Skip to content

feat: surface closed sessions in the "+" tab menu#88

Open
adamatan wants to merge 1 commit into
simion:mainfrom
adamatan:feature/resume-closed-tabs
Open

feat: surface closed sessions in the "+" tab menu#88
adamatan wants to merge 1 commit into
simion:mainfrom
adamatan:feature/resume-closed-tabs

Conversation

@adamatan

Copy link
Copy Markdown
Contributor

Fixes #77

This PR solves a big usability issue I have: too many tasks and tabs open at the same time, just because I'm not sure I can close them. Adding the ability to reliably restore any closed tab (or archived task) helps me keep a focused environment, without the nagging fear that closing something means losing it for good.

UX

  • Tab strip "+" (inside an open task): a new "Resume" section lists the last few secondary agent tabs you've closed in this task, each with its CLI icon and a relative timestamp ("2m ago"). Clicking one reopens it with the exact same session, so the agent picks the conversation back up instead of starting fresh. A "More…" row links to the full History view.
  • Sidebar project "+" (the task launcher): the same "Resume" pattern, but for that project's recently archived tasks, restoring the whole task and switching to it, one click closer than going through History.
  • Close confirmation, relaxed: closing a non-shell terminal/agent tab still asks for confirmation by default, but the dialog now has a "Don't ask again" checkbox. Once turned off (also available as a toggle in Settings → General), closes happen immediately and a toast tells you where the tab went, with an inline "Resume" button.

Technical change

  • src/store/app.ts: new closedTabs: Record<taskId, ClosedTabEntry[]> slice, in-memory only (cleared on app restart, no new persistence/backend work). closeTab snapshots a closing secondary tab (title, cli, session id) into this list right before syncDurableTabs would otherwise forget it for good; the main tab and shell tabs are excluded since they either auto-resume already or have no session. A new resumeClosedTab action removes the entry and reopens it by constructing a fresh TerminalTab with the original sessionId pre-set, letting the existing resume-by-id logic in TerminalPane do the rest (no changes needed there).
  • src/components/task/TabBar.tsx: the "Resume" dropdown section, reusing the existing DropdownItem/DropdownLabel primitives and the same icon-resolution helpers as the rest of the menu.
  • src/components/sidebar/ProjectActionsMenuItems.tsx: same pattern, sourced from s.tasks filtered to archived && project_id === projectId, sorted most-recent-first (mirrors HistoryView's sort), restoring via the existing task_restore IPC + setActiveTask.
  • src/lib/closeTab.ts / src/store/prefs.ts: new confirmBeforeCloseAgentTab preference (default on), gating whether the confirmation dialog blocks the close; a toast with a bound "Resume" action fires on the fast path.
  • CHANGELOG.md: added an [Unreleased] entry (not a version bump, that stays the maintainer's call at release time).
  • Fixed a real bug caught during manual testing: a useApp(s => s.closedTabs[task.id] ?? []) selector was minting a new array reference on every render, which (Zustand v5 uses useSyncExternalStore internally) caused React's "Maximum update depth exceeded" crash. Fixed with a stable shared empty-array constant.
  • Fixed a second bug from code review: the "Don't ask again" checkbox was being persisted even when the close was cancelled (Escape/Cancel/click-outside), silently disabling future confirmations against the user's actual intent. Now only persists on actual confirmation.

Test coverage

  • npx tsc -b and npm test (242 tests, all passing) after every change, including after rebasing onto the latest workspace→task rename migration.
  • Two rounds of automated code review (code-reviewer subagent), each finding and fixing one real bug (see above).
  • Live end-to-end verification via the dev automation bridge against a real running app: spawned a secondary agent tab, closed it, confirmed it appeared in both the toast and the "+" > Resume dropdown, clicked Resume through real DOM events, and confirmed via the debug log that the PTY respawned with --resume <the original session id> (not a fresh session), with a screenshot showing the terminal itself printing that resume argument. Repeated the same for the sidebar's archived-task Resume (restored an archived task, confirmed it un-archived and became the active task, Claude Code launched with "Welcome back").
  • No automated test coverage added for the new UI paths themselves (dropdown rendering, toast wiring). The existing resume.integration.test.ts covers the underlying resume-by-id mechanism this reuses, but the new Resume-menu UI and the closedTabs capture/eviction logic are only covered by the manual e2e pass above. Happy to add unit coverage for closedTabs capture/cap/resume in app.test.ts if useful before merge.

Adds a "Resume" section to both "+" launchers so a closed tab or
archived task is one click away instead of buried in History:

- Tab strip "+": lists recently closed secondary agent tabs
  (session-only), reopening them with their original session id so
  the agent resumes the prior conversation instead of starting fresh.
- Sidebar project "+": lists the project's recently archived tasks,
  restoring via the existing task_restore path.

Also relaxes the close-confirmation dialog: a new "Confirm before
closing an agent tab" preference (default on) can be turned off via
the dialog's "Don't ask again" checkbox, closing tabs immediately
with a toast pointing back at Resume instead of a blocking modal
every time.

Fixes simion#77
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.

Surface closed sessions in the "+" tab menu

1 participant