GhostText Session Management
Relevant source files
The following files were used as context for generating this wiki page:GhostText Session Management is the core component of Ghostbird responsible for handling the communication between Thunderbird's email compose window and an external text editor via the GhostText protocol. It manages the entire lifecycle of a connection, from initiation and handshake to real-time text synchronization and termination. This system is designed to be robust, handling various states and ensuring data is relayed correctly between the client (Thunderbird) and the server (the text editor's GhostText plugin).
The architecture follows the Ports and Adapters pattern, with a central ghosttext-session module containing the core protocol logic, which is independent of other modules. The ghosttext-runner and ghosttext-adaptor modules orchestrate the session and connect the core logic to the Thunderbird-specific parts of the add-on. For more high-level architectural details, see the design documentation.
Sources: doc/design.md, README.md
Architecture and Core Components
The session management is orchestrated by several key classes and interfaces that collaborate to connect to a GhostText server and synchronize content. The GhostTextStarter initiates the process, which is then managed by the GhostTextRunner and the GhostTextClient.
Sources: src/ghosttext-runner/ghost_text_starter.ts, src/ghosttext-runner/ghost_text_runner.ts, src/ghosttext-session/ghost_text_client.ts
Component Interaction
The following diagram illustrates the high-level interaction between the main components involved in a GhostText session.
This diagram shows that the GhostTextStarter creates a GhostTextRunner instance, which then uses GhostTextClient for the session logic, IGhostTextConnector to establish the WebSocket connection, and IClientEditor to interact with the Thunderbird compose window.
Sources: doc/design.md, src/ghosttext-runner/ghost_text_runner.ts, src/ghosttext-runner/ghost_text_starter.ts
Key Interfaces
The system relies on a set of interfaces defined in api.ts to decouple the core logic from the Thunderbird-specific implementation.
| Interface | Description |
|---|---|
IGhostTextConnector | Handles the initial HTTP GET and establishes the WebSocket connection to the GhostText server. |
ISession | Represents an active WebSocket connection, allowing sending and receiving of updates. |
IClientEditor | An abstraction for the Thunderbird compose window, used to get its state and apply changes. |
IClientOptionsLoader | Loads configuration options like the server URL. |
IStatusIndicator | Updates the UI (e.g., the toolbar button icon) to reflect the current connection status. |
IHeart | Provides a keep-alive mechanism to prevent the background script from being suspended by the browser. |
Sources: src/ghosttext-runner/api.ts
Session Lifecycle
The GhostTextClient class implements the session logic as a state machine, managed by a generator function run(). The session transitions through several states from connection to termination.
Sources: src/ghosttext-session/ghost_text_client.ts:8-12
Session States
The status of a session is tracked by the SessionStatus type.
| Status | Description |
|---|---|
unconnected | The initial state before any connection attempt. |
connecting | A connection attempt to the GhostText server is in progress. |
running | The connection is established, and text synchronization is active. |
error | An error occurred, e.g., the connection failed or was unexpectedly closed. |
finished | The session was closed gracefully. |
Sources: src/ghosttext-session/ghost_text_client.ts:8
The following state diagram shows the possible transitions during a session.
Session Flow
The sequence diagram below details the process from the user initiating a session to the first exchange of data.
This diagram shows the initial HTTP request to get the WebSocket port, the WebSocket connection being established, and the subsequent exchange of text updates.
Sources: doc/design.md
Core Logic: GhostTextClient
The GhostTextClient class is the heart of the session management. Its run() method is a generator function that yields Command objects and expects CommandResult objects in return. This creates a state machine that is driven by an external runner (GhostTextRunner).
Sources: src/ghosttext-session/ghost_text_client.ts:16-50
Commands and Results
The GhostTextSession generator communicates via a command/result pattern.
Command Type | Description |
|---|---|
connect | Instructs the runner to establish a connection. |
queryEditor | Asks the runner for the initial state of the compose window. |
requestUpdate | Sends the current editor state to the GhostText server. |
applyChange | Applies a change from the server to the local editor. |
notifyStatus | Instructs the runner to update the UI with the new session status. |
CommandResult Type | Description |
|---|---|
connected | Returned when a connection is successfully established. |
statusUpdated | Confirms that the status has been updated. |
serverChanged | Provides a text change from the server. |
clientState | Provides the full state of the local editor. |
disconnected | Indicates the connection was lost. |
editorClosed | Indicates the local editor (compose window) was closed. |
Sources: src/ghosttext-session/ghost_text_client.ts:10-31
Example run() Logic
The run() generator function orchestrates the session by yielding commands and processing results.
// src/ghosttext-session/ghost_text_client.ts:18-35
export class GhostTextClient {
// ...
*run(): GhostTextSession {
let res = yield { type: "notifyStatus", status: "connecting" };
if (res?.type !== "statusUpdated") {
return "error";
}
res = yield { type: "connect" };
if (res?.type !== "connected") {
return "error";
}
res = yield { type: "notifyStatus", status: "running" };
if (res?.type !== "statusUpdated") {
return "error";
}
// ... main loop follows
}
}
This snippet shows the initial handshake sequence as managed by the state machine.
Sources: src/ghosttext-session/ghost_text_client.ts:18-35
Data Structures
Communication with the GhostText server adheres to a specific protocol, with data structures defined in types.ts.
Sources: src/ghosttext-session/types.ts
UpdateRequest
This message is sent from Ghostbird to the GhostText server. It contains the full state of the compose window.
| Field | Type | Description |
|---|---|---|
title | string | The document title (email subject). |
url | string | The host of the document's URL. |
syntax | "" | Not used; always an empty string. |
text | string | The value of the textarea (email body). |
selections | ReadonlyArray<SelectionObject> | The user's current cursor selections. |
Sources: src/ghosttext-session/types.ts:29-41
EditorChangeResponse
This message is received from the GhostText server when the user makes a change in the external editor.
| Field | Type | Description |
|---|---|---|
text | string (optional) | The updated content of the temporary file. |
selections | ReadonlyArray<SelectionObject> (optional) | The updated cursor selections. |
Sources: src/ghosttext-session/types.ts:47-52
Summary
The GhostText Session Management system is a well-defined, stateful module that serves as the communication backbone of the Ghostbird add-on. By leveraging a generator-based state machine (GhostTextClient) and a set of clear interfaces (api.ts), it effectively manages the complexities of the GhostText protocol. This architecture ensures a clean separation of concerns, making the core logic testable and independent of the Thunderbird-specific UI and APIs.