Mailspring

:love_letter: A beautiful, fast and fully open source mail client for Mac, Windows and Linux.

17,720 stars JavaScript Markdown Skills API Spec #electron#electron-app#email#imap
AI Prompts & Specs

Repository: Foundry376/Mailspring


Stars: 17395

CLAUDE.md

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build and Development Commands

bash

Install dependencies


npm install

Run the app in development mode (uses --dev flag, data stored in Mailspring-dev folder)


npm start

Run with specific language locale


npm start -- --lang=de

Run linting (prettier + eslint)


npm run lint

Run all tests


npm test

Run window-specific tests


npm test-window

TypeScript type checking in watch mode


npm run tsc-watch

Build for production


npm run build

Architecture Overview

Mailspring is an Electron-based email client written in TypeScript with React. It uses a plugin architecture where features are implemented as internal packages.

Key Directories

- app/src/ - Core application source code
- browser/ - Main process code (application lifecycle, window management, auto-updates)
- flux/ - Flux-based state management (actions, stores, models, tasks)
- components/ - Reusable React UI components
- services/ - Application services (search, sanitization, etc.)
- registries/ - Extension registries (components, extensions, database objects)
- global/ - Global exports (mailspring-exports, mailspring-component-kit)

- app/internal_packages/ - Built-in plugins implementing features (composer, message-list, thread-list, preferences, themes, etc.)

IMPORTANT: Application source code lives in both app/src/ and app/internal_packages/. When searching for usages of a module, symbol, or pattern, always search both directories. Searching only app/src/ will miss a large portion of the codebase and lead to incomplete changes.

- app/spec/ - Jasmine test specs

Core Modules

Global exports for plugins:
- mailspring-exports - Core APIs: Actions, Stores, Models, Tasks, Utils, database access
- mailspring-component-kit - Reusable UI components

Flux Architecture:
- Models (flux/models/) - Data models: Message, Thread, Contact, Account, Folder, Label, etc.
- Stores (flux/stores/) - Application state: DatabaseStore, DraftStore, AccountStore, etc.
- Tasks (flux/tasks/) - Async operations: SendDraftTask, ChangeFolderTask, etc.
- Actions (flux/actions.ts) - Application-wide action dispatcher

Plugin Structure

Each plugin in internal_packages/ has:
- package.json - Metadata with windowTypes specifying where plugin loads
- lib/main.ts - Entry point with activate() and deactivate() lifecycle hooks
- lib/ - Plugin source code
- styles/ - LESS stylesheets
- keymaps/ - Keyboard shortcut definitions

Core Data Flow: Sync Engine, Tasks, and Observable Database

Important: The UI is read-only with respect to the database. All database modifications happen in the C++ sync engine (Mailspring-Sync). The Electron app requests changes via Tasks, and the sync engine streams entity changes back to create a real-time UI.

Sync Engine Communication (mailsync-process.ts, mailsync-bridge.ts)

The sync engine is a separate C++ process spawned per account:

1. Electron β†’ Sync Engine: JSON messages sent via stdin (task requests, commands)
2. Sync Engine β†’ Electron: Newline-delimited JSON streamed via stdout (database change deltas)

text
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         stdin (JSON)          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Electron UI β”‚ ──────────────────────────────▢│ Mailspring-Sync β”‚
β”‚ (TypeScript) β”‚ β”‚ (C++) β”‚
β”‚ β”‚ ◀────────────────────────────── β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ stdout (JSON deltas) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The MailsyncBridge (in main window only) manages sync process lifecycle, listens to Actions.queueTask, and forwards tasks to the appropriate account's sync process.

Task System (flux/tasks/)

Tasks represent operations the user wants to perform (send email, star thread, move to folder). They are persisted models stored in the database.

Task Lifecycle:
1. UI calls Actions.queueTask(new SomeTask({...}))
2. MailsyncBridge._onQueueTask() validates and sends to sync engine via stdin
3. Sync engine executes the task (local changes + remote API calls)
4. Sync engine persists task status updates and emits deltas
5. Task completion triggers onSuccess() or onError() callbacks

Task States (flux/tasks/task.ts):
- local - Not yet executed
- remote - Local phase complete, waiting for remote
- complete - Finished successfully
- cancelled - Cancelled before completion

Key Task Classes:
- SendDraftTask, DestroyDraftTask - Email composition
- ChangeLabelsTask, ChangeFolderTask - Organization
- ChangeStarredTask, ChangeUnreadTask - Status flags
- SyncbackMetadataTask - Plugin metadata sync
- SyncbackEventTask - Calendar event sync

Undoable Tasks:

Tasks can support undo/redo by implementing canBeUndone and createUndoTask(). The UndoRedoStore automatically registers tasks with canBeUndone = true for undo.

Two patterns exist:
1. Toggle pattern (ChangeStarredTask): Undo simply flips a boolean flag
2. Snapshot pattern (SyncbackMetadataTask, SyncbackEventTask): Store original state in undoData, swap on undo

typescript
// Snapshot pattern example
const undoData = { ics: event.ics, recurrenceStart: event.recurrenceStart };
event.ics = newIcs; // Modify after capturing
Actions.queueTask(SyncbackEventTask.forUpdating({ event, undoData, description: 'Edit event' }));

See docs/undo-redo-task-pattern.md for detailed implementation guide.

Task Queue (flux/stores/task-queue.ts)

The TaskQueue store observes Task model changes from the database and provides:
- queue() - Active tasks
- completed() - Finished tasks
- waitForPerformLocal(task) - Promise that resolves when task runs locally
- waitForPerformRemote(task) - Promise that resolves when task fully completes

Observable Database Pattern

Database is read-only in Electron (flux/stores/database-store.ts):
- DatabaseStore.inTransaction() throws - writes are not allowed
- Uses SQLite in WAL mode via better-sqlite3 for concurrent reads
- The sync engine exclusively handles writes

Change Records (flux/stores/database-change-record.ts):

When the sync engine modifies data, it emits JSON deltas that become DatabaseChangeRecord objects:

typescript
{
type: 'persist' | 'unpersist',
objectClass: 'Thread' | 'Message' | ...,
objects: Model[],
objectsRawJSON: object[]
}

Reactive Queries (flux/models/query-subscription.ts):

QuerySubscription provides live-updating query results:

typescript
// Subscribe to all unread threads
const subscription = new QuerySubscription(
DatabaseStore.findAll(Thread).where({ unread: true })
);
subscription.addCallback((threads) => this.setState({ threads }));

// Subscription automatically updates when DatabaseStore triggers

Observable Integration (Rx.Observable.fromQuery):

Wrap queries as RxJS observables for reactive UI updates:

typescript
Rx.Observable.fromQuery(DatabaseStore.findAll(Thread))
.subscribe(threads => this.updateUI(threads));

ObservableListDataSource (flux/stores/observable-list-data-source.ts):

Adapts QuerySubscription for virtualized list components (MultiselectList), supporting:
- Windowed/paginated data loading
- Selection state management
- Automatic updates from database changes

Data Flow Summary

text
User Action β†’ Actions.queueTask() β†’ MailsyncBridge β†’ stdin β†’ Sync Engine
β”‚
β–Ό
UI Updates ← QuerySubscription ← DatabaseStore.trigger() ← stdout deltas

Development Notes

- Hot reload is available via CTRL+R (Windows/Linux) or CMD+R (macOS)
- Dev tools accessible via Menu > Developer > Toggle Developer Tools
- In dev tools console, $m provides access to mailspring-exports for debugging
- Dev mode data is stored separately (e.g., ~/.config/Mailspring-dev/ on Linux)

Claude Hooks

after_edit

Run linting after modifying TypeScript or JavaScript files.

json
{
"hooks": {
"after_edit": [
{
"command": "npm run lint",
"file_paths": ["/.ts", "/.tsx", "/.js", "/.jsx"]
}
]
}
}


README.md

πŸ’Œ Mailspring

Mailspring is a new version of Nylas Mail maintained by one of the original authors. It's faster, leaner, and shipping today! It replaces the JavaScript sync code in Nylas Mail with a new C++ sync engine based on Mailcore2. It uses roughly half the RAM and CPU of Nylas Mail and idles with almost zero "CPU Wakes", which translates to great battery life. It also has an entirely revamped composer and other great new features.

Mailspring's UI is open source (GPLv3) and written in TypeScript with Electron and React - it's built on a plugin architecture and was designed to be easy to extend. Check out CONTRIBUTING.md to get started!

Mailspring's sync engine is spawned by the Electron application and runs locally on your computer. It is open source (GPLv3) and written in C++ and C. For convenience, however, when you set up your development environment, Mailspring uses the latest version of the sync engine we've shipped for your platform so you don't need to pull sources or install its compile-time dependencies.

!Mailspring Screenshot

Features

Mailspring comes packed with powerful features like Unified Inbox, Snooze, Send
Later, Mail Rules, Templates and more. Mailspring Pro, which you can unlock
with a monthly subscription, adds even more features for people who send a ton
of email: link tracking, read receipts, mailbox analytics, contact and company
profiles. All of these features run in the client - Mailspring does not send
your email credentials to the cloud. For a full list of features, check out
getmailspring.com.

Download Mailspring

You can download compiled versions of Mailspring for Windows, Mac OS X, and
Linux (deb, rpm and snap) from
https://getmailspring.com/download.

Getting Help

You can find community-based help and discussion with other Mailspring users on our
Discourse community.

Contributing

Mailspring is entirely open-source. Pull requests and contributions are
welcome! There are three ways to contribute: building a plugin, building a
theme, and submitting pull requests to the project itself. When you're getting
started, you may want to join our
Discourse so you can ask questions and
learn from other people doing development.

![Contributor Covenant](CODE_OF_CONDUCT.md)

Running Mailspring from Source

To install all dependencies and run Mailspring from its source code,
run the following commands from the root directory of the Mailspring repository:

text
export npm_config_arch=x64 # If you are on an M1 / Apple Silicon Mac
npm install
npm start

You can attach command line parameters by separating them using a double hyphen:

text
npm start -- --help

Building Mailspring

To build Mailspring, you need to run the following command from the root directory
of the Mailspring repository:

text
npm run-script build

Building A Plugin

Plugins lie at the heart of Mailspring and give it its powerful features.
Building your own plugins allows you to integrate the app with other tools,
experiment with new workflows, and more. Follow the Getting Started
guide
to write your first plugin in
five minutes.

- To create your own theme, check out the
Mailspring-Theme-Starter.

- To create your own plugin, check out the
Mailspring-Plugin-Starter.

A plugin "store" like the Chrome Web Store is coming soon, and will make it
easy for other users to discover plugins you create. (Right now, users need to
"sideload" the plugins into the app by downloading them and copying them into
place.)

You can share and browse Mailspring Plugins, and discuss plugin development
with other developers, on our
Discourse.

Building a Theme

The Mailspring user interface is styled using CSS, which means it's easy to
modify and extend. Mailspring comes stock with a few beautiful themes, and
there are many more which have been built by community developers. To start
creating a theme, clone the theme starter!

If you are updating an existing Nylas theme for Mailspring here is a
step by step tutorial.
Notice: as part of the update process you will probably need to import mailspring base variables.

You can share and browse Mailspring Themes, and discuss theme development with other developers, on our Discourse.

Localizing / Translating

Mailspring (1.5.0 and above) supports localization. If you're a fluent speaker of
another language, we'd love your help improving translations. Check out the
LOCALIZATION
guide for more information. You can discuss localization and translation with
other developers on our Discourse.

Contributing to Mailspring Core

Pull requests are always welcome - check out
CONTRIBUTING
for more information about setting up the development environment, running
tests locally, and submitting pull requests.