tabby-sftp-ui 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/index.js +287 -33
- package/dist/index.js.map +1 -1
- package/dist/sftp-manager-tab.component.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,8 +126,13 @@ Then restart Tabby.
|
|
|
126
126
|
|
|
127
127
|
### Changelog
|
|
128
128
|
|
|
129
|
-
-
|
|
130
|
-
|
|
129
|
+
- **0.2.4**
|
|
130
|
+
- Fix: OS drag-and-drop from Explorer
|
|
131
|
+
- Fix: Multiple files drag-and-drop between panes works again.
|
|
132
|
+
- **0.2.3**
|
|
133
|
+
- Fix: Remote folders drag-and-drop (Remote → Local) works again.
|
|
134
|
+
- Feature: Replace confirmation is now symmetric (both Local → Remote and Remote → Local).
|
|
135
|
+
- UI: Added “Go up” row to the Local pane.
|
|
131
136
|
- **0.2.2**
|
|
132
137
|
- Integrate Tabby’s native `startUploadFromDragEvent` for more reliable OS drag-and-drop (files & folders).
|
|
133
138
|
- Refine Delete key handling so it works normally inside input dialogs while still triggering delete in lists.
|
package/dist/index.js
CHANGED
|
@@ -309,6 +309,9 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
309
309
|
this.deleteConfirmText = '';
|
|
310
310
|
this.pendingLocalDelete = [];
|
|
311
311
|
this.pendingRemoteDelete = [];
|
|
312
|
+
this.replaceConfirmVisible = false;
|
|
313
|
+
this.replaceConfirmText = '';
|
|
314
|
+
this.replaceConfirmResolve = null;
|
|
312
315
|
this.inputDialogVisible = false;
|
|
313
316
|
this.inputDialogTitle = '';
|
|
314
317
|
this.inputDialogPlaceholder = '';
|
|
@@ -593,12 +596,13 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
593
596
|
const sources = this.selectedLocal.includes(e) && this.selectedLocal.length ? this.selectedLocal : [e];
|
|
594
597
|
const movePayload = sources.map(x => x.fullPath);
|
|
595
598
|
ev.dataTransfer?.setData('application/x-tabby-sftp-ui-local-move', JSON.stringify(movePayload));
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
599
|
+
// Cross-device drag (local -> remote) supports multi-select
|
|
600
|
+
const payload = {
|
|
601
|
+
kind: 'local-paths',
|
|
602
|
+
paths: sources.map(x => ({ fullPath: x.fullPath, name: x.name, isDirectory: x.isDirectory })),
|
|
603
|
+
};
|
|
604
|
+
ev.dataTransfer?.setData('application/x-tabby-sftp-ui', JSON.stringify(payload));
|
|
605
|
+
ev.dataTransfer?.setData('text/plain', e.fullPath);
|
|
602
606
|
ev.dataTransfer?.setDragImage?.(ev.target ?? document.body, 0, 0);
|
|
603
607
|
}
|
|
604
608
|
onDragStartRemote(ev, item) {
|
|
@@ -608,18 +612,19 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
608
612
|
const sources = this.selectedRemote.includes(item) && this.selectedRemote.length ? this.selectedRemote : [item];
|
|
609
613
|
const movePayload = sources.map(x => x.fullPath);
|
|
610
614
|
ev.dataTransfer?.setData('application/x-tabby-sftp-ui-remote-move', JSON.stringify(movePayload));
|
|
611
|
-
//
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
remotePath:
|
|
616
|
-
name:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
615
|
+
// Cross-device drag (remote -> local) supports multi-select
|
|
616
|
+
const payload = {
|
|
617
|
+
kind: 'remote-paths',
|
|
618
|
+
paths: sources.map(x => ({
|
|
619
|
+
remotePath: x.fullPath,
|
|
620
|
+
name: x.name,
|
|
621
|
+
isDirectory: x.isDirectory,
|
|
622
|
+
size: x.size,
|
|
623
|
+
mode: x.mode,
|
|
624
|
+
})),
|
|
625
|
+
};
|
|
626
|
+
ev.dataTransfer?.setData('application/x-tabby-sftp-ui', JSON.stringify(payload));
|
|
627
|
+
ev.dataTransfer?.setData('text/plain', item.fullPath);
|
|
623
628
|
ev.dataTransfer?.setDragImage?.(ev.target ?? document.body, 0, 0);
|
|
624
629
|
}
|
|
625
630
|
async onDropOnRemote(ev) {
|
|
@@ -636,6 +641,16 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
636
641
|
if (osPaths.length) {
|
|
637
642
|
try {
|
|
638
643
|
for (const p of osPaths) {
|
|
644
|
+
const baseName = path__WEBPACK_IMPORTED_MODULE_0__.basename(p);
|
|
645
|
+
const existing = this.remoteEntries.find(e => e.name === baseName);
|
|
646
|
+
if (existing) {
|
|
647
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${baseName}" on remote?`);
|
|
648
|
+
if (!ok) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const remoteTarget = path__WEBPACK_IMPORTED_MODULE_0__.posix.join(this.remotePath, baseName);
|
|
652
|
+
await this.deleteRemotePathRecursive(remoteTarget);
|
|
653
|
+
}
|
|
639
654
|
await this.uploadLocalPathToRemote(this.remotePath, p);
|
|
640
655
|
}
|
|
641
656
|
await this.refreshRemote();
|
|
@@ -668,15 +683,40 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
668
683
|
catch {
|
|
669
684
|
return;
|
|
670
685
|
}
|
|
671
|
-
if (payload.kind !== 'local-file') {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
686
|
try {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
687
|
+
if (payload.kind === 'local-file') {
|
|
688
|
+
const targetRemotePath = path__WEBPACK_IMPORTED_MODULE_0__.posix.join(this.remotePath, payload.name);
|
|
689
|
+
const existsOnRemote = this.remoteEntries.some(e => e.name === payload.name);
|
|
690
|
+
if (existsOnRemote) {
|
|
691
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${payload.name}" on remote?`);
|
|
692
|
+
if (!ok) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
await this.deleteRemotePathRecursive(targetRemotePath);
|
|
696
|
+
}
|
|
697
|
+
const upload = new _local_transfers__WEBPACK_IMPORTED_MODULE_7__.LocalPathFileUpload(payload.fullPath);
|
|
698
|
+
this.trackTransfer(upload, 'upload', targetRemotePath, payload.fullPath);
|
|
699
|
+
await this.sftpSession.upload(targetRemotePath, upload);
|
|
700
|
+
await this.refreshRemote();
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (payload.kind === 'local-paths') {
|
|
704
|
+
for (const p of payload.paths) {
|
|
705
|
+
const targetRemotePath = path__WEBPACK_IMPORTED_MODULE_0__.posix.join(this.remotePath, p.name);
|
|
706
|
+
const existsOnRemote = this.remoteEntries.some(e => e.name === p.name);
|
|
707
|
+
if (existsOnRemote) {
|
|
708
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${p.name}" on remote?`);
|
|
709
|
+
if (!ok) {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
await this.deleteRemotePathRecursive(targetRemotePath);
|
|
713
|
+
}
|
|
714
|
+
// uploadLocalPathToRemote handles both files and directories
|
|
715
|
+
await this.uploadLocalPathToRemote(this.remotePath, p.fullPath);
|
|
716
|
+
}
|
|
717
|
+
await this.refreshRemote();
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
680
720
|
}
|
|
681
721
|
catch (e) {
|
|
682
722
|
console.error('[SFTP-UI] Upload failed', e);
|
|
@@ -685,11 +725,103 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
685
725
|
async onDropOnLocal(ev) {
|
|
686
726
|
ev.preventDefault();
|
|
687
727
|
this.localDropActive = false;
|
|
728
|
+
// 1) Tabby's internal drag (remote -> local download)
|
|
729
|
+
const rawInternal = ev.dataTransfer?.getData('application/x-tabby-sftp-ui');
|
|
730
|
+
if (rawInternal) {
|
|
731
|
+
let payload;
|
|
732
|
+
try {
|
|
733
|
+
payload = JSON.parse(rawInternal);
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
payload = null;
|
|
737
|
+
}
|
|
738
|
+
if (payload && payload.kind === 'remote-file') {
|
|
739
|
+
try {
|
|
740
|
+
const targetLocalPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, payload.name);
|
|
741
|
+
if (!this.sftpSession) {
|
|
742
|
+
throw new Error('Not connected');
|
|
743
|
+
}
|
|
744
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(targetLocalPath)) {
|
|
745
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${payload.name}"?`);
|
|
746
|
+
if (!ok) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const dl = new _local_transfers__WEBPACK_IMPORTED_MODULE_7__.LocalPathFileDownload(targetLocalPath, payload.mode, payload.size);
|
|
751
|
+
this.trackTransfer(dl, 'download', payload.remotePath, targetLocalPath);
|
|
752
|
+
await this.sftpSession.download(payload.remotePath, dl);
|
|
753
|
+
await this.refreshLocal();
|
|
754
|
+
}
|
|
755
|
+
catch (e) {
|
|
756
|
+
console.error('[SFTP-UI] Download failed', e);
|
|
757
|
+
}
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (payload && payload.kind === 'remote-dir') {
|
|
761
|
+
try {
|
|
762
|
+
if (!this.sftpSession) {
|
|
763
|
+
throw new Error('Not connected');
|
|
764
|
+
}
|
|
765
|
+
const targetLocalPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, payload.name);
|
|
766
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(targetLocalPath)) {
|
|
767
|
+
const ok = await this.showReplaceConfirm(`Replace existing folder "${payload.name}"?`);
|
|
768
|
+
if (!ok) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
await this.deleteLocalPathRecursive(targetLocalPath);
|
|
772
|
+
}
|
|
773
|
+
await this.downloadRemoteDirectoryRecursive(payload.remotePath, targetLocalPath);
|
|
774
|
+
await this.refreshLocal();
|
|
775
|
+
}
|
|
776
|
+
catch (e) {
|
|
777
|
+
console.error('[SFTP-UI] Download directory failed', e);
|
|
778
|
+
}
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (payload && payload.kind === 'remote-paths') {
|
|
782
|
+
try {
|
|
783
|
+
if (!this.sftpSession) {
|
|
784
|
+
throw new Error('Not connected');
|
|
785
|
+
}
|
|
786
|
+
for (const it of payload.paths) {
|
|
787
|
+
const targetLocalPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, it.name);
|
|
788
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(targetLocalPath)) {
|
|
789
|
+
const ok = await this.showReplaceConfirm(it.isDirectory ? `Replace existing folder "${it.name}"?` : `Replace existing "${it.name}"?`);
|
|
790
|
+
if (!ok) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
await this.deleteLocalPathRecursive(targetLocalPath);
|
|
794
|
+
}
|
|
795
|
+
if (it.isDirectory) {
|
|
796
|
+
await this.downloadRemoteDirectoryRecursive(it.remotePath, targetLocalPath);
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
const dl = new _local_transfers__WEBPACK_IMPORTED_MODULE_7__.LocalPathFileDownload(targetLocalPath, it.mode ?? 0o644, it.size ?? 0);
|
|
800
|
+
this.trackTransfer(dl, 'download', it.remotePath, targetLocalPath);
|
|
801
|
+
await this.sftpSession.download(it.remotePath, dl);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
await this.refreshLocal();
|
|
805
|
+
}
|
|
806
|
+
catch (e) {
|
|
807
|
+
console.error('[SFTP-UI] Download paths failed', e);
|
|
808
|
+
}
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
688
812
|
// Drag & drop from OS file manager into the local pane (copy into current local folder)
|
|
689
813
|
const osPaths = this.getDroppedOsPaths(ev);
|
|
690
814
|
if (osPaths.length) {
|
|
691
815
|
try {
|
|
692
816
|
for (const p of osPaths) {
|
|
817
|
+
const baseName = path__WEBPACK_IMPORTED_MODULE_0__.basename(p);
|
|
818
|
+
const destPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, baseName);
|
|
819
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(destPath)) {
|
|
820
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${baseName}"?`);
|
|
821
|
+
if (!ok) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
693
825
|
await this.copyLocalPathIntoLocalDir(this.localPath, p);
|
|
694
826
|
}
|
|
695
827
|
await this.refreshLocal();
|
|
@@ -722,17 +854,41 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
722
854
|
catch {
|
|
723
855
|
return;
|
|
724
856
|
}
|
|
725
|
-
if (payload.kind !== 'remote-file') {
|
|
857
|
+
if (payload.kind !== 'remote-file' && payload.kind !== 'remote-dir') {
|
|
726
858
|
return;
|
|
727
859
|
}
|
|
728
860
|
try {
|
|
729
|
-
|
|
861
|
+
if (payload.kind === 'remote-file') {
|
|
862
|
+
const targetLocalPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, payload.name);
|
|
863
|
+
if (!this.sftpSession) {
|
|
864
|
+
throw new Error('Not connected');
|
|
865
|
+
}
|
|
866
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(targetLocalPath)) {
|
|
867
|
+
const ok = await this.showReplaceConfirm(`Replace existing "${payload.name}"?`);
|
|
868
|
+
if (!ok) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
await this.deleteLocalPathRecursive(targetLocalPath);
|
|
872
|
+
}
|
|
873
|
+
const dl = new _local_transfers__WEBPACK_IMPORTED_MODULE_7__.LocalPathFileDownload(targetLocalPath, payload.mode, payload.size);
|
|
874
|
+
this.trackTransfer(dl, 'download', payload.remotePath, targetLocalPath);
|
|
875
|
+
await this.sftpSession.download(payload.remotePath, dl);
|
|
876
|
+
await this.refreshLocal();
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
// remote-dir -> local-dir (recursive download)
|
|
730
880
|
if (!this.sftpSession) {
|
|
731
881
|
throw new Error('Not connected');
|
|
732
882
|
}
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
883
|
+
const targetLocalPath = path__WEBPACK_IMPORTED_MODULE_0__.join(this.localPath, payload.name);
|
|
884
|
+
if (fs__WEBPACK_IMPORTED_MODULE_3__.existsSync(targetLocalPath)) {
|
|
885
|
+
const ok = await this.showReplaceConfirm(`Replace existing folder "${payload.name}"?`);
|
|
886
|
+
if (!ok) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
await this.deleteLocalPathRecursive(targetLocalPath);
|
|
890
|
+
}
|
|
891
|
+
await this.downloadRemoteDirectoryRecursive(payload.remotePath, targetLocalPath);
|
|
736
892
|
await this.refreshLocal();
|
|
737
893
|
}
|
|
738
894
|
catch (e) {
|
|
@@ -837,11 +993,40 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
837
993
|
}
|
|
838
994
|
}
|
|
839
995
|
}
|
|
996
|
+
async downloadRemoteDirectoryRecursive(remoteDir, localDir) {
|
|
997
|
+
if (!this.sftpSession) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
await fs_promises__WEBPACK_IMPORTED_MODULE_2__.mkdir(localDir, { recursive: true });
|
|
1001
|
+
const entries = await this.sftpSession.readdir(remoteDir).catch(() => null);
|
|
1002
|
+
if (!entries) {
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
for (const e of entries) {
|
|
1006
|
+
const targetLocal = path__WEBPACK_IMPORTED_MODULE_0__.join(localDir, e.name);
|
|
1007
|
+
if (e.isDirectory) {
|
|
1008
|
+
await this.downloadRemoteDirectoryRecursive(e.fullPath, targetLocal);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
const dl = new _local_transfers__WEBPACK_IMPORTED_MODULE_7__.LocalPathFileDownload(targetLocal, e.mode, e.size);
|
|
1012
|
+
this.trackTransfer(dl, 'download', e.fullPath, targetLocal);
|
|
1013
|
+
await this.sftpSession.download(e.fullPath, dl);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
840
1017
|
getDroppedOsPaths(ev) {
|
|
841
1018
|
const dt = ev.dataTransfer;
|
|
842
1019
|
if (!dt) {
|
|
843
1020
|
return [];
|
|
844
1021
|
}
|
|
1022
|
+
const isWin = os__WEBPACK_IMPORTED_MODULE_1__.platform() === 'win32';
|
|
1023
|
+
const isLocalPath = (p) => {
|
|
1024
|
+
if (isWin) {
|
|
1025
|
+
// Accept both `C:\...` and `C:/...` (some drag sources provide forward slashes)
|
|
1026
|
+
return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith('\\\\');
|
|
1027
|
+
}
|
|
1028
|
+
return p.startsWith('/');
|
|
1029
|
+
};
|
|
845
1030
|
// 1) Electron-style File.path
|
|
846
1031
|
const filePaths = Array.from(dt.files ?? [])
|
|
847
1032
|
.map(f => f.path)
|
|
@@ -866,7 +1051,14 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
866
1051
|
}
|
|
867
1052
|
return x;
|
|
868
1053
|
})
|
|
869
|
-
.
|
|
1054
|
+
.map(x => {
|
|
1055
|
+
// Some sources may produce `/C:/Users/...`
|
|
1056
|
+
if (isWin && /^\/[A-Za-z]:[\\/]/.test(x)) {
|
|
1057
|
+
return x.slice(1);
|
|
1058
|
+
}
|
|
1059
|
+
return x;
|
|
1060
|
+
})
|
|
1061
|
+
.filter(x => x && isLocalPath(x));
|
|
870
1062
|
if (uris.length) {
|
|
871
1063
|
return uris;
|
|
872
1064
|
}
|
|
@@ -885,7 +1077,13 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
885
1077
|
}
|
|
886
1078
|
return x;
|
|
887
1079
|
})
|
|
888
|
-
.
|
|
1080
|
+
.map(x => {
|
|
1081
|
+
if (isWin && /^\/[A-Za-z]:[\\/]/.test(x)) {
|
|
1082
|
+
return x.slice(1);
|
|
1083
|
+
}
|
|
1084
|
+
return x;
|
|
1085
|
+
})
|
|
1086
|
+
.filter(x => x && isLocalPath(x));
|
|
889
1087
|
return textPaths;
|
|
890
1088
|
}
|
|
891
1089
|
getFilteredLocalEntries() {
|
|
@@ -1800,6 +1998,11 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
1800
1998
|
this.cancelDelete();
|
|
1801
1999
|
return;
|
|
1802
2000
|
}
|
|
2001
|
+
if (this.replaceConfirmVisible) {
|
|
2002
|
+
event.preventDefault();
|
|
2003
|
+
this.cancelReplace();
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
1803
2006
|
}
|
|
1804
2007
|
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
1805
2008
|
// Don't intercept Delete/Backspace while typing in inputs
|
|
@@ -1881,6 +2084,37 @@ let SftpManagerTabComponent = class SftpManagerTabComponent extends tabby_core__
|
|
|
1881
2084
|
this.pendingLocalDelete = [];
|
|
1882
2085
|
this.pendingRemoteDelete = [];
|
|
1883
2086
|
}
|
|
2087
|
+
async showReplaceConfirm(text) {
|
|
2088
|
+
if (this.replaceConfirmVisible) {
|
|
2089
|
+
// Prevent stacking multiple confirmations; choose the latest replacement intent.
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
this.replaceConfirmText = text;
|
|
2093
|
+
this.replaceConfirmVisible = true;
|
|
2094
|
+
return new Promise(resolve => {
|
|
2095
|
+
this.replaceConfirmResolve = resolve;
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
async confirmReplace() {
|
|
2099
|
+
if (!this.replaceConfirmVisible) {
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
this.replaceConfirmVisible = false;
|
|
2103
|
+
const resolve = this.replaceConfirmResolve;
|
|
2104
|
+
this.replaceConfirmResolve = null;
|
|
2105
|
+
this.replaceConfirmText = '';
|
|
2106
|
+
resolve?.(true);
|
|
2107
|
+
}
|
|
2108
|
+
cancelReplace() {
|
|
2109
|
+
if (!this.replaceConfirmVisible) {
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
this.replaceConfirmVisible = false;
|
|
2113
|
+
const resolve = this.replaceConfirmResolve;
|
|
2114
|
+
this.replaceConfirmResolve = null;
|
|
2115
|
+
this.replaceConfirmText = '';
|
|
2116
|
+
resolve?.(false);
|
|
2117
|
+
}
|
|
1884
2118
|
async deleteLocalEntry(entry) {
|
|
1885
2119
|
await this.deleteLocalPathRecursive(entry.fullPath);
|
|
1886
2120
|
}
|
|
@@ -2188,6 +2422,16 @@ SftpManagerTabComponent = __decorate([
|
|
|
2188
2422
|
<span class="size sortable" (click)="setLocalSort('size')">Size</span>
|
|
2189
2423
|
<span class="date sortable" (click)="setLocalSort('modified')">Modified</span>
|
|
2190
2424
|
</div>
|
|
2425
|
+
<div
|
|
2426
|
+
class="entry"
|
|
2427
|
+
*ngIf="canLocalUp()"
|
|
2428
|
+
(dblclick)="localUp()"
|
|
2429
|
+
>
|
|
2430
|
+
<span class="icon">⬆</span>
|
|
2431
|
+
<span class="name">Go up</span>
|
|
2432
|
+
<span class="size"></span>
|
|
2433
|
+
<span class="date"></span>
|
|
2434
|
+
</div>
|
|
2191
2435
|
<div
|
|
2192
2436
|
class="entry"
|
|
2193
2437
|
*ngFor="let e of getFilteredLocalEntries()"
|
|
@@ -2366,6 +2610,16 @@ SftpManagerTabComponent = __decorate([
|
|
|
2366
2610
|
</div>
|
|
2367
2611
|
</div>
|
|
2368
2612
|
|
|
2613
|
+
<div class="delete-overlay" *ngIf="replaceConfirmVisible">
|
|
2614
|
+
<div class="delete-dialog">
|
|
2615
|
+
<div class="delete-text">{{ replaceConfirmText }}</div>
|
|
2616
|
+
<div class="delete-buttons">
|
|
2617
|
+
<button class="danger" (click)="confirmReplace()">Replace</button>
|
|
2618
|
+
<button (click)="cancelReplace()">Cancel</button>
|
|
2619
|
+
</div>
|
|
2620
|
+
</div>
|
|
2621
|
+
</div>
|
|
2622
|
+
|
|
2369
2623
|
<div class="delete-overlay" *ngIf="inputDialogVisible">
|
|
2370
2624
|
<div class="delete-dialog" (click)="$event.stopPropagation()">
|
|
2371
2625
|
<div class="delete-text">{{ inputDialogTitle }}</div>
|