What Is the Tabby Electron-Browser Architecture?

Tabby’s electron-browser architecture implements a core-plugin pattern where a platform-agnostic Angular core delegates native operations to abstract services, with concrete implementations provided by tabby-electron for desktop IPC integration and tabby-web for browser compatibility.

Tabby (Eugeny/tabby) is a modern terminal emulator built with TypeScript and Angular that runs both as a desktop Electron application and as a pure web app. Its electron-browser architecture achieves this portability through strict separation between the UI logic and platform-specific APIs. The core application code remains identical across both environments, relying on Angular’s dependency injection to swap底层 implementations at runtime.

Core-Plugin Architecture: The Platform-Agnostic Foundation

The architecture centers on tabby-core, a package containing the public API and Angular UI logic that is completely platform-agnostic. It defines abstract service interfaces without importing Electron or browser-specific APIs, enabling the same components to execute in either environment.

Abstract Service Interfaces

Two critical abstractions enable platform isolation:

  • HostAppService – Defines methods for host interaction such as newWindow(), quit(), openExternal(), and selectFile()【/cache/repos/github.com/Eugeny/tabby/master/tabby-core/src/api/hostApp.ts】. The UI layer injects this interface to perform native operations without knowing the underlying platform.

  • PlatformService – Provides utilities for theme handling, clipboard access, and screen information【/cache/repos/github.com/Eugeny/tabby/master/tabby-core/src/api/platform.ts】.

Platform Plugin Structure

Concrete implementations reside in separate platform packages:

Platform Package Purpose
Desktop tabby-electron Wraps Electron main-process and renderer-process APIs
Web tabby-web Implements browser-compatible stubs using standard web APIs

Electron Platform Implementation (tabby-electron)

The desktop implementation bridges the Angular renderer to Electron’s native APIs through an IPC (Inter-Process Communication) layer.

ElectronService and Low-Level Wrappers

The ElectronService exposes raw Electron objects as injectable Angular services. Located at tabby-electron/src/services/electron.service.ts, it provides typed access to app, ipcRenderer, shell, and other Electron modules【/cache/repos/github.com/Eugeny/tabby/master/tabby-electron/src/services/electron.service.ts】.

ElectronHostAppService and IPC Integration

ElectronHostAppService implements the abstract HostAppService interface by delegating to ElectronService:

  • newWindow() sends the 'app:new-window' IPC message to the main process
  • showItemInFolder() calls electron.shell.showItemInFolder()
  • quit() invokes electron.app.quit()
  • relaunch() uses electron.app.relaunch()【/cache/repos/github.com/Eugeny/tabby/master/tabby-electron/src/services/hostApp.service.ts】

The main process (entry point app/src/main.js) creates the BrowserWindow, loads the Angular bundle, and sends bootstrap data via the 'start' IPC channel. The renderer process receives this in app/src/entry.ts via ipcRenderer.once('start', …) before bootstrapping the Angular module【/cache/repos/github.com/Eugeny/tabby/master/appsrc/entry.ts】.

Web Platform Implementation (tabby-web)

For browser deployment, tabby-web provides stub implementations that replace Electron-specific functionality with web-standard equivalents or throw NotImplemented errors where impossible.

WebHostApp Fallbacks

WebHostApp implements the same HostAppService interface using browser primitives:

  • relaunch() calls location.reload()
  • quit() closes the current window
  • newWindow() throws an error because web pages cannot spawn native windows
  • openExternal() falls back to window.open()【/cache/repos/github.com/Eugeny/tabby/master/tabby-web/src/services/hostApp.service.ts】

The WebModule registers these providers in tabby-web/src/index.ts, ensuring Angular injects browser-compatible services when building for the web【/cache/repos/github.com/Eugeny/tabby/master/tabby-web/src/index.ts】.

Runtime Integration and Bootstrap Flow

The electron-browser architecture relies on Angular’s dependency injection system to substitute implementations at runtime:

  1. Main Process Launch: Electron’s main process (app/src/main.js) initializes the native environment and creates a BrowserWindow.
  2. Renderer Bootstrap: app/src/entry.ts receives BootstrapData through the 'start' IPC message, discovers plugins via findPlugins(), and bootstraps the root Angular module.
  3. Service Resolution: Angular resolves HostAppService to ElectronHostAppService in desktop builds and WebHostApp in browser builds.
  4. Feature Isolation: Platform-specific features (native dialogs, file system access) remain confined to their respective plugins, preventing the core from importing Electron directly.

Cross-Platform Implementation Examples

The following examples demonstrate how the same UI code executes differently across platforms.

Opening External URLs

This code works identically in Electron and browsers:

import { HostAppService } from 'tabby-core';

constructor (private hostApp: HostAppService) {}

openDocs () {
    // Electron uses electron.shell.openExternal; browser uses window.open
    this.hostApp.openExternal('https://tabby.sh/docs/');
}

Native File Dialogs (Electron Only)

File selection requires native APIs unavailable in browsers:

import { HostAppService } from 'tabby-core';

async selectFile () {
    const files = await this.hostApp.selectFile({
        properties: ['openFile']
    });
    return files;
}

In Electron, this delegates to dialog.showOpenDialog via ElectronHostAppService. In the browser, the method is not implemented.

Window Management

Creating new application windows uses IPC in Electron:

import { HostAppService } from 'tabby-core';

createWindow () {
    // Electron: sends 'app:new-window' IPC; Web: throws NotImplemented
    this.hostApp.newWindow();
}

Application Relaunch

Cross-platform restart logic adapts to the environment:

import { HostAppService } from 'tabby-core';

restart () {
    // Electron: app.relaunch(); Browser: location.reload()
    this.hostApp.relaunch();
}

Summary

  • Tabby’s electron-browser architecture separates platform-agnostic UI logic (tabby-core) from native implementations via abstract services.
  • HostAppService and PlatformService define the contract that enables the same Angular code to run on desktop and web.
  • tabby-electron implements native capabilities through ElectronService and ElectronHostAppService, using IPC to communicate with the main process.
  • tabby-web provides browser-compatible stubs that use standard web APIs where possible.
  • Dependency injection automatically substitutes the correct platform implementation at bootstrap time, ensuring clean separation of concerns.

Frequently Asked Questions

How does Tabby handle native features like file dialogs in the browser version?

The web implementation throws NotImplemented for unsupported operations or provides limited fallbacks. For example, selectFile() is only available in the Electron build where ElectronHostAppService can access dialog.showOpenDialog. Browser users must use HTML file inputs directly, as the web platform cannot access the native file system without user interaction through specific web APIs.

What is the role of the main process in Tabby's Electron architecture?

The main process (app/src/main.js) manages native resources by creating BrowserWindow instances, handling system events, and exposing capabilities to the renderer via IPC. It sends initial BootstrapData to the Angular app through the 'start' channel before the UI initializes in app/src/entry.ts, effectively bridging the Electron container with the Angular application.

Can plugins access Electron APIs directly?

No. Plugins interact only with the abstract services defined in tabby-core, such as HostAppService. Direct Electron access is encapsulated in tabby-electron/src/services/electron.service.ts. This design ensures plugins remain compatible with both desktop and web builds unless they explicitly depend on platform-specific features, maintaining the integrity of the electron-browser architecture.

How does Tabby achieve code sharing between the desktop and web versions?

The Angular UI layer imports only from tabby-core, never from Electron or browser-specific modules. At build time, the Angular compiler includes either tabby-electron or tabby-web providers based on the target platform, injecting the correct implementation of HostAppService and PlatformService throughout the application via Angular’s dependency injection system.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →