4.3 KiB
2026-03-26 Terminal Multi Tabs Design
Background
The connection list already opens terminal sessions into the shared terminal workspace at /terminal, and the sidebar already renders terminal tabs. However, the current store deduplicates tabs by connectionId, so clicking 终端 for the same connection only focuses the existing tab instead of opening a new shell session.
Goal
- Allow users to open multiple terminal tabs from the connection list for the same connection.
- Keep each terminal tab as an independent shell session.
- Preserve the current terminal workspace route and sidebar interaction model.
Non-Goals
- No tab persistence across refresh.
- No route change from
/terminalto per-tab URLs. - No generic tab-system refactor shared with SFTP.
- No terminal session restore or reconnect workflow.
Current State
frontend/src/stores/terminalTabs.tsmanages terminal tabs.openOrFocus(connection)reuses an existing tab whenconnectionIdmatches.frontend/src/views/ConnectionsView.vueuses that method before routing to/terminal.frontend/src/views/TerminalWorkspaceView.vuealready mounts oneTerminalWidgetper tab keyed bytab.id, so multiple tabs can coexist as long as the store allows them.
Selected Approach
Keep the existing terminal workspace architecture and change only the terminal tab creation semantics: clicking 终端 always creates a new tab, even when the same connection already has open tabs.
1. Terminal tab creation becomes always-new
Update frontend/src/stores/terminalTabs.ts so the terminal action always creates a new tab entry:
- remove the
connectionIddedup behavior from the open action - generate a fresh
tab.idevery time - activate the newly created tab immediately
This preserves the current in-memory tab lifecycle while enabling multiple concurrent sessions to the same host.
2. Distinguishable tab titles for repeated connections
When the same connection is opened multiple times, sidebar labels must remain distinguishable.
Recommended title strategy:
- first tab:
Connection Name - second tab:
Connection Name (2) - third tab:
Connection Name (3)
The sequence is computed from currently open tabs for the same connectionId. No persistence is needed.
3. Connections entry behavior
Update frontend/src/views/ConnectionsView.vue so clicking 终端 calls the always-new tab action and routes to /terminal.
Result:
- each click from the connection list opens a fresh shell session
- users can intentionally keep several terminals open for the same host
4. Terminal workspace behavior stays unchanged
frontend/src/views/TerminalWorkspaceView.vue and frontend/src/components/TerminalWidget.vue do not need architectural changes:
- the workspace already loops through tabs by
tab.id - each active tab renders its own
TerminalWidget - each widget maintains an independent xterm instance and WebSocket connection
This matches the desired UX: multiple tabs for the same connection are separate terminal sessions rather than alternate views of one shared session.
Behavior Kept Unchanged
- Terminal route remains
/terminal. - Sidebar terminal tab section remains the primary tab switcher.
- Closing and activating adjacent terminal tabs continues to follow the current logic.
- SFTP tab behavior remains unchanged.
- Existing slate/cyan visual language remains unchanged.
Acceptance Criteria
- Clicking
终端for the same connection multiple times creates multiple terminal tabs. - Each new terminal tab becomes the active tab.
- Repeated tabs for the same connection are visually distinguishable in the sidebar.
- Switching between tabs preserves each tab's own terminal session output.
- Closing a terminal tab keeps current close/activate behavior intact.
- Opening tabs for different connections continues to work.
Verification
- Run
npm run buildinfrontend/. - Manual verification:
- Click
终端on the same connection three times and confirm three tabs appear. - Confirm the sidebar shows distinguishable titles for repeated tabs.
- Type different commands in different tabs for the same connection and confirm each tab keeps its own output.
- Close active and inactive terminal tabs and confirm focus changes remain correct.
- Re-check that opening terminal tabs for different connections still works.
- Click