import MastermindPOCCore import SwiftUI final class OverlayViewModel: ObservableObject { @Published var statusTitle = "Mastermind: Idle" @Published var statusDetail = "Native macOS companion proof" @Published var clickThroughEnabled = false @Published var backgroundOpacity = OverlaySettings.default.backgroundOpacity @Published var screenFrames = 0 @Published var microphoneFrames = 0 @Published var microphoneBytes = 0 @Published var systemFrames = 0 @Published var systemBytes = 0 func apply(status: TrustStatus) { statusTitle = status.menuBarTitle switch status { case .idle: statusDetail = "Ready. Capture is off." case .listening: statusDetail = "Microphone capture is active." case .screenContext: statusDetail = "Screen context proof is active." case .systemAudio: statusDetail = "System audio proof is active." case .agentWorking: statusDetail = "Agent work placeholder." case .paused: statusDetail = "All capture paused." case .permissionNeeded(let message): statusDetail = message case .error(let message): statusDetail = message } } } struct OverlayView: View { @ObservedObject var viewModel: OverlayViewModel let actions: OverlayActions var body: some View { VStack(alignment: .leading, spacing: 12) { HudTitleBar(viewModel: viewModel, actions: actions) VStack(alignment: .leading, spacing: 4) { Text(viewModel.statusTitle) .font(.system(.title3, design: .rounded, weight: .semibold)) .foregroundStyle(.white) Text(viewModel.statusDetail) .font(.callout) .foregroundStyle(.white.opacity(0.78)) .lineLimit(2) } Divider() .overlay(.white.opacity(0.2)) HStack(spacing: 14) { CounterView(label: "Screen", value: viewModel.screenFrames) CounterView(label: "Mic", value: viewModel.microphoneFrames) CounterView(label: "System", value: viewModel.systemFrames) } Text("PCM bytes mic \(viewModel.microphoneBytes) | system \(viewModel.systemBytes)") .font(.caption2.monospacedDigit()) .foregroundStyle(.white.opacity(0.62)) } .padding(18) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .background( RoundedRectangle(cornerRadius: 18, style: .continuous) .fill(.black.opacity(viewModel.backgroundOpacity)) .stroke(.white.opacity(0.16), lineWidth: 1) ) } } private struct HudTitleBar: View { @ObservedObject var viewModel: OverlayViewModel let actions: OverlayActions var body: some View { HStack(spacing: 10) { ZStack(alignment: .leading) { WindowDragRegion() HStack(spacing: 8) { Image(systemName: "sparkles") .font(.caption.weight(.semibold)) Text("Mastermind") .font(.headline) Text(viewModel.clickThroughEnabled ? "Click-through" : "Interactive") .font(.caption) .foregroundStyle(.white.opacity(0.66)) } .foregroundStyle(.white) .allowsHitTesting(false) } .frame(height: 28) HStack(spacing: 6) { HudIconButton(systemName: "gearshape", help: "Settings", action: actions.openSettings) HudIconButton(systemName: "eye.slash", help: "Hide assistant", action: actions.hideOverlay) HudIconButton(systemName: "xmark", help: "Quit Mastermind POC", role: .destructive, action: actions.quitApp) } } } } private struct HudIconButton: View { let systemName: String let help: String var role: ButtonRole? let action: () -> Void var body: some View { Button(role: role, action: action) { Image(systemName: systemName) .font(.caption.weight(.semibold)) .frame(width: 24, height: 24) .contentShape(Rectangle()) } .buttonStyle(.plain) .foregroundStyle(.white.opacity(0.82)) .background(.white.opacity(0.08), in: RoundedRectangle(cornerRadius: 6, style: .continuous)) .help(help) } } private struct WindowDragRegion: NSViewRepresentable { func makeNSView(context: Context) -> DragHandleView { DragHandleView() } func updateNSView(_ nsView: DragHandleView, context: Context) {} } private final class DragHandleView: NSView { override var mouseDownCanMoveWindow: Bool { true } override func mouseDown(with event: NSEvent) { window?.performDrag(with: event) } } private struct CounterView: View { let label: String let value: Int var body: some View { VStack(alignment: .leading, spacing: 3) { Text(label) .font(.caption2) .foregroundStyle(.white.opacity(0.58)) Text("\(value)") .font(.caption.monospacedDigit().weight(.semibold)) .foregroundStyle(.white) } .frame(maxWidth: .infinity, alignment: .leading) } }