diff --git a/tabby-ssh/src/components/sftpCreateDirectoryModal.component.pug b/tabby-ssh/src/components/sftpCreateDirectoryModal.component.pug new file mode 100644 index 00000000..e320ca54 --- /dev/null +++ b/tabby-ssh/src/components/sftpCreateDirectoryModal.component.pug @@ -0,0 +1,13 @@ +.modal-body + label(translate) Create Directory + .form-group.w-100.mr-2 + label(translate) Name + input.form-control( + type='text', + [(ngModel)]='directoryName', + ) + + +.modal-footer + button.btn.btn-success((click)='create()', translate) Create + button.btn.btn-danger((click)='cancel()', translate) Cancel diff --git a/tabby-ssh/src/components/sftpCreateDirectoryModal.component.ts b/tabby-ssh/src/components/sftpCreateDirectoryModal.component.ts new file mode 100644 index 00000000..2f2a543c --- /dev/null +++ b/tabby-ssh/src/components/sftpCreateDirectoryModal.component.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import path from 'path' +import { BaseComponent } from 'tabby-core' +import { SFTPFile, SFTPSession } from '../session/sftp' +import { SFTPPanelComponent } from './sftpPanel.component' + +/** @hidden */ +@Component({ + template: require('./sftpCreateDirectoryModal.component.pug'), +}) +export class SFTPCreateDirectoryModalComponent extends BaseComponent { + sftp: SFTPSession + item: SFTPFile + panel: SFTPPanelComponent + directoryName: string + + constructor ( + private modalInstance: NgbActiveModal, + ) { + super() + } + + async ngOnInit (): Promise { + + } + + create (): void { + this.createDirectory(this.item.directory); + } + cancel (): void { + this.modalInstance.close() + } + + async createDirectory (currentDirectory: string): Promise { + this.sftp.mkdir(path.join(currentDirectory, this.directoryName)).finally(() => { + this.panel.navigate(path.join(currentDirectory, this.directoryName)); + this.cancel(); + }); + + } +} diff --git a/tabby-ssh/src/index.ts b/tabby-ssh/src/index.ts index 750838e5..e970b8f3 100644 --- a/tabby-ssh/src/index.ts +++ b/tabby-ssh/src/index.ts @@ -26,6 +26,7 @@ import { SFTPContextMenu } from './tabContextMenu' import { SSHProfilesService } from './profiles' import { SFTPContextMenuItemProvider } from './api/contextMenu' import { CommonSFTPContextMenu } from './sftpContextMenu' +import { SFTPCreateDirectoryModalComponent } from './components/sftpCreateDirectoryModal.component' /** @hidden */ @NgModule({ @@ -50,6 +51,7 @@ import { CommonSFTPContextMenu } from './sftpContextMenu' entryComponents: [ SSHProfileSettingsComponent, SFTPDeleteModalComponent, + SFTPCreateDirectoryModalComponent, SSHPortForwardingModalComponent, SSHSettingsTabComponent, SSHTabComponent, @@ -58,6 +60,7 @@ import { CommonSFTPContextMenu } from './sftpContextMenu' declarations: [ SSHProfileSettingsComponent, SFTPDeleteModalComponent, + SFTPCreateDirectoryModalComponent, SSHPortForwardingModalComponent, SSHPortForwardingConfigComponent, SSHSettingsTabComponent, diff --git a/tabby-ssh/src/session/sftp.ts b/tabby-ssh/src/session/sftp.ts index 4c85e70c..1e60ad8b 100644 --- a/tabby-ssh/src/session/sftp.ts +++ b/tabby-ssh/src/session/sftp.ts @@ -12,6 +12,7 @@ import type { FileEntry, Stats } from 'ssh2-streams' export interface SFTPFile { name: string fullPath: string + directory: string isDirectory: boolean isSymlink: boolean mode: number @@ -103,6 +104,7 @@ export class SFTPSession { const stats = await wrapPromise(this.zone, promisify(f => this.sftp.stat(p, f))()) return { name: posixPath.basename(p), + directory: posixPath.dirname(p), fullPath: p, isDirectory: stats.isDirectory(), isSymlink: stats.isSymbolicLink(), @@ -123,6 +125,11 @@ export class SFTPSession { await promisify((f: any) => this.sftp.rmdir(p, f))() } + async mkdir (p: string): Promise { + this.logger.debug('mkdir', p) + await promisify((f: any) => this.sftp.mkdir(p, f))() + } + async rename (oldPath: string, newPath: string): Promise { this.logger.debug('rename', oldPath, newPath) await promisify((f: any) => this.sftp.rename(oldPath, newPath, f))() @@ -181,6 +188,7 @@ export class SFTPSession { return { fullPath: p, name: posixPath.basename(p), + directory: posixPath.dirname(p), isDirectory: (entry.attrs.mode & C.S_IFDIR) === C.S_IFDIR, isSymlink: (entry.attrs.mode & C.S_IFLNK) === C.S_IFLNK, mode: entry.attrs.mode, diff --git a/tabby-ssh/src/sftpContextMenu.ts b/tabby-ssh/src/sftpContextMenu.ts index b1e0cff9..05b1c95b 100644 --- a/tabby-ssh/src/sftpContextMenu.ts +++ b/tabby-ssh/src/sftpContextMenu.ts @@ -5,6 +5,7 @@ import { SFTPSession, SFTPFile } from './session/sftp' import { SFTPContextMenuItemProvider } from './api' import { SFTPDeleteModalComponent } from './components/sftpDeleteModal.component' import { SFTPPanelComponent } from './components/sftpPanel.component' +import { SFTPCreateDirectoryModalComponent } from './components/sftpCreateDirectoryModal.component' /** @hidden */ @@ -22,6 +23,12 @@ export class CommonSFTPContextMenu extends SFTPContextMenuItemProvider { async getItems (item: SFTPFile, panel: SFTPPanelComponent): Promise { return [ + { + click: async () => { + await this.createDirectory(item, panel); + }, + label: this.translate.instant('Create Directory'), + }, { click: async () => { if ((await this.platform.showMessageBox({ @@ -49,4 +56,12 @@ export class CommonSFTPContextMenu extends SFTPContextMenuItemProvider { modal.componentInstance.sftp = session await modal.result } + + async createDirectory (item: SFTPFile, panel: SFTPPanelComponent): Promise { + const modal = this.ngbModal.open(SFTPCreateDirectoryModalComponent) + modal.componentInstance.item = item + modal.componentInstance.sftp = panel.sftp + modal.componentInstance.panel = panel + await modal.result + } }