6.8 KiB
SFTP Sidebar 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: Add non-duplicated, session-scoped SFTP tabs in the sidebar so users can return to file sessions without losing tab entries.
Architecture: Introduce a dedicated Pinia store (sftpTabs) parallel to terminalTabs, then wire it into ConnectionsView, MainLayout, and SftpView. Keep /sftp/:id as the single SFTP route and use route-driven synchronization so tab switching updates the view correctly without stale state.
Tech Stack: Vue 3, TypeScript, Pinia, Vue Router, Vite, vue-tsc
File Structure
- Create:
frontend/src/stores/sftpTabs.ts- Owns SFTP tab state and actions (
openOrFocus,activate,close).
- Owns SFTP tab state and actions (
- Modify:
frontend/src/views/ConnectionsView.vue- Registers/focuses SFTP tab before routing to
/sftp/:id.
- Registers/focuses SFTP tab before routing to
- Modify:
frontend/src/layouts/MainLayout.vue- Renders SFTP sidebar tabs, handles click/close navigation behavior.
- Modify:
frontend/src/views/SftpView.vue- Keeps route param and SFTP tab state in sync via watcher.
- Verify:
frontend/src/router/index.ts- Route shape remains unchanged (
/sftp/:id).
- Route shape remains unchanged (
- Verify:
docs/superpowers/specs/2026-03-24-sftp-tabs-design.md- Source of truth for requirements and acceptance criteria.
Task 1: Add Dedicated SFTP Tabs Store
Files:
-
Create:
frontend/src/stores/sftpTabs.ts -
Reference:
frontend/src/stores/terminalTabs.ts -
Step 1: Check current store pattern and workspace status
Run git status --short and read frontend/src/stores/terminalTabs.ts to mirror established naming/style patterns.
- Step 2: Add
SftpTabmodel and store state
Create sftpTabs store with:
export interface SftpTab {
id: string
connectionId: number
title: string
active: boolean
}
State and computed:
const tabs = ref<SftpTab[]>([])
const activeTabId = ref<string | null>(null)
const activeTab = computed(() => tabs.value.find(t => t.id === activeTabId.value) || null)
- Step 3: Implement open/activate/close logic with dedup
Implement actions analogous to terminal tabs:
-
openOrFocus(connection)reuses existing tab byconnectionId -
activate(tabId)flips active flags and updatesactiveTabId -
close(tabId)removes tab and activates neighbor when needed -
Step 4: Build-check after new store
Run in frontend/: npm run build
Expected: build succeeds and no type errors in new store.
Task 2: Register SFTP Tabs from Connections Entry
Files:
-
Modify:
frontend/src/views/ConnectionsView.vue -
Use:
frontend/src/stores/sftpTabs.ts -
Step 1: Add store import and instance
Import useSftpTabsStore and initialize const sftpTabsStore = useSftpTabsStore().
- Step 2: Update
openSftp(conn)behavior
Before routing, call:
sftpTabsStore.openOrFocus(conn)
router.push(`/sftp/${conn.id}`)
- Step 3: Build-check for integration safety
Run in frontend/: npm run build
Expected: build succeeds and ConnectionsView typing remains valid.
Task 3: Render and Control SFTP Tabs in Sidebar
Files:
-
Modify:
frontend/src/layouts/MainLayout.vue -
Use:
frontend/src/stores/sftpTabs.ts -
Step 1: Add SFTP store wiring and computed values
Add:
-
const sftpTabsStore = useSftpTabsStore() -
const sftpTabs = computed(() => sftpTabsStore.tabs) -
route helper for SFTP context (for active styling and close-navigation logic)
-
Step 2: Add SFTP tab click and close handlers
Implement:
-
click handler: activate tab +
router.push(/sftp/${tab.connectionId})+ close sidebar -
close handler:
- always close in store
- only navigate when current route is the closed tab's route
- if tabs remain, navigate to active tab route
- if no tabs remain, navigate
/connections
-
Step 3: Render
文件sidebar section near terminal section
Add a section matching current visual style conventions:
-
title row with
FolderOpenicon and text文件 -
list items for
sftpTabs -
close button per item
-
active class aligned with current route context
-
Step 4: Build-check and inspect no terminal regressions
Run in frontend/: npm run build
Expected: build succeeds; no type/template errors in MainLayout.
Task 4: Route-Driven Sync in SFTP View
Files:
-
Modify:
frontend/src/views/SftpView.vue -
Use:
frontend/src/stores/sftpTabs.ts -
Step 1: Add SFTP tabs store and route-param watcher
Add watcher on route.params.id with { immediate: true } so first load and tab switching share one code path.
- Step 2: Consolidate initialization into watcher path
Use watcher flow:
- parse and validate id; if invalid, show user-visible error feedback, do not create/open any SFTP tab, and navigate to
/connections - ensure connections loaded (fetch when needed)
- resolve connection; if missing, show user-visible error and route back to
/connections - call
sftpTabsStore.openOrFocus(connection) - refresh connection-bound SFTP view state for current route id
Avoid duplicate request/init logic split across both watcher and old mount flow.
- Step 3: Keep existing file-management behavior unchanged
Do not alter existing upload/download/delete/core SFTP operations; only route/tab synchronization behavior should change.
- Step 4: Build-check after route-sync changes
Run in frontend/: npm run build
Expected: build succeeds and SftpView compiles cleanly.
Task 5: End-to-End Verification
Files:
-
Verify runtime behavior in app UI
-
Verify
frontend/src/router/index.tsunchanged for route shape -
Step 1: Run final build verification
Run in frontend/: npm run build
Expected: vue-tsc -b and Vite build both pass.
- Step 2: Manual behavior checks
Verify all acceptance criteria:
- Same-connection
文件clicks do not create duplicate tabs. - Multiple SFTP tabs can be opened and switched.
- Navigating away (for example back to connections) keeps SFTP tabs in sidebar during current session.
- Closing active/non-active tabs follows designed route behavior.
- Switching
/sftp/:idbetween tabs updates header/file list without stale previous-connection state. - Invalid/nonexistent
/sftp/:idcreates no tab, shows visible feedback, and routes back to/connections. - Terminal tabs continue to open/switch/close normally.
- Step 3: Commit if user requests
If the user asks to commit, keep commit focused:
git add frontend/src/stores/sftpTabs.ts frontend/src/views/ConnectionsView.vue frontend/src/layouts/MainLayout.vue frontend/src/views/SftpView.vue
git commit -m "feat: add sidebar tabs for sftp sessions"