-
Notifications
You must be signed in to change notification settings - Fork 12
systems app shell
Active contributors: Nik, Ran
The app shell is the menu bar application that hosts everything else: it owns the process lifecycle, the status-bar item and menu, the Sparkle updater, server startup/shutdown ordering, notifications, auth-directory monitoring, and the SwiftUI settings window. DroidProxy is an LSUIElement app, so it has no dock icon and no main window — the menu bar item is the primary surface, and settings open in an on-demand window.
The shell spans src/Sources/main.swift, src/Sources/AppDelegate.swift, and src/Sources/SettingsView.swift, supported by src/Sources/IconCatalog.swift, src/Sources/LogoView.swift, and src/Sources/NotificationNames.swift.
src/Sources/
main.swift # NSApplicationMain entry point
AppDelegate.swift # menu bar, lifecycle, Sparkle, server ordering
SettingsView.swift # SwiftUI settings window
IconCatalog.swift # thread-safe NSImage cache
LogoView.swift # inline-SVG wordmark
NotificationNames.swift # shared Notification.Name constants
| Type | Role |
|---|---|
main.swift |
Creates NSApplication.shared, assigns an AppDelegate, and calls NSApplicationMain. |
AppDelegate |
NSApplicationDelegate + NSWindowDelegate + UNUserNotificationCenterDelegate. Owns statusItem, menu, serverManager, thinkingProxy, the Sparkle SPUStandardUpdaterController, the AuthDirectoryMonitor, and the settings window. |
SettingsView |
SwiftUI Form with all settings sections. Holds AuthManager, OAuthUsageTracker, and @AppStorage-backed AppPreferences. |
ServiceRow / AccountRowView
|
Reusable provider row with expandable account list, disable/remove actions, and the round-robin note. |
VisualEffectBlur |
NSViewRepresentable wrapping NSVisualEffectView for behind-window blur. |
IconCatalog |
Singleton thread-safe NSImage cache keyed by name + size + template flag. |
LogoView |
Renders the DroidProxy wordmark from an inline SVG as a template image. |
NotificationNames |
serverStatusChanged and authDirectoryChanged; droidProxyThemeChanged is declared in AppDelegate.swift. |
main.swift boots NSApplication with an AppDelegate. applicationDidFinishLaunching forces the dark appearance, builds the main menu (so Cmd+C/V/X/A work), builds the menu bar, instantiates ServerManager and ThinkingProxy, warms commonly used icons, requests notification authorization, starts the servers, and registers observers for serverStatusChanged and authDirectoryChanged.
startServer() starts the ThinkingProxy first, then pollForProxyReadiness polls up to 60 times at 50 ms intervals for thinkingProxy.isRunning. Only once the proxy is ready does it call serverManager.start; if the backend fails, the proxy is stopped again to keep state consistent. stopServer() reverses the order — proxy first, then backend.
sequenceDiagram
participant M as main.swift
participant A as AppDelegate
participant TP as ThinkingProxy :8317
participant SM as ServerManager :8318
M->>A: NSApplicationMain + delegate
A->>A: applicationDidFinishLaunching
A->>A: setupMainMenu / setupMenuBar
A->>A: init ServerManager + ThinkingProxy
A->>A: preloadIcons, configureNotifications
A->>TP: startServer() -> thinkingProxy.start()
loop poll up to 60x @ 50ms
A->>TP: thinkingProxy.isRunning?
end
TP-->>A: ready
A->>SM: serverManager.start { success }
alt backend ok
SM-->>A: success
A->>A: updateMenuBarStatus + notify "Server Started"
else backend failed
A->>TP: thinkingProxy.stop()
A->>A: notify "Server Failed"
end
setupMenuBar builds a variable-length status item and a menu: a status line, Open Settings, Start/Stop Server (tag startStop), Copy Server URL (tag copyURL), Open Dashboard (tag dashboard), Check for Updates... (targeting the Sparkle SPUStandardUpdaterController), and Quit. updateMenuBarStatus rewrites the status line, the start/stop title, and the enabled state of the copy/dashboard items, then refreshes the icon. updateStatusBarIcon pulls icon-active.png / icon-inactive.png from IconCatalog and falls back to the network / network.slash SF Symbols if the bundled asset is missing. Copy Server URL copies http://<host>:8317, and Open Dashboard opens http://<host>:8318/management.html (host derived from AppPreferences.bindAddress, with 0.0.0.0 displayed as localhost/127.0.0.1).
Notifications are delivered through UNUserNotificationCenter (showNotification), with willPresent configured to show banners while the app is foreground. startMonitoringAuthDirectory installs an AuthDirectoryMonitor; on change it posts authDirectoryChanged, and handleAuthDirectoryChanged re-fronts the settings window so newly discovered accounts appear. A themeObserver listens for droidProxyThemeChanged and re-runs applyTheme, which toggles between an opaque black window (OLED / full opacity) and a clear non-opaque window (translucent Liquid Glass).
createSettingsWindow builds a 1000×900 titled window with a transparent titlebar (titlebarAppearsTransparent, hidden title, movable by background) so the traffic-light buttons float over the content, then hosts SettingsView in an NSHostingView. The window is not released on close; windowDidClose removes the theme observer and clears the reference.
quit stops the servers, waits 0.3 s, then terminates. applicationWillTerminate removes observers, stops the auth monitor, and stops the servers; applicationShouldTerminate also stops the servers and returns .terminateNow.
SettingsView is a grouped Form with these sections:
-
Server status — capsule button that starts/stops
ServerManager. -
OAuth Quota Usage —
oauthUsageDashboardplus a refresh button drivingOAuthUsageTracker; shown only when a Codex or Claude provider/account is present. -
Launch at login —
SMAppService.mainAppregister/unregister (macOS 13+). -
Auth files — "Open Folder" opens
~/.cli-proxy-api/. -
Factory custom models — Apply / Re-apply writes DroidProxy model aliases into
~/.factory/settings.json(timestamped.bakfirst), reconciled againstcheckFactoryModelsInstalled. -
Remote Management — collapsible
allow-remotetoggle,secret-keyfield, and a beta-gated bind-address field. -
Logging — verbose toggle plus "Open Logs" for
~/.cli-proxy-api/logs/. -
Services — one
providerServiceRowper provider (claude, codex, antigravity, kimi, and beta-gated cursor) plus the collapsible Codex fast-mode subsection (GPT 5.3 Codex / 5.4 / 5.5 toggles).
ServiceRow and AccountRowView render the per-provider enable toggle, the expandable connected-account list, per-account disable/remove with confirmation, and the round-robin-with-auto-failover note when more than one account is enabled. The Liquid Glass helpers (droidGlassCard, droidGlassCapsule, droidGlassProminent, droidGlassPlain) gate on macOS 26 availability and fall back to flat fills/standard button styles. The header carries a Beta toggle, a background-opacity slider, and an OLED/Liquid-Glass theme button; the background composes VisualEffectBlur with radial gradients (or solid black under OLED).
-
Drives
ServerManagerandThinkingProxy: the shell owns startup ordering, the start/stop menu, and the server-status capsule. -
Renders
AuthManageraccounts (viaServiceRow) andOAuthUsageTrackerwindows (via the usage dashboard). -
Writes Factory settings through
DroidProxyModelCatalogwhen Apply / Re-apply runs. -
Reads/writes
AppPreferencesthrough@AppStorage(fast-mode toggles, remote access, bind address, theme, opacity, verbose logging, beta flag). -
Notifications: posts and observes
serverStatusChanged,authDirectoryChanged, anddroidProxyThemeChanged.
- Add a menu item: extend
setupMenuBarand, if state-dependent,updateMenuBarStatus(assign a tag inMenuTag). - Change startup/shutdown ordering or readiness polling: edit
startServer/pollForProxyReadiness/stopServer. - Add a settings section or provider row: edit
SettingsView.bodyandproviderServiceRow. - Change window chrome or theming: edit
createSettingsWindow/applyThemeand thebackgroundblock inSettingsView. - Add a cached icon: call
IconCatalog.shared.image(named:resizedTo:template:)and optionally warm it inpreloadIcons.
| File | Role |
|---|---|
src/Sources/main.swift |
NSApplicationMain entry point. |
src/Sources/AppDelegate.swift |
Menu bar, lifecycle, Sparkle, server ordering, notifications, theme, settings window. |
src/Sources/SettingsView.swift |
SwiftUI settings form and reusable rows. |
src/Sources/IconCatalog.swift |
Thread-safe NSImage cache. |
src/Sources/LogoView.swift |
Inline-SVG wordmark. |
src/Sources/NotificationNames.swift |
Shared Notification.Name constants. |