Files
ssh-manager/docs/superpowers/plans/2026-03-26-terminal-multi-tabs.md
2026-03-26 18:04:39 +08:00

3.6 KiB

Terminal Multi Tabs Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Allow the connection list to open multiple terminal tabs for the same connection, with each click creating a fresh terminal session.

Architecture: Keep the existing /terminal workspace and Pinia-driven tab model, but change terminal tab creation from connection-deduplicated to always-new. Add lightweight title numbering so repeated tabs for the same connection remain distinguishable without changing routing or terminal widget lifecycle.

Tech Stack: Vue 3, TypeScript, Pinia, Vue Router, xterm.js, Vite, vue-tsc


File Structure

  • Modify: frontend/src/stores/terminalTabs.ts
    • Change tab creation semantics and add repeated-title numbering.
  • Modify: frontend/src/views/ConnectionsView.vue
    • Use the new always-open terminal action from the connection list.
  • Verify: frontend/src/views/TerminalWorkspaceView.vue
    • Confirm current per-tab widget mounting already supports independent sessions.
  • Verify: frontend/src/layouts/MainLayout.vue
    • Confirm terminal sidebar tab rendering needs no structure change beyond new titles.
  • Reference: docs/superpowers/specs/2026-03-26-terminal-multi-tabs-design.md
    • Source of truth for behavior and acceptance criteria.

Task 1: Change Terminal Tabs Store to Always Create New Tabs

Files:

  • Modify: frontend/src/stores/terminalTabs.ts

  • Reference: frontend/src/stores/sftpTabs.ts

  • Step 1: Inspect current tab store behavior

Read frontend/src/stores/terminalTabs.ts and confirm the existing dedup-by-connection behavior.

  • Step 2: Add repeated-title generation

Add a helper that counts currently open tabs for the same connectionId and returns:

function getTabTitle(connection: Connection) {
  const sameConnectionCount = tabs.value.filter(t => t.connectionId === connection.id).length

  if (sameConnectionCount === 0) {
    return connection.name
  }

  return `${connection.name} (${sameConnectionCount + 1})`
}
  • Step 3: Replace dedup open logic with always-new creation

Change the open action so it always creates a new TerminalTab and activates it immediately.

  • Step 4: Keep activate/close behavior unchanged

Do not change tab activation and close-neighbor behavior except what is necessary to support the new open logic.

Task 2: Wire the Connection List to the New Behavior

Files:

  • Modify: frontend/src/views/ConnectionsView.vue

  • Use: frontend/src/stores/terminalTabs.ts

  • Step 1: Rename the method usage to match semantics

Update openTerminal(conn) to call the new always-open store action.

  • Step 2: Preserve routing behavior

Keep router.push('/terminal') unchanged after opening the tab.

Task 3: Verify Integration and Build

Files:

  • Verify: frontend/src/layouts/MainLayout.vue

  • Verify: frontend/src/views/TerminalWorkspaceView.vue

  • Step 1: Confirm sidebar rendering already keys by tab.id

No structural code changes should be required if repeated tabs render correctly with distinct titles.

  • Step 2: Run final frontend build

Run in frontend/:

npm run build

Expected: vue-tsc -b and vite build both pass.

  • Step 3: Manual runtime verification

Check:

  1. Same connection opens multiple terminal tabs.
  2. Repeated tabs are titled distinctly.
  3. Each tab remains an independent terminal session.
  4. Close/switch behavior still works.