8.1 KiB
Remote -> Many Multi-File (Files Only) 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 Transfers -> Remote -> Many to select multiple source files (files only) and transfer them to many targets.
Architecture: Frontend-only change. Expand sourcePaths x targetConnectionIds into a queue of items; each item uses existing backend /sftp/transfer-remote/tasks and SSE progress tracking.
Tech Stack: Vue 3 + TypeScript (Vite), Pinia, Spring Boot backend unchanged.
File Map
Modify:
frontend/src/views/TransfersView.vue(Remote -> Many UI + state)frontend/src/components/SftpFilePickerModal.vue(add multi-select mode)frontend/src/stores/transfers.ts(supportsourcePaths[],targetMode, improve SSE unsubscription)
No backend changes required.
Task 1: Add multi-select mode to SftpFilePickerModal
Files:
-
Modify:
frontend/src/components/SftpFilePickerModal.vue -
Step 1: Update component API (props + emits)
- Add optional prop:
multiple?: boolean(defaultfalse). - Keep existing event:
selectfor single selection. - Add new event:
select-manyfor multi selection (payload:string[]).
- Add optional prop:
-
Step 2: Add selection state and deterministic ordering
- Track selected paths in selection-time order (array) + a set for O(1) membership.
- File click behavior:
- Directory: navigate into directory.
- File:
- if
multiple: toggle selection. - else: emit
select(path)and close (keep existing).
- if
- Persist selection across directory navigation within the modal session.
- If a file is toggled off and later toggled on again, re-add it at the end of the array (selection-time order).
-
Step 2.1: Add a visible selection indicator
- When
multiple, render a checkbox in each file row (checked = selected). - Directory rows never render a checkbox.
- When
-
Step 3: Add modal footer controls
- Add footer actions when
multiple:Confirm (N)(disabled whenN=0) emitsselect-many(paths)then closes.Cancelcloses without emitting.
- Add a "Selected (N)" compact summary area with remove (
x) for each selected file.
- Add footer actions when
-
Step 4: Manual verification
- Run:
npm --prefix frontend run build - Expected: build succeeds.
- Manual:
- Open picker, select multiple files across different directories, ensure selections persist.
- Remove items in the "Selected" summary.
- Confirm emits all paths in selection-time order.
- Verify search still filters entries correctly.
- Verify hidden-files toggle still works as before.
- Run:
-
Step 5: Commit (optional)
- If doing commits:
- Run:
git add frontend/src/components/SftpFilePickerModal.vue - Run:
git commit -m "feat: add multi-select mode to SFTP file picker"
- Run:
- If doing commits:
Task 2: Update TransfersView Remote -> Many UI for multi-file selection
Files:
-
Modify:
frontend/src/views/TransfersView.vue -
Step 1: Update remote source model
- Replace
remoteSourcePath: stringwithremoteSourcePaths: string[]. - UI:
- Show count + list of selected paths (basename + full path tooltip).
- Provide remove per item + clear all.
- Replace
-
Step 1.1: Add client-side validation for typed paths
- Before starting Remote -> Many:
- validate each
remoteSourcePaths[i]is non-empty - validate it does not end with
/(heuristic directory indicator)
- validate each
- If invalid: show inline error and block Start.
- Before starting Remote -> Many:
-
Step 2: Wire picker in multi mode
- Call
<SftpFilePickerModal :multiple="true" ... />. - Listen for
select-manyand merge paths intoremoteSourcePaths:- append + de-duplicate by full path while preserving first-seen order.
- Call
-
Step 3: Add target path mode toggle
- Add UI control:
Directory(default) vsExact Path. - Behavior:
Directorytreats input as directory; normalize to end with/; append source filename.Exact Pathuses raw input as full file path (only allowed when exactly one source file is selected).
- Disable
Exact PathwhenremoteSourcePaths.length > 1(force directory mode).
- Add UI control:
-
Step 3.1: Update Start button enablement
- Require
remoteSourceConnectionId != null. - Require
remoteSourcePaths.length > 0. - Require
remoteSelectedTargets.length > 0. - Also require validation in Step 1.1 passes.
- Require
-
Step 4: Add basename collision warning
- When
Directorymode andremoteSourcePathscontains duplicate basenames:- show a warning dialog/banner before starting (acknowledge to proceed).
- message mentions overwrite will happen on each selected target connection.
- When
-
Step 5: Source connection change behavior
- When
remoteSourceConnectionIdchanges:- clear
remoteSourcePaths - remove source id from
remoteSelectedTargetsif present
- clear
- When
-
Step 6: Manual verification
- Run:
npm --prefix frontend run build - Manual:
- Pick 2 files, choose 2 targets -> queue shows 4 items.
- Toggle Directory/Exact Path:
- multi-file forces Directory
- single-file allows Exact Path
- Change source connection -> selected files cleared.
- Run:
-
Step 7: Commit (optional)
- If doing commits:
- Run:
git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue - Run:
git commit -m "feat: allow selecting multiple remote source files"
- Run:
- If doing commits:
Task 3: Update transfers store to support multi-file Remote -> Many + safe SSE unsubscription
Files:
-
Modify:
frontend/src/stores/transfers.ts -
Step 1: Update store API and types
- Change
startRemoteToManyparams:- from:
sourcePath: string - to:
sourcePaths: string[]
- from:
- Add param:
targetMode: 'dir' | 'path'.
- Change
-
Step 2: Implement target path resolution in store
- For each
sourcePath:- compute
filenameas basename. - if
targetMode === 'dir': normalizetargetDirOrPathto end with/and append filename. - if
targetMode === 'path': requiresourcePaths.length === 1and use rawtargetDirOrPath.
- compute
- For each
-
Step 3: Expand items as
sourcePaths x targets- Create a
TransferItemper pair. - Label includes both source path and target connection.
- Concurrency limiter applies across the full list.
- Create a
-
Step 4: Fix SSE subscription cleanup
- In
waitForRemoteTransferand upload-wait logic:- ensure the returned unsubscribe is called on terminal states (success/error/cancel).
- still store unsubscribe in the run controller so
cancelRuncan close any active subscriptions.
- In
-
Step 5: Manual verification
- Run:
npm --prefix frontend run build - Manual:
- Start a small run and ensure completion does not leave SSE connections open (observe via browser network if needed).
- Cancel a run and confirm subscriptions close promptly.
- Run:
-
Step 6: Commit (optional)
- If doing commits:
- Run:
git add frontend/src/stores/transfers.ts - Run:
git commit -m "fix: close SSE subscriptions for transfer tasks"
- Run:
- If doing commits:
Task 4: End-to-end verification
Files:
-
Verify:
frontend/src/views/TransfersView.vue -
Verify:
frontend/src/components/SftpFilePickerModal.vue -
Verify:
frontend/src/stores/transfers.ts -
Step 1: Frontend build
- Run:
npm --prefix frontend run build - Expected: success.
- Run:
-
Step 2: Smoke test (manual)
- Start app (if needed):
- Backend:
mvn -f backend/pom.xml spring-boot:run - Frontend:
npm --prefix frontend run dev
- Backend:
- If deps aren't installed:
- Frontend:
npm --prefix frontend install
- Frontend:
- Use Transfers:
- Remote -> Many: pick 2-3 files, 2 targets, Directory mode.
- Verify files arrive at target directory with correct names.
- Verify errors are per-item, run continues for other items.
- Start app (if needed):
-
Step 3: Commit (optional)
- If doing a final commit:
- Run:
git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue frontend/src/stores/transfers.ts - Run:
git commit -m "feat: support multi-file Remote -> Many transfers"
- Run:
- If doing a final commit: