Skip to content

Latest commit

 

History

History
94 lines (76 loc) · 4.09 KB

File metadata and controls

94 lines (76 loc) · 4.09 KB

Native macOS Shell

apps/macos contains the native app shell for germ. It is a SwiftPM macOS app that wraps the localhost daemon instead of replacing it — the same architecture as oída's shell: the daemon stays the single source of truth and every surface renders the same state.

Shape

apps/macos/
  Package.swift
  Sources/GermMacOS/
    App/        SwiftUI entry point and AppKit launch delegate
    Services/   Shell store (health polling, daemon lifecycle),
                daemon supervisor, launch-at-login
    Support/    Repository locator
    Views/      Main window (embedded dashboard), menu bar extra, settings
  Resources/    AppIcon.svg + AppIcon.icns (generated by script/make_icon.py)
  script/       build_and_run.sh, make_icon.py

Build and launch:

apps/macos/script/build_and_run.sh

--build-only stages apps/germ.app without launching it, so the runnable bundle is visible beside the repository's app sources. The icon and brand PNG family are generated deterministically by script/make_icon.py (stdlib-only Python + iconutil). The vector sources are Resources/AppIcon.svg and docs/assets/germ-logo-{light,dark}.svg; the same centered-culture geometry also produces the 2048 px light/dark PNGs and web icon sizes. Its single arc is oída's outer listening arc mirrored exactly, creating the corresponding lower-right opening around three centered cells.

One Daemon, One Dashboard

The main window embeds the daemon's own dashboard (http://127.0.0.1:5178/dashboard) in a WKWebView, so the mac app and the browser render the exact same page and cannot diverge: same Chamber, same modules and connections, same library, same providers.

Cross-surface session continuity comes from the daemon, not the WebView: the dashboard autosaves the live graph to PUT /sessions/current (debounced) and restores it on boot when its own graph is empty, and named sessions live at /sessions under output/sessions/. Opening the mac app after patching in a browser (or the other way around) restores the same modules and connections. Both surfaces write last-writer-wins; the client_id field records which surface saved last.

The WKWebView grants microphone capture natively (macOS permission still applies) so the Chamber's Record module works in-app, saves the dashboard's WAV/JSON/GWT downloads to the user's Downloads folder, and opens target="_blank" links in the default browser. Audio Snapshot uses WebKit's native display-sharing picker; choose a display or app and include its audio, subject to the normal macOS Screen Recording permission.

Supervision

If no daemon is reachable at launch, the shell locates the repository root by walking up from the bundle/current directory or by reading GERM_REPOSITORY_ROOT, then starts one:

uv run uvicorn server.main:app --host 127.0.0.1 --port 5178

Common development paths are appended to PATH so a GUI-launched app can find uv, and Homebrew dylib paths are exported for the audio toolchain. The shell stops only processes it started itself — on explicit Stop Managed Daemon and on app quit. A small standard-library process supervisor watches the owning app PID and tears down the uvicorn process group if the app exits unexpectedly, so managed daemons cannot be orphaned. A daemon already running outside the shell is observed over HTTP and never owned.

Menu Bar & Settings

The menu bar extra shows daemon status (/health: provider, device, loaded models) plus open-window / open-in-browser / settings / start / stop / quit. Settings exposes the persisted light/dark appearance and accent palette, the daemon base URL (for an alternate-port daemon), launch-at-login through SMAppService.mainApp, and the managed daemon's recent log lines. Appearance changes synchronize with the embedded dashboard and its browser-local saved preferences.

Current Limits

  • The shell is unsigned by default; see oída's docs/macos-signing-notarization.md for the Developer ID flow (the same script layout applies).
  • The shell adds no audio processing of its own — everything sonic stays in the dashboard's engine and the Python DSP routes.