# GoldenGateAlly Integration Guide

This folder is intended to be copied into another macOS app as a self-contained Bartender-style menu bar controller.

The high-level entry point is `GoldenGateManager`. The two user-facing settings pages are:

- `BartenderLayoutView`
- `BartenderGeneralView`

The lower-level scanner entry point is still `GoldenGateMenuItemManager`, but most host apps should use `GoldenGateManager`.

## What To Copy

Copy the entire `GoldenGateAlly` folder into the target app and add every file to the app target.

Important subgroups:

- Core manager: `GoldenGateManager.swift`
- Menu item scanner: `GoldenGateMenuItemManager.swift`, `MenuBarScanner.swift`, `AXMenuBarElementResolver.swift`
- Item model/resolvers: `MenuBarItem.swift`, `MenuBarDisplayNameResolver.swift`, `MenuBarAppIconResolver.swift`, `UnmovableItemDetector.swift`, `BartenderItemTypeResolver.swift`
- Snapshot capture/cache: `MenuBarSnapshotCapturer.swift`, `MenuBarSnapshotImageCache.swift`, `MenuBarDisplayImage.swift`, `MenuBarSnapshotModels.swift`
- MenuBarAgent plist movement: `MoveHelperMenuBarAgent/MoveHelperMenuBarAgent.swift`
- Visibility control: `MenuBarItemVisibilityController.swift`, `GoldenGateMenuBarAgentVisibilityManager.swift`, `GoldenGateBarManager.swift`
- Bartender Bar: `BartenderBarManager.swift`, `AutoHideTrackedAppsManager.swift`, `GoldenGateTransientMenuProbe.swift`, `MenuTrackingHelper.swift`
- Refresh/watchers: `GoldenGateMenuRefreshMonitor.swift`, `GoldenGateMenuBarAgentPlistMonitor.swift`, `GoldenGateEmptyMenuBarTriggerManager.swift`
- Desktop tinting: `GoldenGateDesktopMenuBarBackgroundSampler.swift`
- Settings UI: everything under `Settings UI/`
- Legacy capture bridge: `WindowCapture.h`, `WindowCapture.m`, `CGWindowBridge.swift`

`MoveHelperThree/MoveHelperThree.swift` is the older CGEvent move path. It is kept for reference/debugging. The current plist-based mover is `MoveHelperMenuBarAgent`.

## Host App Setup

### 1. Objective-C Bridging Header

`WindowCapture.m` is used by the legacy snapshot path. The host target needs a bridging header that imports:

```objc
#import "GoldenGateAlly/WindowCapture.h"
```

If the copied folder lives at a different relative path, update the import.

### 2. Frameworks

The files use:

- `AppKit`
- `SwiftUI`
- `Observation`
- `ApplicationServices`
- `ScreenCaptureKit`
- `CoreGraphics`
- `CoreImage`
- `CoreFoundation`
- `Combine`

The current app target already links these through system SDK imports. If a new target complains, add the missing frameworks in Build Phases.

### 3. Permissions

The feature relies on several macOS permissions/capabilities:

- Accessibility: required for AX scanning and clicking menu bar items.
- Screen Recording: required for menu item image capture through ScreenCaptureKit/legacy window capture.
- Full Disk Access: required for direct Control Center visibility plist edits.

The code prompts for Accessibility through `MenuBarScanner`. Screen Recording and Full Disk Access need host-app onboarding/settings copy.

### 4. Deployment

The Layout page uses SwiftUI `reorderContainer`, `reorderable`, and related APIs behind `@available(macOS 27.0, *)`. The current intended deployment for the full UI is macOS 27.

The lower-level scanning/moving code is not all macOS 27-specific, but the current reorder UI is.

## Basic Lifecycle

Create one long-lived manager and keep it alive for the lifetime of the app:

```swift
@State private var goldenGateManager = GoldenGateManager()
```

Start it once from the root UI or app coordinator:

```swift
.task {
    await goldenGateManager.start()
}
```

If your app has an explicit shutdown path, call:

```swift
goldenGateManager.stop()
```

`start()` installs the app status item, performs the initial scan, starts app-change monitoring, starts plist monitoring, starts empty-menu-bar triggers, and samples the desktop menu-bar background.

## Settings UI

The drop-in settings pages are:

```swift
BartenderLayoutView(manager: goldenGateManager)
BartenderGeneralView(manager: goldenGateManager)
```

`BartenderLayoutView` owns its faster plist polling mode. When the page is visible and the app scene is active, it asks `GoldenGateManager` to poll MenuBarAgent preferences more aggressively. When the page disappears or the app goes inactive, it disables that path.

This means a host app does not need to duplicate that logic in its root `ContentView`.

## Status Item

`GoldenGateManager.start()` calls `GoldenGateBarManager.installStatusItem(...)`.

The status item:

- uses an ellipsis-style menu bar item
- toggles hidden items or the Bartender Bar depending on settings
- opens the settings window through `GoldenGateManager.openSettingsWindow()`

If the host app has a custom settings window/router, replace the `onOpenSettings` closure in `GoldenGateManager.start()`.

## Key State Surfaces

Use these for UI:

- `manager.visibleItems`
- `manager.hiddenItems`
- `manager.alwaysHiddenItems`
- `manager.displayItems`
- `manager.barManager.state`
- `manager.bartenderBarManager.state`
- `manager.menuItemManager.state`
- `manager.menuItemManager.lastMoveErrorDescription`
- `manager.lastVisibilityErrorDescription`
- `manager.didDetectMenuBarAgentUpdate`
- `manager.menuBarBackgroundColor`

Useful item helpers:

- `manager.visibilityState(for:)`
- `manager.canToggleVisibility(_:)`
- `manager.isHidden(_:)`
- `item.isMovable`
- `item.unmovableReason`
- `item.menuBarAgentPlistMatch`
- `item.bartenderItemType`

## Moving Items

Current movement uses `MoveHelperMenuBarAgent`, which edits:

```text
~/Library/Preferences/com.apple.MenuBarAgent.plist
TrailingItemPreferredPositions
```

Positions are sanitized before writing:

- non-negative
- integer-only

Successful moves use targeted AX refreshes. Failed validation triggers a full refresh to avoid stale gaps.

System MenuBarAgent items such as Clock, Siri, Control Centre, and Audio/Video are force-visible/unhidable, but they can be moved when the resolver finds a `module:*` plist match.

## Hiding Items

There are two movement/visibility modes:

- `GoldenGateMoveType.controlCentre`
- `GoldenGateMoveType.menuBarAgent`

The default is currently Control Center mode.

Control Center mode edits the Control Center visibility plist through `MenuBarItemVisibilityController`. It preserves existing plist entry shapes and only toggles the allowed state.

MenuBarAgent mode uses synthetic anchor status items (`BTHidden`, `BTAlwaysHidden`) and moves items behind those anchors.

MenuBarAgent-owned system extras are force-visible internally and should not show hide controls.

## Bartender Bar

`BartenderBarManager` owns the floating mini bar. It is configured through `GoldenGateSettings` and surfaced in `BartenderGeneralView`.

Clicking an item in the Bartender Bar calls `GoldenGateManager.showAndClick(..., mode: .ephemeral)`.

Ephemeral items are tracked by `AutoHideTrackedAppsManager` and auto-hidden after the configured delay. Before auto-hiding, the manager probes for transient menus/popovers so it does not hide the item while the user is still interacting with it.

## Persistent Settings

`GoldenGateSettings` persists user-facing settings with `UserDefaults`.

Persisted:

- showing hidden items trigger
- empty menu bar auto-hide delay
- show behavior
- app status item visibility
- Bartender Bar enabled
- Bartender Bar background
- Bartender Bar custom color
- Bartender Bar auto-hide delay
- Bartender Bar show behavior

Not persisted by design:

- capture mode
- move type

Hidden and always-hidden local orders are persisted by `GoldenGateManager` using:

- `GoldenGateHiddenItemOrderKeys`
- `GoldenGateAlwaysHiddenItemOrderKeys`

## Snapshot Cache

`MenuBarSnapshotImageCache` stores item images on disk. Cache keys prefer stable item identity:

- MenuBarAgent plist match
- AX identifier
- AX source bundle/source app
- AX label/display label fallback

Frames are intentionally not part of the primary identity, so moves do not invalidate images.

## Extending Rules

Common extension points:

- Display names: `MenuBarDisplayNameResolver`
- App/icon symbols: `MenuBarAppIconResolver`
- Unmovable items: `UnmovableItemDetector`
- Bartender special items: `BartenderItemTypeResolver`
- MenuBarAgent item IDs: `MenuBarAgentItemIDResolver` inside `MoveHelperMenuBarAgent.swift`

When Apple exposes a system item with a friendly AX id but a different plist module key, add a narrow mapping in `knownModuleName(forIdentifierSuffix:)`.

Example:

```swift
case "audiovideo":
    return "AudioVideoModule"
```

## Minimal Host Example

```swift
@main
struct HostApp: App {
    @State private var goldenGateManager = GoldenGateManager()

    var body: some Scene {
        WindowGroup {
            NavigationSplitView {
                List {
                    NavigationLink("Layout") {
                        BartenderLayoutView(manager: goldenGateManager)
                    }
                    NavigationLink("General") {
                        BartenderGeneralView(manager: goldenGateManager)
                    }
                }
            } detail: {
                BartenderLayoutView(manager: goldenGateManager)
            }
            .task {
                await goldenGateManager.start()
            }
        }
    }
}
```

## Porting Checklist

1. Copy `GoldenGateAlly/`.
2. Add all copied files to the host app target.
3. Import `WindowCapture.h` from the host bridging header.
4. Ensure ScreenCaptureKit/AppKit/CoreGraphics/CoreImage are available.
5. Create one long-lived `GoldenGateManager`.
6. Call `await manager.start()` once.
7. Add `BartenderLayoutView` and `BartenderGeneralView` to settings.
8. Replace `GoldenGateManager.openSettingsWindow()` behavior if the host app has custom settings navigation.
9. Add onboarding for Accessibility, Screen Recording, and Full Disk Access.
10. Test moving, hiding, always-hidden, Bartender Bar, empty-space trigger, and auto-hide on a real macOS 27 system.
