Home
Relevant source files
The following files were used as context for generating this wiki page:
Ghostbird is a Thunderbird add-on that enables users to compose emails in their favorite external text editor. It implements the GhostText protocol, acting as a client that connects to a GhostText server running within a text editor (like Vim, VS Code, Emacs, etc.). The core function is to relay text between the Thunderbird compose window and the editor via a WebSocket connection.
This project is designed to be a modern replacement for the legacy "External Editor" add-on, leveraging current Thunderbird WebExtension APIs. It focuses on live text synchronization and is architected for maintainability and testability to adapt to future Thunderbird API changes.
Sources: README.md:10-14, doc/faq.md:5-9, 58-62
Core Architecture
The Ghostbird add-on's architecture is designed to be modular and testable, following principles like Ports and Adapters and the Dependency Inversion Principle. This isolates Thunderbird-specific code from the core application logic.
Sources: doc/design.md:73-77
High-Level Flow
When a user initiates a Ghostbird session, the add-on establishes a WebSocket connection to the GhostText server (defaulting to port 4001). Text is then synchronized between the Thunderbird compose window and the external editor.
The following diagram illustrates the basic interaction flow:
This diagram shows the high-level data flow between the Thunderbird compose window, the Ghostbird add-on, and the external text editor's GhostText server.
Sources: README.md:112-126, CONTRIBUTING.md:13-15
Component Interaction
The add-on consists of several key scripts and components that interact to manage the editing session. The main parts are the background script (background.js), the compose script (compose.js), and the options page (options.js).
The sequence of events for starting a session is as follows:
- User clicks the Ghostbird button in the compose window.
- The
background.jsscript is activated. background.jsattempts to connect to the GhostText server via WebSocket.- It injects
compose.jsinto the compose window and establishes a communicationPort. compose.jssends the initial email text tobackground.js.background.jsrelays this text to the GhostText server.- Edits from the text editor are sent back through the WebSocket to
background.js, which relays them tocompose.jsto update the compose window.
This diagram details the message sequence between the GhostText server and the add-on's background and compose scripts.
Sources: doc/design.md:28-70
Code Structure
The source code is organized into directories based on functionality, adhering to a Ports and Adapters architecture. This separates the core logic from the parts that interact directly with the Thunderbird API.
| Directory | Description |
|---|---|
src/root/ | Entry points for the add-on scripts (background.ts, compose.ts, options.ts). |
src/ghosttext-session/ | Core module implementing the GhostText protocol. Has no other project dependencies. |
src/ghosttext-runner/ | Facilitator that uses ghosttext-session and executes its decisions. |
src/ghosttext-adaptor/ | Helpers for ghosttext-runner. |
src/app-* | Infrastructure for background, compose, and options scripts, handling Thunderbird events. |
src/thunderbird/ | Wrappers for the Thunderbird MailExtension API. |
src/util/ | General utility modules. |
Sources: doc/design.md:110-132
The dependencies between these modules are strictly managed to maintain separation of concerns.
This diagram shows the dependency flow, where core logic is independent of the application and platform layers.
Sources: doc/design.md:137-143
Dependency Management
The project uses a custom dependency injection (DI) mechanism to instantiate and wire classes together at startup. This is managed in the src/root/startup/ directory. Classes that should be managed by this system are marked with a static isSingleton property.
- If
isSingletonistrue, a single shared instance is created. - If
isSingletonisfalse, a new instance is created for each dependency.
This approach is intended to make the code more testable and easier to maintain, especially when adapting to breaking changes in the Thunderbird API. The dependency tree can be visualized by running yarn test, which generates a build/test/dependency_tree.md file.
Sources: doc/design.md:192-218, doc/faq-architectural.md:5-7, 18-22, src/test/util/dependency_reporter.ts:10-17
Development
Contribution and local development are managed using Node.js and Yarn.
Sources: CONTRIBUTING.md:18-24
Setup and Workflow
- Prerequisites: Git, Thunderbird, and Node.js (LTS).
- Installation: Clone the repository and run
yarnto install dependencies. - Build: Run
yarn buildto compile TypeScript and package the extension. - Run: Use
yarn startto launch Thunderbird with the add-on temporarily loaded for debugging. - Testing: Run
yarn testfor unit tests andyarn checkfor linting and type-checking.
Sources: CONTRIBUTING.md:33-52, BUILDING.md:4-6
Key Scripts
The package.json file defines several scripts for common development tasks.
| Script | Description |
|---|---|
yarn build | Runs checks, tests, and builds the final .xpi file. |
yarn watch | Watches for source file changes and rebuilds JavaScript automatically. |
yarn start | Starts Thunderbird with the extension loaded for debugging. |
yarn test | Runs the Vitest unit test suite. |
yarn check | Runs the Biome linter and the TypeScript compiler for type checking. |
yarn fix | Automatically fixes linting issues found by Biome. |
Sources: package.json:22-48, CONTRIBUTING.md:42-65
Build Process
The build process is orchestrated by tsdown, a bundler based on Rolldown. The configuration is defined in tools/tsdown_config.ts.
The process involves several steps:
- Generating
index.tsbarrel files usingbarrelsby. - Generating
manifest.jsonfrommanifest_template.jsonand version info. - Generating i18n
messages.jsonfiles fromlocales.toml. - Compiling TypeScript source files into JavaScript bundles in
dist/ext/. - Copying static assets (HTML, CSS, images) from
ext/todist/ext/. - Finally,
web-extpackages thedist/ext/directory into a.xpifile.
This diagram outlines the build pipeline, from source files to the final XPI package.
Sources: doc/building.md:10-25, tools/tsdown_config.ts:1-20
Testing Strategy
The project uses Vitest for unit testing.
- Tests are located in
src/test/*.tsand as in-source tests within component files. - The testing policy focuses on the core logic modules (
ghosttext-sessionandghosttext-runner) to ensure the protocol implementation is robust. - Manual testing is required for UI interactions and end-to-end functionality.
- Test coverage is not required to be 100%, but enough to prevent regressions.
Sources: doc/testing.md:5-17
Configuration
The add-on's behavior and capabilities are defined in its manifest file and build configuration.
Extension Manifest
The manifest_template.json file is the template for the add-on's manifest. It defines permissions, UI components, and commands.
| Key | Description |
|---|---|
permissions | Requests access to Thunderbird APIs like compose, storage, notifications, and scripting. |
host_permissions | Allows connection to http://localhost/ and ws://localhost/ for the GhostText server. |
compose_action | Defines the Ghostbird button in the compose window's toolbar. |
commands | Defines keyboard shortcuts, such as Ctrl+Shift+H to start a session. |
background | Specifies the background script (js/background.js) that runs persistently. |
options_ui | Specifies the HTML page for the add-on's options. |
Sources: manifest_template.json:1-49
Dependencies
Ghostbird relies on several key development and runtime libraries defined in package.json.
- Build & Tooling:
tsdown,rolldown,typescript,biome,vitest,web-ext. - Runtime:
webext-options-syncfor managing user settings.
Sources: package.json:50-89