5.4 KiB
2026-03-24 SFTP Tabs Design
Background
The current connection list provides both 终端 and 文件 actions. 终端 has persistent sidebar tabs, but 文件 (SFTP) opens a route directly and has no sidebar tab lifecycle. Users lose the quick return path after navigating away, which feels inconsistent.
Goal
- Add sidebar tabs for SFTP sessions near the existing terminal tabs.
- Ensure SFTP tabs are unique per connection (no duplicates).
- Keep tab state only for the current in-memory session.
Non-Goals
- No persistence across full page refresh (no localStorage/sessionStorage).
- No refactor that merges terminal and SFTP tab models into one generic tab system.
- No route restructuring; keep
/sftp/:idas the SFTP route entry.
Current State
- Terminal tabs are managed by
frontend/src/stores/terminalTabs.tsand rendered infrontend/src/layouts/MainLayout.vue. ConnectionsViewopens terminal viaterminalTabs.openOrFocus(conn)and then routes to/terminal.ConnectionsViewopens SFTP by routing directly to/sftp/:id, with no tab store.SftpViewcurrently has no tab registration step.
Selected Approach
Introduce a dedicated SFTP tab store and render a new sidebar tab section in MainLayout, following the same interaction model as terminal tabs while keeping route behavior centered on /sftp/:id.
1. New store: sftpTabs
Create frontend/src/stores/sftpTabs.ts with a model parallel to terminal tabs:
tabs: SftpTab[]activeTabId: string | nullactiveTabcomputedopenOrFocus(connection)activate(tabId)close(tabId)
Each tab includes:
id: stringconnectionId: numbertitle: stringactive: boolean
Dedup rule:
openOrFocus(connection)must reuse an existing tab whenconnectionIdmatches.
2. Connection list behavior
Update frontend/src/views/ConnectionsView.vue:
- In
openSftp(conn), callsftpTabs.openOrFocus(conn)beforerouter.push(/sftp/${conn.id}).
Result:
- Clicking
文件on the same connection repeatedly does not create duplicate tabs.
3. Sidebar rendering and controls
Update frontend/src/layouts/MainLayout.vue:
- Import and use
useSftpTabsStore. - Add computed
sftpTabs. - Add a new sidebar section (near terminal tabs) titled
文件. - Render each tab as a clickable item with close button.
Interactions:
- Click tab: activate and navigate to
/sftp/:connectionId. - Close tab:
- Always remove the tab from store.
- Only trigger route navigation when the current route is the closed tab's route (
/sftp/:connectionId):- If another SFTP tab remains, navigate to
/sftp/:newActiveConnectionId. - If no SFTP tabs remain, navigate to
/connections.
- If another SFTP tab remains, navigate to
- If closing a non-active tab, or closing from a non-SFTP route, remove only (no route change).
Highlighting:
- Keep active style tied to both tab active state and current SFTP route context.
4. Route-entry consistency
Update frontend/src/views/SftpView.vue:
- Import and use
useSftpTabsStore. - Watch
route.params.idwith{ immediate: true }so logic runs on first load and on/sftp/:idparam changes (tab-to-tab switching). - On each
idchange:- Parse
idto number; if invalid, navigate to/connections. - Ensure connections are loaded (fetch when needed).
- Resolve the matching connection; if not found, show error feedback and navigate to
/connections. - Call
sftpTabs.openOrFocus(connection)and then refresh SFTP view state for that connection.
- Parse
- Consolidate route-driven initialization into this watcher (avoid a separate
onMountedpath-init flow for the same concern) so first load and param switching use one code path and do not trigger duplicate requests.
This keeps behavior consistent for direct navigation and sidebar tab switching, without duplicate tabs.
Behavior Kept Unchanged
- Existing terminal tab logic and terminal workspace lifecycle.
- Existing SFTP route path (
/sftp/:id) and core file-management interactions remain unchanged, except for tab registration/sync and invalid-or-missing connection route handling described above. - Authentication and router guards.
- UI visual language (slate/cyan styling).
Acceptance Criteria
文件opens/activates an SFTP tab in sidebar.- Repeatedly opening
文件for the same connection does not create duplicate tabs. - SFTP tabs remain available when navigating back to connections or other pages within the same session.
- Closing active/non-active SFTP tabs follows the navigation rules above.
- Terminal tabs continue working exactly as before.
- Switching between existing SFTP tabs (for example
/sftp/1and/sftp/2) updates connection header and file list correctly, without stale data from the previous connection. - Direct navigation to an invalid or nonexistent
/sftp/:iddoes not create a tab and returns the user to/connectionswith visible feedback.
Verification
- Run
npm run buildinfrontend/. - Manual verification:
- Open SFTP for one connection multiple times and confirm single tab.
- Open SFTP for multiple connections and switch between tabs.
- Navigate away and return via sidebar SFTP tabs.
- Close active and inactive SFTP tabs and verify resulting route behavior.
- Re-check terminal tab open/switch/close behavior for regressions.
- Open an invalid/nonexistent
/sftp/:idand verify no tab is created, visible error feedback appears, and navigation returns to/connections.