{"owner":"Foundry376","repo":"Mailspring","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build and Development Commands\n\n```bash\n# Install dependencies\nnpm install\n\n# Run the app in development mode (uses --dev flag, data stored in Mailspring-dev folder)\nnpm start\n\n# Run with specific language locale\nnpm start -- --lang=de\n\n# Run linting (prettier + eslint)\nnpm run lint\n\n# Run all tests\nnpm test\n\n# Run window-specific tests\nnpm test-window\n\n# TypeScript type checking in watch mode\nnpm run tsc-watch\n\n# Build for production\nnpm run build\n```\n\n## Architecture Overview\n\nMailspring is an Electron-based email client written in TypeScript with React. It uses a plugin architecture where features are implemented as internal packages.\n\n### Key Directories\n\n- **`app/src/`** - Core application source code\n  - `browser/` - Main process code (application lifecycle, window management, auto-updates)\n  - `flux/` - Flux-based state management (actions, stores, models, tasks)\n  - `components/` - Reusable React UI components\n  - `services/` - Application services (search, sanitization, etc.)\n  - `registries/` - Extension registries (components, extensions, database objects)\n  - `global/` - Global exports (`mailspring-exports`, `mailspring-component-kit`)\n\n- **`app/internal_packages/`** - Built-in plugins implementing features (composer, message-list, thread-list, preferences, themes, etc.)\n\n> **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.\n\n- **`app/spec/`** - Jasmine test specs\n\n### Core Modules\n\n**Global exports for plugins:**\n- `mailspring-exports` - Core APIs: Actions, Stores, Models, Tasks, Utils, database access\n- `mailspring-component-kit` - Reusable UI components\n\n**Flux Architecture:**\n- **Models** (`flux/models/`) - Data models: Message, Thread, Contact, Account, Folder, Label, etc.\n- **Stores** (`flux/stores/`) - Application state: DatabaseStore, DraftStore, AccountStore, etc.\n- **Tasks** (`flux/tasks/`) - Async operations: SendDraftTask, ChangeFolderTask, etc.\n- **Actions** (`flux/actions.ts`) - Application-wide action dispatcher\n\n### Plugin Structure\n\nEach plugin in `internal_packages/` has:\n- `package.json` - Metadata with `windowTypes` specifying where plugin loads\n- `lib/main.ts` - Entry point with `activate()` and `deactivate()` lifecycle hooks\n- `lib/` - Plugin source code\n- `styles/` - LESS stylesheets\n- `keymaps/` - Keyboard shortcut definitions\n\n## Core Data Flow: Sync Engine, Tasks, and Observable Database\n\n**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.\n\n### Sync Engine Communication (`mailsync-process.ts`, `mailsync-bridge.ts`)\n\nThe sync engine is a separate C++ process spawned per account:\n\n1. **Electron → Sync Engine**: JSON messages sent via stdin (task requests, commands)\n2. **Sync Engine → Electron**: Newline-delimited JSON streamed via stdout (database change deltas)\n\n```\n┌─────────────────┐         stdin (JSON)          ┌──────────────────┐\n│   Electron UI   │ ──────────────────────────────▶│  Mailspring-Sync │\n│  (TypeScript)   │                                │      (C++)       │\n│                 │ ◀────────────────────────────── │                  │\n└─────────────────┘    stdout (JSON deltas)        └──────────────────┘\n```\n\nThe `MailsyncBridge` (in main window only) manages sync process lifecycle, listens to `Actions.queueTask`, and forwards tasks to the appropriate account's sync process.\n\n### Task System (`flux/tasks/`)\n\nTasks represent operations the user wants to perform (send email, star thread, move to folder). They are **persisted models** stored in the database.\n\n**Task Lifecycle:**\n1. UI calls `Actions.queueTask(new SomeTask({...}))`\n2. `MailsyncBridge._onQueueTask()` validates and sends to sync engine via stdin\n3. Sync engine executes the task (local changes + remote API calls)\n4. Sync engine persists task status updates and emits deltas\n5. Task completion triggers `onSuccess()` or `onError()` callbacks\n\n**Task States** (`flux/tasks/task.ts`):\n- `local` - Not yet executed\n- `remote` - Local phase complete, waiting for remote\n- `complete` - Finished successfully\n- `cancelled` - Cancelled before completion\n\n**Key Task Classes:**\n- `SendDraftTask`, `DestroyDraftTask` - Email composition\n- `ChangeLabelsTask`, `ChangeFolderTask` - Organization\n- `ChangeStarredTask`, `ChangeUnreadTask` - Status flags\n- `SyncbackMetadataTask` - Plugin metadata sync\n- `SyncbackEventTask` - Calendar event sync\n\n**Undoable Tasks:**\n\nTasks can support undo/redo by implementing `canBeUndone` and `createUndoTask()`. The `UndoRedoStore` automatically registers tasks with `canBeUndone = true` for undo.\n\nTwo patterns exist:\n1. **Toggle pattern** (`ChangeStarredTask`): Undo simply flips a boolean flag\n2. **Snapshot pattern** (`SyncbackMetadataTask`, `SyncbackEventTask`): Store original state in `undoData`, swap on undo\n\n```typescript\n// Snapshot pattern example\nconst undoData = { ics: event.ics, recurrenceStart: event.recurrenceStart };\nevent.ics = newIcs;  // Modify after capturing\nActions.queueTask(SyncbackEventTask.forUpdating({ event, undoData, description: 'Edit event' }));\n```\n\nSee `docs/undo-redo-task-pattern.md` for detailed implementation guide.\n\n### Task Queue (`flux/stores/task-queue.ts`)\n\nThe TaskQueue store observes Task model changes from the database and provides:\n- `queue()` - Active tasks\n- `completed()` - Finished tasks\n- `waitForPerformLocal(task)` - Promise that resolves when task runs locally\n- `waitForPerformRemote(task)` - Promise that resolves when task fully completes\n\n### Observable Database Pattern\n\n**Database is read-only in Electron** (`flux/stores/database-store.ts`):\n- `DatabaseStore.inTransaction()` throws - writes are not allowed\n- Uses SQLite in WAL mode via better-sqlite3 for concurrent reads\n- The sync engine exclusively handles writes\n\n**Change Records** (`flux/stores/database-change-record.ts`):\n\nWhen the sync engine modifies data, it emits JSON deltas that become `DatabaseChangeRecord` objects:\n```typescript\n{\n  type: 'persist' | 'unpersist',\n  objectClass: 'Thread' | 'Message' | ...,\n  objects: Model[],\n  objectsRawJSON: object[]\n}\n```\n\n**Reactive Queries** (`flux/models/query-subscription.ts`):\n\n`QuerySubscription` provides live-updating query results:\n```typescript\n// Subscribe to all unread threads\nconst subscription = new QuerySubscription(\n  DatabaseStore.findAll(Thread).where({ unread: true })\n);\nsubscription.addCallback((threads) => this.setState({ threads }));\n\n// Subscription automatically updates when DatabaseStore triggers\n```\n\n**Observable Integration** (`Rx.Observable.fromQuery`):\n\nWrap queries as RxJS observables for reactive UI updates:\n```typescript\nRx.Observable.fromQuery(DatabaseStore.findAll(Thread))\n  .subscribe(threads => this.updateUI(threads));\n```\n\n**ObservableListDataSource** (`flux/stores/observable-list-data-source.ts`):\n\nAdapts QuerySubscription for virtualized list components (MultiselectList), supporting:\n- Windowed/paginated data loading\n- Selection state management\n- Automatic updates from database changes\n\n### Data Flow Summary\n\n```\nUser Action → Actions.queueTask() → MailsyncBridge → stdin → Sync Engine\n                                                              │\n                                                              ▼\nUI Updates ← QuerySubscription ← DatabaseStore.trigger() ← stdout deltas\n```\n\n## Development Notes\n\n- Hot reload is available via `CTRL+R` (Windows/Linux) or `CMD+R` (macOS)\n- Dev tools accessible via Menu > Developer > Toggle Developer Tools\n- In dev tools console, `$m` provides access to `mailspring-exports` for debugging\n- Dev mode data is stored separately (e.g., `~/.config/Mailspring-dev/` on Linux)\n\n## Claude Hooks\n\n### after_edit\n\nRun linting after modifying TypeScript or JavaScript files.\n\n```json\n{\n  \"hooks\": {\n    \"after_edit\": [\n      {\n        \"command\": \"npm run lint\",\n        \"file_paths\": [\"**/*.ts\", \"**/*.tsx\", \"**/*.js\", \"**/*.jsx\"]\n      }\n    ]\n  }\n}\n```\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build and Development Commands\n\n```bash\n# Install dependencies\nnpm install\n\n# Run the app in development mode (uses --dev flag, data stored in Mailspring-dev folder)\nnpm start\n\n# Run with specific language locale\nnpm start -- --lang=de\n\n# Run linting (prettier + eslint)\nnpm run lint\n\n# Run all tests\nnpm test\n\n# Run window-specific tests\nnpm test-window\n\n# TypeScript type checking in watch mode\nnpm run tsc-watch\n\n# Build for production\nnpm run build\n```\n\n## Architecture Overview\n\nMailspring is an Electron-based email client written in TypeScript with React. It uses a plugin architecture where features are implemented as internal packages.\n\n### Key Directories\n\n- **`app/src/`** - Core application source code\n  - `browser/` - Main process code (application lifecycle, window management, auto-updates)\n  - `flux/` - Flux-based state management (actions, stores, models, tasks)\n  - `components/` - Reusable React UI components\n  - `services/` - Application services (search, sanitization, etc.)\n  - `registries/` - Extension registries (components, extensions, database objects)\n  - `global/` - Global exports (`mailspring-exports`, `mailspring-component-kit`)\n\n- **`app/internal_packages/`** - Built-in plugins implementing features (composer, message-list, thread-list, preferences, themes, etc.)\n\n> **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.\n\n- **`app/spec/`** - Jasmine test specs\n\n### Core Modules\n\n**Global exports for plugins:**\n- `mailspring-exports` - Core APIs: Actions, Stores, Models, Tasks, Utils, database access\n- `mailspring-component-kit` - Reusable UI components\n\n**Flux Architecture:**\n- **Models** (`flux/models/`) - Data models: Message, Thread, Contact, Account, Folder, Label, etc.\n- **Stores** (`flux/stores/`) - Application state: DatabaseStore, DraftStore, AccountStore, etc.\n- **Tasks** (`flux/tasks/`) - Async operations: SendDraftTask, ChangeFolderTask, etc.\n- **Actions** (`flux/actions.ts`) - Application-wide action dispatcher\n\n### Plugin Structure\n\nEach plugin in `internal_packages/` has:\n- `package.json` - Metadata with `windowTypes` specifying where plugin loads\n- `lib/main.ts` - Entry point with `activate()` and `deactivate()` lifecycle hooks\n- `lib/` - Plugin source code\n- `styles/` - LESS stylesheets\n- `keymaps/` - Keyboard shortcut definitions\n\n## Core Data Flow: Sync Engine, Tasks, and Observable Database\n\n**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.\n\n### Sync Engine Communication (`mailsync-process.ts`, `mailsync-bridge.ts`)\n\nThe sync engine is a separate C++ process spawned per account:\n\n1. **Electron → Sync Engine**: JSON messages sent via stdin (task requests, commands)\n2. **Sync Engine → Electron**: Newline-delimited JSON streamed via stdout (database change deltas)\n\n```\n┌─────────────────┐         stdin (JSON)          ┌──────────────────┐\n│   Electron UI   │ ──────────────────────────────▶│  Mailspring-Sync │\n│  (TypeScript)   │                                │      (C++)       │\n│                 │ ◀────────────────────────────── │                  │\n└─────────────────┘    stdout (JSON deltas)        └──────────────────┘\n```\n\nThe `MailsyncBridge` (in main window only) manages sync process lifecycle, listens to `Actions.queueTask`, and forwards tasks to the appropriate account's sync process.\n\n### Task System (`flux/tasks/`)\n\nTasks represent operations the user wants to perform (send email, star thread, move to folder). They are **persisted models** stored in the database.\n\n**Task Lifecycle:**\n1. UI calls `Actions.queueTask(new SomeTask({...}))`\n2. `MailsyncBridge._onQueueTask()` validates and sends to sync engine via stdin\n3. Sync engine executes the task (local changes + remote API calls)\n4. Sync engine persists task status updates and emits deltas\n5. Task completion triggers `onSuccess()` or `onError()` callbacks\n\n**Task States** (`flux/tasks/task.ts`):\n- `local` - Not yet executed\n- `remote` - Local phase complete, waiting for remote\n- `complete` - Finished successfully\n- `cancelled` - Cancelled before completion\n\n**Key Task Classes:**\n- `SendDraftTask`, `DestroyDraftTask` - Email composition\n- `ChangeLabelsTask`, `ChangeFolderTask` - Organization\n- `ChangeStarredTask`, `ChangeUnreadTask` - Status flags\n- `SyncbackMetadataTask` - Plugin metadata sync\n- `SyncbackEventTask` - Calendar event sync\n\n**Undoable Tasks:**\n\nTasks can support undo/redo by implementing `canBeUndone` and `createUndoTask()`. The `UndoRedoStore` automatically registers tasks with `canBeUndone = true` for undo.\n\nTwo patterns exist:\n1. **Toggle pattern** (`ChangeStarredTask`): Undo simply flips a boolean flag\n2. **Snapshot pattern** (`SyncbackMetadataTask`, `SyncbackEventTask`): Store original state in `undoData`, swap on undo\n\n```typescript\n// Snapshot pattern example\nconst undoData = { ics: event.ics, recurrenceStart: event.recurrenceStart };\nevent.ics = newIcs;  // Modify after capturing\nActions.queueTask(SyncbackEventTask.forUpdating({ event, undoData, description: 'Edit event' }));\n```\n\nSee `docs/undo-redo-task-pattern.md` for detailed implementation guide.\n\n### Task Queue (`flux/stores/task-queue.ts`)\n\nThe TaskQueue store observes Task model changes from the database and provides:\n- `queue()` - Active tasks\n- `completed()` - Finished tasks\n- `waitForPerformLocal(task)` - Promise that resolves when task runs locally\n- `waitForPerformRemote(task)` - Promise that resolves when task fully completes\n\n### Observable Database Pattern\n\n**Database is read-only in Electron** (`flux/stores/database-store.ts`):\n- `DatabaseStore.inTransaction()` throws - writes are not allowed\n- Uses SQLite in WAL mode via better-sqlite3 for concurrent reads\n- The sync engine exclusively handles writes\n\n**Change Records** (`flux/stores/database-change-record.ts`):\n\nWhen the sync engine modifies data, it emits JSON deltas that become `DatabaseChangeRecord` objects:\n```typescript\n{\n  type: 'persist' | 'unpersist',\n  objectClass: 'Thread' | 'Message' | ...,\n  objects: Model[],\n  objectsRawJSON: object[]\n}\n```\n\n**Reactive Queries** (`flux/models/query-subscription.ts`):\n\n`QuerySubscription` provides live-updating query results:\n```typescript\n// Subscribe to all unread threads\nconst subscription = new QuerySubscription(\n  DatabaseStore.findAll(Thread).where({ unread: true })\n);\nsubscription.addCallback((threads) => this.setState({ threads }));\n\n// Subscription automatically updates when DatabaseStore triggers\n```\n\n**Observable Integration** (`Rx.Observable.fromQuery`):\n\nWrap queries as RxJS observables for reactive UI updates:\n```typescript\nRx.Observable.fromQuery(DatabaseStore.findAll(Thread))\n  .subscribe(threads => this.updateUI(threads));\n```\n\n**ObservableListDataSource** (`flux/stores/observable-list-data-source.ts`):\n\nAdapts QuerySubscription for virtualized list components (MultiselectList), supporting:\n- Windowed/paginated data loading\n- Selection state management\n- Automatic updates from database changes\n\n### Data Flow Summary\n\n```\nUser Action → Actions.queueTask() → MailsyncBridge → stdin → Sync Engine\n                                                              │\n                                                              ▼\nUI Updates ← QuerySubscription ← DatabaseStore.trigger() ← stdout deltas\n```\n\n## Development Notes\n\n- Hot reload is available via `CTRL+R` (Windows/Linux) or `CMD+R` (macOS)\n- Dev tools accessible via Menu > Developer > Toggle Developer Tools\n- In dev tools console, `$m` provides access to `mailspring-exports` for debugging\n- Dev mode data is stored separately (e.g., `~/.config/Mailspring-dev/` on Linux)\n\n## Claude Hooks\n\n### after_edit\n\nRun linting after modifying TypeScript or JavaScript files.\n\n```json\n{\n  \"hooks\": {\n    \"after_edit\": [\n      {\n        \"command\": \"npm run lint\",\n        \"file_paths\": [\"**/*.ts\", \"**/*.tsx\", \"**/*.js\", \"**/*.jsx\"]\n      }\n    ]\n  }\n}\n```\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build and Development Commands\n\n```bash\n# Install dependencies\nnpm install\n\n# Run the app in development mode (uses --dev flag, data stored in Mailspring-dev folder)\nnpm start\n\n# Run with specific language locale\nnpm start -- --lang=de\n\n# Run linting (prettier + eslint)\nnpm run lint\n\n# Run all tests\nnpm test\n\n# Run window-specific tests\nnpm test-window\n\n# TypeScript type checking in watch mode\nnpm run tsc-watch\n\n# Build for production\nnpm run build\n```\n\n## Architecture Overview\n\nMailspring is an Electron-based email client written in TypeScript with React. It uses a plugin architecture where features are implemented as internal packages.\n\n### Key Directories\n\n- **`app/src/`** - Core application source code\n  - `browser/` - Main process code (application lifecycle, window management, auto-updates)\n  - `flux/` - Flux-based state management (actions, stores, models, tasks)\n  - `components/` - Reusable React UI components\n  - `services/` - Application services (search, sanitization, etc.)\n  - `registries/` - Extension registries (components, extensions, database objects)\n  - `global/` - Global exports (`mailspring-exports`, `mailspring-component-kit`)\n\n- **`app/internal_packages/`** - Built-in plugins implementing features (composer, message-list, thread-list, preferences, themes, etc.)\n\n> **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.\n\n- **`app/spec/`** - Jasmine test specs\n\n### Core Modules\n\n**Global exports for plugins:**\n- `mailspring-exports` - Core APIs: Actions, Stores, Models, Tasks, Utils, database access\n- `mailspring-component-kit` - Reusable UI components\n\n**Flux Architecture:**\n- **Models** (`flux/models/`) - Data models: Message, Thread, Contact, Account, Folder, Label, etc.\n- **Stores** (`flux/stores/`) - Application state: DatabaseStore, DraftStore, AccountStore, etc.\n- **Tasks** (`flux/tasks/`) - Async operations: SendDraftTask, ChangeFolderTask, etc.\n- **Actions** (`flux/actions.ts`) - Application-wide action dispatcher\n\n### Plugin Structure\n\nEach plugin in `internal_packages/` has:\n- `package.json` - Metadata with `windowTypes` specifying where plugin loads\n- `lib/main.ts` - Entry point with `activate()` and `deactivate()` lifecycle hooks\n- `lib/` - Plugin source code\n- `styles/` - LESS stylesheets\n- `keymaps/` - Keyboard shortcut definitions\n\n## Core Data Flow: Sync Engine, Tasks, and Observable Database\n\n**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.\n\n### Sync Engine Communication (`mailsync-process.ts`, `mailsync-bridge.ts`)\n\nThe sync engine is a separate C++ process spawned per account:\n\n1. **Electron → Sync Engine**: JSON messages sent via stdin (task requests, commands)\n2. **Sync Engine → Electron**: Newline-delimited JSON streamed via stdout (database change deltas)\n\n```\n┌─────────────────┐         stdin (JSON)          ┌──────────────────┐\n│   Electron UI   │ ──────────────────────────────▶│  Mailspring-Sync │\n│  (TypeScript)   │                                │      (C++)       │\n│                 │ ◀────────────────────────────── │                  │\n└─────────────────┘    stdout (JSON deltas)        └──────────────────┘\n```\n\nThe `MailsyncBridge` (in main window only) manages sync process lifecycle, listens to `Actions.queueTask`, and forwards tasks to the appropriate account's sync process.\n\n### Task System (`flux/tasks/`)\n\nTasks represent operations the user wants to perform (send email, star thread, move to folder). They are **persisted models** stored in the database.\n\n**Task Lifecycle:**\n1. UI calls `Actions.queueTask(new SomeTask({...}))`\n2. `MailsyncBridge._onQueueTask()` validates and sends to sync engine via stdin\n3. Sync engine executes the task (local changes + remote API calls)\n4. Sync engine persists task status updates and emits deltas\n5. Task completion triggers `onSuccess()` or `onError()` callbacks\n\n**Task States** (`flux/tasks/task.ts`):\n- `local` - Not yet executed\n- `remote` - Local phase complete, waiting for remote\n- `complete` - Finished successfully\n- `cancelled` - Cancelled before completion\n\n**Key Task Classes:**\n- `SendDraftTask`, `DestroyDraftTask` - Email composition\n- `ChangeLabelsTask`, `ChangeFolderTask` - Organization\n- `ChangeStarredTask`, `ChangeUnreadTask` - Status flags\n- `SyncbackMetadataTask` - Plugin metadata sync\n- `SyncbackEventTask` - Calendar event sync\n\n**Undoable Tasks:**\n\nTasks can support undo/redo by implementing `canBeUndone` and `createUndoTask()`. The `UndoRedoStore` automatically registers tasks with `canBeUndone = true` for undo.\n\nTwo patterns exist:\n1. **Toggle pattern** (`ChangeStarredTask`): Undo simply flips a boolean flag\n2. **Snapshot pattern** (`SyncbackMetadataTask`, `SyncbackEventTask`): Store original state in `undoData`, swap on undo\n\n```typescript\n// Snapshot pattern example\nconst undoData = { ics: event.ics, recurrenceStart: event.recurrenceStart };\nevent.ics = newIcs;  // Modify after capturing\nActions.queueTask(SyncbackEventTask.forUpdating({ event, undoData, description: 'Edit event' }));\n```\n\nSee `docs/undo-redo-task-pattern.md` for detailed implementation guide.\n\n### Task Queue (`flux/stores/task-queue.ts`)\n\nThe TaskQueue store observes Task model changes from the database and provides:\n- `queue()` - Active tasks\n- `completed()` - Finished tasks\n- `waitForPerformLocal(task)` - Promise that resolves when task runs locally\n- `waitForPerformRemote(task)` - Promise that resolves when task fully completes\n\n### Observable Database Pattern\n\n**Database is read-only in Electron** (`flux/stores/database-store.ts`):\n- `DatabaseStore.inTransaction()` throws - writes are not allowed\n- Uses SQLite in WAL mode via better-sqlite3 for concurrent reads\n- The sync engine exclusively handles writes\n\n**Change Records** (`flux/stores/database-change-record.ts`):\n\nWhen the sync engine modifies data, it emits JSON deltas that become `DatabaseChangeRecord` objects:\n```typescript\n{\n  type: 'persist' | 'unpersist',\n  objectClass: 'Thread' | 'Message' | ...,\n  objects: Model[],\n  objectsRawJSON: object[]\n}\n```\n\n**Reactive Queries** (`flux/models/query-subscription.ts`):\n\n`QuerySubscription` provides live-updating query results:\n```typescript\n// Subscribe to all unread threads\nconst subscription = new QuerySubscription(\n  DatabaseStore.findAll(Thread).where({ unread: true })\n);\nsubscription.addCallback((threads) => this.setState({ threads }));\n\n// Subscription automatically updates when DatabaseStore triggers\n```\n\n**Observable Integration** (`Rx.Observable.fromQuery`):\n\nWrap queries as RxJS observables for reactive UI updates:\n```typescript\nRx.Observable.fromQuery(DatabaseStore.findAll(Thread))\n  .subscribe(threads => this.updateUI(threads));\n```\n\n**ObservableListDataSource** (`flux/stores/observable-list-data-source.ts`):\n\nAdapts QuerySubscription for virtualized list components (MultiselectList), supporting:\n- Windowed/paginated data loading\n- Selection state management\n- Automatic updates from database changes\n\n### Data Flow Summary\n\n```\nUser Action → Actions.queueTask() → MailsyncBridge → stdin → Sync Engine\n                                                              │\n                                                              ▼\nUI Updates ← QuerySubscription ← DatabaseStore.trigger() ← stdout deltas\n```\n\n## Development Notes\n\n- Hot reload is available via `CTRL+R` (Windows/Linux) or `CMD+R` (macOS)\n- Dev tools accessible via Menu > Developer > Toggle Developer Tools\n- In dev tools console, `$m` provides access to `mailspring-exports` for debugging\n- Dev mode data is stored separately (e.g., `~/.config/Mailspring-dev/` on Linux)\n\n## Claude Hooks\n\n### after_edit\n\nRun linting after modifying TypeScript or JavaScript files.\n\n```json\n{\n  \"hooks\": {\n    \"after_edit\": [\n      {\n        \"command\": \"npm run lint\",\n        \"file_paths\": [\"**/*.ts\", \"**/*.tsx\", \"**/*.js\", \"**/*.jsx\"]\n      }\n    ]\n  }\n}\n```\n","category":"root","tokens":2100}]}