# 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) ``` ┌─────────────────┐ 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 ``` 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](https://github.com/MailCore/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](https://github.com/atom/electron) and [React](https://facebook.github.io/react/) - it's built on a plugin architecture and was designed to be easy to extend. Check out [CONTRIBUTING.md](https://github.com/Foundry376/Mailspring/blob/master/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.](https://github.com/Foundry376/Mailspring-Sync) 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](https://github.com/Foundry376/Mailspring/raw/master/screenshots/hero_graphic_mac%402x.png) ## 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](https://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](https://getmailspring.com/download). ## Getting Help You can find community-based help and discussion with other Mailspring users on our [Discourse community](https://community.getmailspring.com/). ## 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](https://community.getmailspring.com/) so you can ask questions and learn from other people doing development. [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](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: ``` 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: ``` npm start -- --help ``` ### Building Mailspring To build Mailspring, you need to run the following command from the root directory of the Mailspring repository: ``` 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](https://Foundry376.github.io/Mailspring/) to write your first plugin in five minutes. - To create your own theme, check out the [Mailspring-Theme-Starter](https://github.com/Foundry376/Mailspring-Theme-Starter). - To create your own plugin, check out the [Mailspring-Plugin-Starter](https://github.com/Foundry376/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](https://community.getmailspring.com/). ### 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](https://github.com/Foundry376/Mailspring-Theme-Starter)! If you are updating an existing Nylas theme for Mailspring here is a [step by step tutorial](https://community.getmailspring.com/t/updating-an-n1-nylas-mail-theme-for-mailspring/195). Notice: as part of the update process you will probably need to [import mailspring base variables](https://github.com/Foundry376/Mailspring/issues/326#issuecomment-343757775). You can share and browse Mailspring Themes, and discuss theme development with other developers, on our [Discourse](https://community.getmailspring.com/). ### 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](https://github.com/Foundry376/Mailspring/blob/master/LOCALIZATION.md) guide for more information. You can discuss localization and translation with other developers on our [Discourse](https://community.getmailspring.com/). ### Contributing to Mailspring Core Pull requests are always welcome - check out [CONTRIBUTING](https://github.com/Foundry376/Mailspring/blob/master/CONTRIBUTING.md) for more information about setting up the development environment, running tests locally, and submitting pull requests.