How Tabby's SFTP Panel Implementation Works: A Deep Dive into the Source Code
Tabby's SFTP panel is implemented as a layered architecture of Angular components, SSH session wrappers, and context-menu extensions that convert russh SFTP channels into an interactive file manager UI.
The SFTP panel in the Eugeny/tabby repository transforms authenticated SSH connections into a visual file browser. This Tabby SFTP panel implementation bridges the gap between low-level SSH protocol handling and a polished Angular-based user interface. Understanding this architecture reveals how the terminal emulator manages file transfers without external tools.
Opening the SFTP Panel
The entry point for the SFTP panel follows a clear delegation chain from the UI context menu down to the SSH session layer.
Context Menu Entry
When a user selects "Open SFTP panel" from an SSH tab's context menu, the SFTPContextMenu provider in tabby-ssh/src/tabContextMenu.ts triggers the opening sequence. This provider registers the menu item and calls tab.openSFTP() on the active SSHTabComponent.
The SSHTabComponent (defined in sshTab.component.ts) handles the UI request by delegating to its underlying session:
async openSFTP (): Promise<void> {
this.sftp = await this.session.openSFTP()
…
}
Session Initialization
The actual SFTP channel creation occurs in SSHSession.openSFTP() within tabby-ssh/src/session/ssh.ts. This method validates authentication state, creates a new channel via the russh library, and wraps it in a high-level session object:
async openSFTP (): Promise<SFTPSession> {
if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
throw new Error('Cannot open SFTP session before auth')
}
if (!this.sftp) {
this.sftp = await this.ssh.activateSFTP(await this.ssh.openSessionChannel())
}
return new SFTPSession(this.sftp, this.injector)
}
This ensures the SFTP panel only opens on authenticated connections and maintains a single session instance per tab.
The SFTPSession API Wrapper
The SFTPSession class in tabby-ssh/src/session/sftp.ts abstracts the native russh.SFTP bindings into TypeScript-friendly async methods. This wrapper handles all remote filesystem operations required by the UI layer.
Key methods exposed by the wrapper include:
readdir(p)– Lists directory contents returningSFTPFileobjects with metadatastat(p)– Retrieves file attributes and permissionsreadlink(p)– Resolves symbolic links to their targetsopen(p, mode)/download(p, transfer)– Manages file streaming for transfersmkdir(p),rmdir(p),rename(old, new)– Handles directory and file mutations
The SFTPFile interface (lines 8-16) defines the metadata structure passed throughout the application, including name, path, type flags, permissions, size, and modification dates.
The SFTPPanelComponent UI Layer
The visual interface resides in SFTPPanelComponent (tabby-ssh/src/components/sftpPanel.component.ts), which orchestrates user interactions and renders file listings.
Navigation and File Listing
During initialization, the component obtains a session instance and immediately populates the root directory:
async ngOnInit(): Promise<void> {
this.sftp = await this.session.openSFTP()
await this.navigate(this.path)
}
The navigate(newPath) method updates breadcrumb UI elements, clears active filters, and retrieves directory contents:
this.fileList = await this.sftp.readdir(this.path)
Results are automatically sorted to display directories before files using the isDirectory property flag.
File Operations and Transfers
The component handles three primary interaction types:
Directory navigation occurs when users double-click folders, triggering recursive path updates. File downloads utilize the platform-wide PlatformService to create FileDownload transfer objects, delegating byte-stream handling to SFTPSession.download().
For recursive folder downloads, downloadFolderRecursive() walks the remote directory tree, creates local directory structures via transfer.createDirectory(), and streams individual files through transfer.createFile().
The getIcon(item) method maps file extensions to FontAwesome categories (code, image, PDF) for visual identification, while getModeString(item) formats UNIX permission bits into human-readable drwxr-xr-x notation.
Context Menu Extensions
The panel supports extensible context menus through the SFTPContextMenuItemProvider interface defined in tabby-ssh/src/api/contextMenu.ts. During menu construction, the component aggregates items from all registered providers:
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(item, this)))) {
items.push({ type: 'separator' })
items = items.concat(section)
}
Default providers supply standard actions like Delete and Create directory, while platform-specific extensions (such as EditSFTPContextMenu in tabby-electron/src/sftpContextMenu.ts) add Launch WinSCP functionality on Windows systems.
Extending the SFTP Panel
Developers can customize the Tabby SFTP panel implementation through two primary extension points. The SFTPContextMenuItemProvider interface allows plugins to inject custom menu actions by implementing the getItems() method and registering the provider in the Angular dependency injection system.
UI customization requires modifying the Pug template (sftpPanel.component.pug) and associated SCSS styles (sftpPanel.component.scss), though theme authors should note these are core components rather than public API surfaces.
Summary
- Tabby's SFTP panel opens via
SFTPContextMenutriggeringSSHTabComponent.openSFTP(), which delegates toSSHSession.openSFTP()intabby-ssh/src/session/ssh.ts. - The
SFTPSessionwrapper intabby-ssh/src/session/sftp.tsexposes high-level async methods likereaddir(),download(), andrename()over the native russh bindings. - SFTPPanelComponent manages the Angular UI, handling navigation, file listing sorting, permission formatting, and recursive transfer operations.
- Context menus are extensible via the
SFTPContextMenuItemProviderinterface, allowing plugins to register custom file actions. - File transfers integrate with the platform's
PlatformServiceto handle local filesystem operations while streaming remote data through the SFTP session.
Frequently Asked Questions
How does Tabby create the SFTP connection when opening the panel?
Tabby creates the SFTP connection by calling SSHSession.openSFTP() in tabby-ssh/src/session/ssh.ts, which verifies the underlying SSH client is authenticated, then activates an SFTP channel through the russh library and wraps it in a SFTPSession object for the UI components to consume.
What file operations does the SFTPSession wrapper support?
The SFTPSession class supports standard filesystem operations including readdir() for listing directories, stat() and readlink() for metadata, mkdir() and rmdir() for directory management, rename() for moving files, and streaming methods download() and upload() for transfer operations.
Can I add custom actions to the SFTP panel's right-click menu?
Yes, you can add custom actions by implementing the SFTPContextMenuItemProvider interface from tabby-ssh/src/api/contextMenu.ts and registering your provider in the Angular module. Your implementation should return MenuItemOptions arrays from the getItems() method, which the SFTPPanelComponent will merge into the context menu alongside default actions like Delete and Create directory.
Where is the SFTP panel UI defined in the Tabby source code?
The SFTP panel UI is defined in tabby-ssh/src/components/sftpPanel.component.ts (logic), sftpPanel.component.pug (template structure), and sftpPanel.component.scss (styling). Modal dialogs for delete confirmation and directory creation reside in sftpDeleteModal.component.ts and sftpCreateDirectoryModal.component.ts respectively.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →