P3: transfer engine with queue, progress, cancel + transfer bar UI

- TransferEngine: FIFO queue, provider-generic stream pipeline (covers all four
  panel combos), throttled transferUpdate push, cancel via stream destroy,
  self-copy guards, failSession hook for dead SSH sessions
- TransfersService + TransferBar: per-job rows, progress bars, cancel/dismiss,
  auto-dismiss completed, dst panel refresh
- F5 + Copy button wired to active-panel selection
- verified: 30MB nested tree copy (UTF-8 names, empty dirs), self-copy rejection,
  mid-transfer cancel leaving partial file

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 13:46:34 +02:00
co-authored by Claude Fable 5
parent b907e0a4ce
commit 88af44acfc
9 changed files with 455 additions and 7 deletions
+5
View File
@@ -13,6 +13,7 @@ import type { FileEntry } from '@shared/types';
import { FileActionsService } from '../file-actions.service';
import { formatDate, formatSize } from '../format';
import { PanelsService, type Side, type SortKey } from '../panels.service';
import { TransfersService } from '../transfers.service';
@Component({
selector: 'app-file-panel',
@@ -25,6 +26,7 @@ export class FilePanel {
readonly panels = inject(PanelsService);
readonly actions = inject(FileActionsService);
readonly transfers = inject(TransfersService);
readonly panel = computed(() => this.panels.get(this.side()));
readonly isActive = computed(() => this.panels.activeSide() === this.side());
@@ -144,6 +146,9 @@ export class FilePanel {
case 'F2':
this.actions.rename();
break;
case 'F5':
void this.transfers.copyToOtherPanel();
break;
case 'F7':
this.actions.mkdir();
break;