Magic Apps

Documentation

macmagic.app

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: