After #2955, upload conflict checks lstat the destination first.
New files throw ssh2's "No such file"; Electron invoke strips `code`
and wraps the message, so isMissingStatError treated absence as a
hard error and every new upload toasted.
* fix(sftp): instant folder-drop scan UI and drop full-tree pre-scan
Restore interleaved folder transfers as the only non-compressed path, remove
the folder pre-scan setting, and make local folder drops show a scanning task
with live discovered counts via native listLocalTree (with cancel + progress).
UI shows "done · found" instead of a fake grand-total percentage while
discovery is still growing.
* fix(sftp): make folder scan cancellation bridge-safe
* fix(sftp): preserve path-only terminal drops
* fix(terminal): accept native folder drop paths
* fix(sftp): keep scan task cancellable during setup
* fix(sftp): preserve scan failure state during setup errors
* fix(sftp): keep folder scan task across retries
* fix(sftp): cancel scan task immediately
* fix(sftp): keep cancellation active through session setup
* fix(sftp): settle failed drop upload retry
* fix(sftp): preserve cancellation in final scan cleanup
* fix(sftp): harden folder scan cancellation and conflicts
* fix(sftp): finish Codex follow-ups on folder scan cancel UX
- Abort sibling listLocalTree walks when one multi-root scan fails
- Prefer ERR_*_CANCELLED over matching "cancel" in error text
- Clear scanning phase on settle; only label live rows as Scanning
- Omit directory metadata sizes from drop entries used for totals
* fix(sftp): stream folder discovery into concurrent uploads
Local path-backed folder drops now edge-scan and edge-transfer: listLocalTree
streams entry batches while upload workers consume them, so the first files
start before a 100k-file walk finishes. Cancel aborts the walk, clears the
queue, and stops creating child tasks. Compressed uploads still materialize
fully then pack.
* fix(sftp): harden progressive folder UX, retry, and size-based uploads
Align the SFTP panel with transfer-center progress, keep nested tree batches
until tree-end, honor pause during progressive enqueue, add low-cost retry for
external drag-drop files, and drop whole-file upload digests for industry
size-based resume (download live-log prefix checks unchanged).
* fix(sftp): address multi-agent review on progressive upload and retry
Gate progressive drops to path-backed folders only, prefer pool sessions on
drag-drop retry with in-flight and cancel guards, soft-check upload mtime
without fail-open contentVerifiedSeparately, skip multi-root pause HOL,
and fail closed when the local tree stream misses tree-end.
* fix(sftp): finish multi-root pause HOL and parent rollup guards
Race-wait on all latched parents so resuming a non-head folder can proceed,
and only roll progressive parents to completed after terminal failed status.
* fix(sftp): address Codex progressive conflict and hang findings
Add root-level conflict preflight to progressive folder uploads, wake all
backpressure waiters, preserve cancel listeners across uploadEntriesDirect,
and surface empty-directory mkdir failures.
Prefer contiguous bridge checkpointBytes over high-water transferred for
resume offsets, read remote modifyTime for meta pause identity, clear
deferredSparseTruncate on failed pause unpause, and forward fingerprint
fields through drag-drop upload progress.
Add dedicated transfer connections (1–2 per host) so bulk uploads/downloads
no longer block browse sessions. Also cover session leases, resume after
restart, auto-reconnect, skipAdmission, and transfer-center UX fixes for #2396.
* fix(sftp): prevent corrupt uploads from oversized fastPut chunks
Restore ssh2's 32KB default chunk size, verify remote size after upload,
and treat stream finish (not close) as success so truncated zip/tar.gz
transfers fail loudly instead of looking complete (#2022).
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(sftp): wait for stream close after finish on upload fallback
Address Codex review: resolving on finish alone can miss close-time
server errors while the remote handle is still open. Require finish then
close, and fail on premature close.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add SFTP upload conflict handling
Add conflict resolution for SFTP uploads so files and folders can be stopped, skipped, replaced, duplicated, or merged depending on the target state. Support batch uploads with Apply to All behavior, route external upload conflicts through the shared SFTP conflict dialog, and add the bridge operations needed to stat and delete existing upload targets.
* fix review issue
* Fix SFTP conflict cancellation cleanup
---------
Co-authored-by: yuzifu <yuzifu@TB16PGen5.Info>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
* feat(sftp): add onListDirectory to SftpPaneCallbacks interface
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* feat(sftp): implement onListDirectory in left and right callbacks
* feat(sftp): add tree view i18n keys
* feat(sftp): add list/tree view mode toggle to toolbar
* feat(sftp): add viewMode state and tree view conditional rendering to SftpPaneView
* feat(sftp): implement SftpPaneTreeView with lazy loading and context menu
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix(sftp): resolve lint errors in tree view implementation
Rename inner `t` and `ts` variables in onListDirectory callbacks to
`toSize`/`toTs`/`ms` to avoid shadowing the outer `t` translation param.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix(sftp): resolve post-merge lint errors
- Remove duplicate sftp.context.copyPath i18n key (upstream added it too)
- Remove unused AlertCircle import from SftpPaneFileList (upstream removed usage)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* perf(sftp): optimize SftpPaneTreeView render pipeline
Split useMemo into two stages so selection changes no longer
rebuild the full node descriptor array. Extract stable
selection-aware callbacks (drag, copy, delete) via refs so
TreeNode React.memo can reliably bail out. Remove unused props
(onNavigateTo, draggedFiles), move NodeDescriptor type to
module scope, and fix selectedFiles undefined bug in context menu.
* feat(sftp): add path-aware rename and delete for tree view
Wire renameFileAtPath and deleteFilesAtPath through the full
callback stack so tree view context menu actions operate on
full paths instead of basenames. Update useSftpPaneDialogs to
accept entryPath in openRenameDialog and resolve parent dir
in handleDelete, keeping list view behaviour unchanged.
* fix: harden SFTP tree view actions and selection
* fix: support tree selection shortcuts and nested create targets
* fix: keep SFTP tree view sorting in sync
* Improve SFTP tree view interactions and refresh behavior
* Optimize SFTP tree refresh and pane state usage
* Reduce remaining SFTP tree performance overhead
* Fix nested SFTP drop target routing
* Restore keyboard access to parent tree entry
* Revert "Display approved AI commands in terminal sessions before their output. (#546)"
This reverts commit 6d19413025.
* Fix SFTP tree view review issues: accessibility, view persistence, and polish
- Add aria-pressed/aria-checked to view mode toggle buttons for accessibility
- Preserve tree expanded state across view mode switches (CSS hidden instead of unmount)
- Add cross-window localStorage sync for view mode preferences
- Add loading/reconnecting overlay UI for tree view
- Fix toggleExpand concurrent load guard and file list memo dependencies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix review round 2: scroll jank, memo correctness, path handling, a11y
Critical:
- Fix rAF scroll throttle capturing stale scrollTop (use ref for latest value)
- Add sftpDefaultViewMode to memo comparator to react to settings changes
- Replace ad-hoc path splitting in handleDelete with getParentPath/getFileName
- Add fullPath to permissionsState prop type in SftpOverlays
Important:
- Remove treeSelectionState from handleNodeClick/handleTreeContainerKeyDown
deps to prevent full tree re-render on every expand/collapse
- Add role="radiogroup" container and aria-label to view toggle buttons
- Wrap JSON.parse in try/catch for storage event handler
- Deduplicate getParentPath call in renameFileAtPath
- Parallelize reloadExpandedPaths with Promise.all
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Clean up review round 3: dead code, logging, and minor optimizations
- Remove dead isParentNavigation field from tree selection store (always
false since ".." entries are filtered before entering the store)
- Replace empty catch blocks in dialog handlers with logger.warn
- Extract duplicated initialViewMode expression in SftpPaneView
- Stabilize handleSetViewMode by using refs for callbacks instead of
depending on the entire callbacks object
- Remove redundant FINISH_LOADING dispatch on error path in
loadChildrenForPath (LOAD_ERROR already removes from loadingPaths)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add same-pane drag-move, move-to dialog, and fix breadcrumb/tree sync
Features:
- Same-pane drag-and-drop to move files between directories in tree view
- "Move to..." context menu with path input dialog and autocomplete
- "Move to parent directory" quick action in context menu
- "Navigate to" context menu item for directories
- Error state UI with retry button in tree view
- Breadcrumb path deferred display during loading
Fixes:
- Fix breadcrumb and tree content showing different paths during navigation
by atomically syncing resolvedRootPath and rootEntries in a single effect
- Fix toolbar displayPath updating before files load (defer until !loading)
- Reconnection detection and session error reporting in tree directory listing
UI improvements:
- Column widths use minmax()+fr instead of percentages with min-width protection
- Column headers truncate with overflow protection
- buildSftpColumnTemplate utility shared between tree and list views
- Column resize limits per field
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Reapply "Display approved AI commands in terminal sessions before their output. (#546)"
This reverts commit f739e81e8d.
* fix: resolve remote-to-local drag transfer source pane
* fix: invalidate target cache after transfers
* fix: reload tree root after create mutations
* fix: use receive callback for tree drop targets
* fix: trigger pane refresh after transfer completion
* fix: handle transfer refresh tokens only once
* fix: show move-to-parent for direct children
* fix: refresh list view after move-to-parent changes
* fix: address review issues in transfer refresh and retry flows
* feat: improve list view keyboard and folder drops
* fix: strengthen list view keyboard selection feedback
* style: make list view selection more obvious
* fix: keep list selection visible during keyboard navigation
* fix: rerender list rows when selection changes
* fix: sync list selection highlight updates
* style: align list selection with tree view
* style: hide list selection highlight when pane is unfocused
* feat: clear list selection when clicking empty space
* refine transfer row layout and clear list selection on empty click
* perf: make transfer size discovery asynchronous
* perf: parallelize SFTP transfers and show per-file progress for directories
- Parallelize file transfers within directories (4 concurrent workers)
- Batch pre-create all directories before file uploads begin
- Run conflict check and size discovery concurrently
- Parallelize external drag-drop file uploads (4 concurrent workers)
- Show individual child file progress under parent directory task
- Parent directory task displays file count progress (e.g. "3/10 files")
- Child tasks auto-cleanup on parent completion or cancellation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refine sftp transfer panel ux
* fix sftp sidebar and upload task flow
* polish sftp transfer interactions
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
## Summary
- Add SFTP side panel with workspace-level connection caching for instant switching between terminal endpoints
- Responsive toolbar with overflow menu that collapses action buttons when panel is narrow, prioritizing breadcrumb path display
- Silent terminal CWD detection via separate SSH exec channel (no visible commands in terminal)
- Extract SftpTransferQueue as reusable component with i18n support
- Remove passphrase from port forwarding credentials (decrypted at load time)
- Add compressed upload support to uploadEntriesDirect
- Fix various eslint warnings and code quality issues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use FolderUp icon for folder upload in context menu (matches toolbar)
- Auto-close encoding popover when an option is selected
- Add trailing newline to uploadService.ts
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Remove verbose console.log statements from upload components
- Add maximum timeout cap (10 min) for extraction to prevent hangs
- Fix cleanup race condition by checking connection state
- Prefix unused speed parameter with underscore
- Fix duplicate return statement and unnecessary hook dependencies
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Replace 3-option setting (ask/enabled/disabled) with boolean toggle
- Remove CompressedUploadDialog component - no more user prompts
- Auto-detect tar availability and silently fallback to regular upload
- Apply compressed upload setting to drag-and-drop uploads
- Fix upload progress updates for compressed uploads via callback
- Add i18n for upload phase labels (compressing/uploading/extracting)
- Fix empty folder handling - fallback to regular mkdir
- Preserve error message on failed upload tasks for UI display
- Fix fallback logic to only re-upload failed folders, not successful ones
- Handle cancellation during all phases (compression/transfer/extraction)
- Fix filename display to use explicit phase markers instead of substring match
- Fix Toggle onChange prop for settings to work correctly
- Use DropEntry.relativePath for correct drag-drop folder paths
- Dynamic extraction timeout based on archive size (60s base + 30s per 10MB)
Co-Authored-By: Claude (gemini-claude-opus-4-5-thinking) <noreply@anthropic.com>
- Extract standalone file entries separately from folder entries during compressed uploads
- Add logic to upload standalone files using regular upload after compressed folders complete
- Combine results from both compressed folder uploads and standalone file uploads
- Ensure all files are uploaded correctly when mixed with compressed folder uploads
- This allows proper handling of mixed file and folder uploads in a single operation
- Declare tempArchivePath in outer scope for proper cleanup access in error handlers
- Add path separator normalization to handle both forward and backslash separators
- Improve folder path extraction logic to correctly identify the target folder path
- Add fallback pattern matching for both Unix and Windows path separators
- Preserve original path separator style when reconstructing folder paths
- Enhance robustness of path parsing for cross-platform file uploads
- Add activeCompressionIds Set to track ongoing compression operations
- Implement addActiveCompression() and removeActiveCompression() methods for lifecycle management
- Update cancel() method to iterate through active compression IDs and call cancelCompressedUpload()
- Enhance getActiveTransferIds() to include compression IDs alongside file transfer IDs
- Clear activeCompressionIds in reset() method to ensure clean state
- Register compression ID with controller before starting folder compression
- Add cancellation checks before and during compression progress updates
- Remove compression ID from tracking on completion or error
- Distinguish between cancellation and error states in result handling
- Improve logging to separately track file transfer IDs and compression IDs
- Enables proper cancellation of compressed uploads when user cancels the operation
- Fix regex pattern escaping in trailing slash removal (use forward slash instead of escaped forward slash)
- Declare taskId outside try block to ensure it's accessible in catch block for proper error handling
- Update onTaskFailed callback to pass taskId instead of folderName for consistency with task tracking
- Add guard condition to only call onTaskFailed when taskId exists (task was successfully created)
- Prevents undefined taskId from being passed to error callbacks and improves error state management
- Refactor folder path calculation to handle nested directory structures correctly
- Remove the filename-based extraction approach in favor of relativePath-based logic
- Add fallback mechanisms to handle edge cases where relativePath doesn't match localFilePath
- Implement folder name-based path detection as secondary fallback strategy
- Preserve original logic as last resort for single file scenarios
- Fix issue where deeply nested folders were not correctly identified during compression
- Add try-catch wrapper around uploadFoldersCompressed to handle compression failures gracefully
- Implement fallback to regular upload when compressed upload is not supported
- Check for failed folders in compressed results and trigger full fallback if needed
- Return error indicator from uploadFoldersCompressed instead of attempting inline fallback
- Improve error logging to distinguish between compression support issues and other failures
- Ensure all entries are uploaded via regular upload path when compression is unavailable
- This prevents upload failures when the server doesn't support compressed folder uploads
- Add CompressedUploadDialog component to let users choose between compressed and regular transfer methods
- Implement compressUploadService for handling folder compression and extraction on the server
- Add compressUploadBridge to expose compression functionality to the renderer process
- Add sftpUseCompressedUpload setting with three modes: ask, enabled, disabled
- Add new upload progress states: compressing, extracting, scanning, completed
- Add i18n translations for upload dialog and settings in English and Chinese
- Update SFTP modal to support compressed upload workflow with progress tracking
- Add storage key for persisting compressed upload preference across sessions
- Significantly reduces transfer time for folders by using tar compression when available on server
- Add i18n translations for "Upload folder" in English and Chinese locales
- Add folderInputRef to track folder input element in SFTPModal component
- Implement handleFolderSelect handler for processing folder uploads
- Add "Upload folder" button to SftpModalHeader with FolderUp icon
- Add hidden file input with webkitdirectory attribute for folder selection
- Update SftpModalFileList context menu to include folder upload option
- Pass folderInputRef and folder handlers through component hierarchy
- Enable users to upload entire folder structures via SFTP modal
- Replace memory-based file uploads with stream transfers for large files
- Add uploadWithStreams and downloadWithStreams functions in transferBridge
- Fix cancel transfer by properly destroying streams instead of throwing
errors in callbacks (which corrupted SSH connection)
- Fix upload button not triggering upload by copying FileList before
clearing input (clearing input also clears FileList reference)
- Export getPathForFile utility for obtaining local file paths
- Add startStreamTransfer and cancelTransfer bridge methods
Co-Authored-By: Claude <noreply@anthropic.com>
- uploadService.ts: Mark all bundle tasks as cancelled on early loop exit
- uploadService.ts: Don't set wasCancelled for actual errors, preserving
the error result for proper UI feedback
- useSftpModalTransfers.ts: Re-throw real errors instead of masking them
as cancellations, only return cancelled:true for user-initiated cancels
This ensures genuine failures (permission denied, disk full, connection
loss) are reported as errors with proper toast messages, while user
cancellations are correctly identified and handled.
Co-Authored-By: Claude <noreply@anthropic.com>
Adopts a "fail-fast" strategy for SFTP uploads, stopping the entire transfer process upon encountering any error during file or directory operations.
Introduces a new `isFatalUploadError` helper to consolidate and expand the definition of errors that should halt an upload, now explicitly including cases where the target directory is deleted or inaccessible during the transfer.
Removes specific fatal error checks from various components, streamlining error propagation and simplifying the overall error handling logic.
Ensures "Scanning files..." placeholder tasks are consistently removed when actual upload tasks are added, preventing potential state inconsistencies.
Refactors the SFTP upload mechanism to provide a more unified and robust experience.
- **Bundles folder uploads**: When uploading a folder from the local machine, it now appears as a single, aggregated task in the UI, showing overall progress instead of individual files.
- **Enhances cancellation**: Implements a new upload service and controller to manage transfers, allowing for more immediate and reliable cancellation of both individual files and bundled folder uploads.
- **Improves UI feedback**: Adds dedicated buttons for cancelling active uploads and dismissing completed, failed, or cancelled tasks.
- **Faster folder deletion**: Utilizes SSH `rm -rf` command for rapid remote folder deletion, falling back to SFTP rmdir if SSH exec is unavailable.
- Updates internationalization keys for single item deletion confirmation.