Architecture overview
Magimail is a native macOS desktop client for Gmail. This page maps the tree on main and says how
the pieces fit; README.md indexes the pages that go deeper into each subsystem.
Stack and workspace
The runtime, build tooling and workspace layout are true of both apps and live in
../developer-guidelines/coding-standards.md's Stack and workspace.
System overview
Magimail runs the shared process shape: an Electron main process orchestrates, the Swift helper
draws the native UI in a child process, a C++ N-API addon reaches the window from inside Electron,
and the OS-hosted extensions are reached through files, URL schemes and notifications. Gmail itself
runs in a Chromium renderer, one per account partition. The shared mechanism is in
../shared/process-model-and-ipc.md.
flowchart TD
subgraph Main["Electron main process"]
Orchestration["Window, session and routing"]
Pollers["Atom feed pollers"]
AddonBridge["Native addon bridge"]
end
subgraph Renderers["Chromium renderer processes"]
Views["WebContentsView per Gmail account partition"]
end
subgraph Helper["Swift helper (MagimailHelper)"]
SettingsWin["Settings window"]
StatusItem["Menu bar popover"]
Notify["UNUserNotificationCenter"]
end
subgraph Addon["Native N-API addon (in-process)"]
Cocoa["Cocoa window and NSToolbar"]
Drag["Share picker and synthesised drag"]
WidgetReload["WidgetKit timeline reloads"]
end
subgraph Ext["App extensions (OS-hosted .appex)"]
Share["MagimailShare.appex"]
Widgets["MagimailWidgets.appex"]
Intents["MagimailAppIntents.appex"]
end
Main -->|"WebContentsView API"| Views
Main <-->|"newline-delimited JSON-RPC on stdio"| Helper
Main <-->|"N-API callbacks"| Addon
Share -->|"magimail://share URL scheme"| Main
Widgets -->|"App Group snapshot file"| Main
Intents -->|"distributed notification and state file"| Helper
Each account view is a WebContentsView on its own persist:magimail_<id> partition, stacked in
one BaseWindow; see Window and views.
Window and views
The main window is a BaseWindow with a native Swift toolbar and several child WebContentsView
instances stacked by z-order. A BaseWindow carries no hidden renderer of its own, which saves
about 100MB against a BrowserWindow:
graph TD
subgraph "BaseWindow (1280x850, vibrancy: sidebar)"
direction TB
toolbar["Native NSToolbar (unified style)<br/>SF Symbols, NSSegmentedControl account switcher"]
acc1["Account 1 WebContentsView<br/>persist:magimail_personal<br/>mail.google.com"]
acc2["Account 2 WebContentsView<br/>persist:magimail_work<br/>mail.google.com"]
end
toolbar ~~~ acc1
toolbar ~~~ acc2
subgraph "Separate windows"
compose["Compose BrowserWindow<br/>720x770, vibrancy: under-window"]
thread["Thread BrowserWindow<br/>900x780, vibrancy: under-window"]
end
Only the active account view is attached; others are detached but keep their session and unread
state. There is no drag WebContentsView: the NSToolbar sits over the titlebar and consumes those
events itself, so AppKit drags the window from the toolbar's empty space.
The menu bar popover is a native NSPopover owned by the Swift helper, not an Electron window; see
process-model-and-ipc.md.
Sessions and browser identity
Each account gets its own Chromium session partition, named persist:magimail_<id> by
account-store.ts (persist:magimail_personal, persist:magimail_work, …), so cookies, storage and
auth state are fully isolated. The shared session configuration and browser identity are in
../shared/sessions-and-security.md.
Native toolbar
The toolbar's mechanism, and the rules every native toolbar item follows, are in
../shared/native-ui.md. Magimail's default layout holds a native
NSSegmentedControl account switcher, a Pop-out New Message button (Cmd+Shift+N), Share, and
Print. It sets allowsUserCustomization = true, so you can add the triage (Archive, Trash,
Toggle Read), reply actions and search through View → Customize Toolbar... or by right-clicking
empty toolbar space.
Menu bar
The status item and its popover run in the Swift helper; the mechanism is in
../shared/menu-bar.md. Magimail's glyph, unread feed aging and SwiftUI
popover layout are in menu-bar.md, and every row action is in
actions.md.
Styling and theming
The vibrancy, transparency and CSS-injection mechanism is shared and in
../shared/design.md; Magimail's dark-mode filter, and every selector it
exempts, is in dark-mode.md.
Two details are Magimail's own:
- Decoration panel divs (
div.wl,.wq,.wp,.wo,.wn) are hardcoded CSS selectors. They are Gmail's empty background-panel siblings and carry no semantic attributes to match on instead. The CSS fast path leaves no timing gap, so there is no structural JS fallback anchored todiv[role="navigation"]. - Transparent background. Toggling
transparentBackgroundin settings callssetVibrancy('sidebar')/setVibrancy(null)on theBaseWindow. The CSS is the same either way, so transparency shows vibrancy when on and the window's white or darkbackgroundColorwhen off.