|
| 1 | +# Insomnia |
| 2 | + |
| 3 | +A macOS caffeinate utility — menu bar app + CLI. Mascot: Dapple the Mushroom. |
| 4 | + |
| 5 | +## Tech Stack |
| 6 | + |
| 7 | +- Swift (SwiftUI + AppKit hybrid) |
| 8 | +- macOS 14+, Apple Silicon (arm64) only |
| 9 | +- IOKit power assertions (native, no shelling out to `caffeinate`) |
| 10 | +- Swift ArgumentParser for CLI |
| 11 | +- XCTest for unit + integration tests |
| 12 | + |
| 13 | +## Project Structure |
| 14 | + |
| 15 | +Three targets in `Package.swift`: |
| 16 | +- `InsomniaCore` — shared library (power management, scheduling, IPC, models) |
| 17 | +- `Insomnia` — macOS GUI app (MenuBarExtra + AppKit) |
| 18 | +- `InsomniaCLI` — CLI tool |
| 19 | + |
| 20 | +## Build & Test |
| 21 | + |
| 22 | +```bash |
| 23 | +swift build # debug build |
| 24 | +swift test # 118+ tests |
| 25 | +swift run Insomnia # run GUI locally |
| 26 | +swift run InsomniaCLI status # run CLI |
| 27 | +``` |
| 28 | + |
| 29 | +## Code Comments |
| 30 | + |
| 31 | +85%+ comment coverage required. Every file needs: |
| 32 | +- File header explaining purpose |
| 33 | +- `///` doc comments on public APIs |
| 34 | +- Inline comments explaining intent on significant code blocks |
| 35 | + |
| 36 | +## Versioning |
| 37 | + |
| 38 | +- Tags: `v{major}.{minor}` (e.g., `v0.2`, `v0.3`) — no patch number |
| 39 | +- Build number: GitHub Actions run number becomes the patch |
| 40 | +- Bundle version: `{major}.{minor}.{runNumber}` (e.g., `0.2.42`) |
| 41 | +- The `.0` patch in tags is meaningless — never use `v0.2.0` |
| 42 | + |
| 43 | +## Distribution |
| 44 | + |
| 45 | +- Homebrew: `brew install --cask gordonbeeming/tap/insomnia` |
| 46 | + - Always use fully qualified name (upstream "Insomnia" API client conflicts) |
| 47 | + - Cask auto-updated by CI on release via deploy key |
| 48 | +- GitHub Releases: signed + notarized DMG + CLI binary |
| 49 | +- No App Store (IOKit needs unsandboxed access) |
| 50 | + |
| 51 | +## CI/CD |
| 52 | + |
| 53 | +- `.github/workflows/build.yml` |
| 54 | +- Push/PR: build + test only |
| 55 | +- Release (published): build + test + sign + notarize + DMG + upload + update Homebrew tap |
| 56 | +- Secrets: `DEVELOPER_ID_CERTIFICATE`, `DEVELOPER_ID_PASSWORD`, `APPLE_ID`, `APPLE_TEAM_ID`, `APPLE_APP_PASSWORD`, `HOMEBREW_TAP_DEPLOY_KEY` |
| 57 | + |
| 58 | +## Dev vs Prod |
| 59 | + |
| 60 | +Debug builds use separate identifiers (`BuildEnvironment.swift`): |
| 61 | +- App name: "Insomnia Dev" |
| 62 | +- IPC socket: `~/Library/Application Support/Insomnia Dev/insomnia.sock` |
| 63 | +- UserDefaults prefix: `com.insomnia.dev.*` |
| 64 | +- "(Dev)" suffix in status text |
| 65 | + |
| 66 | +This lets dev and prod run side-by-side. |
| 67 | + |
| 68 | +## Key Files |
| 69 | + |
| 70 | +- `Sources/InsomniaCore/Power/PowerAssertionManager.swift` — IOKit assertion lifecycle |
| 71 | +- `Sources/InsomniaCore/IPC/IPCServer.swift` — Unix domain socket server |
| 72 | +- `Sources/InsomniaCore/Models/BuildEnvironment.swift` — dev/prod separation |
| 73 | +- `Sources/Insomnia/InsomniaApp.swift` — @main, MenuBarExtra scene |
| 74 | +- `Sources/Insomnia/Views/MenuBarView.swift` — main dropdown menu |
| 75 | +- `Sources/Insomnia/AppDelegate.swift` — lifecycle, IPC server, icon loading |
| 76 | +- `Scripts/build-release.sh` — builds CLI + .app bundle (args: version, build number) |
| 77 | +- `Resources/AppIcon.icns` — Dapple mushroom icon |
| 78 | + |
| 79 | +## Window Focus Pattern |
| 80 | + |
| 81 | +LSUIElement apps need special handling to show windows: |
| 82 | +1. `NSApp.setActivationPolicy(.regular)` before opening |
| 83 | +2. `openWindow(id:)` to open |
| 84 | +3. Reapply app icon via `AppDelegate.reapplyAppIcon()` |
| 85 | +4. `NSApp.activate(ignoringOtherApps: true)` after short delay |
| 86 | +5. Return to `.accessory` in `onDisappear` (unless dock icon enabled) |
0 commit comments