burrow/Apple/App/AppDelegate.swift
JettChenT d1638726ca Add proxy subscription runtime support
Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling.

Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
2026-06-01 15:23:17 +08:00

179 lines
5.8 KiB
Swift

#if os(macOS)
import AppKit
import BurrowConfiguration
import BurrowCore
import BurrowUI
import SwiftUI
import libburrow
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private var windowController: NSWindowController?
private let logger = Logger.logger(for: AppDelegate.self)
private let quitItem = AppDelegate.makeQuitItem()
private lazy var openItem: NSMenuItem = {
let item = NSMenuItem(
title: "Open Burrow",
action: #selector(openWindow),
keyEquivalent: "o"
)
item.target = self
item.keyEquivalentModifierMask = .command
return item
}()
private let toggleItem: NSMenuItem = {
let toggleView = NSHostingView(rootView: MenuItemToggleView())
toggleView.frame.size = CGSize(width: 300, height: 32)
toggleView.autoresizingMask = [.width]
let toggleItem = NSMenuItem()
toggleItem.view = toggleView
return toggleItem
}()
private lazy var menu: NSMenu = {
let menu = NSMenu()
menu.items = [
toggleItem,
openItem,
.separator(),
quitItem
]
return menu
}()
private lazy var statusItem: NSStatusItem = {
let statusBar = NSStatusBar.system
let statusItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
if let button = statusItem.button {
button.image = NSImage(systemSymbolName: "pipe.and.drop.fill", accessibilityDescription: nil)
}
return statusItem
}()
func applicationDidFinishLaunching(_ notification: Notification) {
installMainMenu()
startDaemon()
statusItem.menu = menu
}
private func startDaemon() {
do {
libburrow.spawnInProcess(
socketPath: try Constants.socketURL.path(percentEncoded: false),
databasePath: try Constants.databaseURL.path(percentEncoded: false)
)
} catch {
logger.error("Failed to spawn daemon thread: \(error)")
}
}
private func installMainMenu() {
let mainMenu = NSMenu(title: "Burrow")
let appItem = NSMenuItem(title: "Burrow", action: nil, keyEquivalent: "")
let appMenu = NSMenu(title: "Burrow")
let aboutItem = NSMenuItem(
title: "About Burrow",
action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)),
keyEquivalent: ""
)
aboutItem.target = NSApplication.shared
appMenu.addItem(aboutItem)
appMenu.addItem(.separator())
appMenu.addItem(Self.makeQuitItem())
appItem.submenu = appMenu
mainMenu.addItem(appItem)
let editItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
editItem.submenu = makeEditMenu()
mainMenu.addItem(editItem)
NSApplication.shared.mainMenu = mainMenu
}
private static func makeQuitItem() -> NSMenuItem {
let quitItem = NSMenuItem(
title: "Quit Burrow",
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"
)
quitItem.target = NSApplication.shared
quitItem.keyEquivalentModifierMask = .command
return quitItem
}
private func makeEditMenu() -> NSMenu {
let editMenu = NSMenu(title: "Edit")
editMenu.addItem(NSMenuItem(title: "Undo", action: Selector(("undo:")), keyEquivalent: "z"))
editMenu.addItem(NSMenuItem(title: "Redo", action: Selector(("redo:")), keyEquivalent: "Z"))
editMenu.addItem(.separator())
editMenu.addItem(NSMenuItem(title: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x"))
editMenu.addItem(NSMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"))
editMenu.addItem(NSMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"))
let pastePlainItem = NSMenuItem(
title: "Paste and Match Style",
action: #selector(NSTextView.pasteAsPlainText(_:)),
keyEquivalent: "V"
)
pastePlainItem.keyEquivalentModifierMask = [.command, .option, .shift]
editMenu.addItem(pastePlainItem)
editMenu.addItem(NSMenuItem(title: "Delete", action: #selector(NSText.delete(_:)), keyEquivalent: ""))
editMenu.addItem(.separator())
editMenu.addItem(NSMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"))
return editMenu
}
@objc
private func openWindow() {
if let window = windowController?.window {
window.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)
return
}
let contentView = BurrowView()
let hostingController = NSHostingController(rootView: contentView)
if #available(macOS 13.0, *) {
hostingController.sizingOptions = []
}
let window = NSWindow(contentViewController: hostingController)
window.title = "Burrow"
window.setContentSize(NSSize(width: 820, height: 720))
window.contentMinSize = NSSize(width: 640, height: 560)
window.styleMask.insert([.titled, .closable, .miniaturizable, .resizable])
window.center()
let controller = NSWindowController(window: window)
controller.shouldCascadeWindows = true
controller.showWindow(nil)
windowController = controller
NSApplication.shared.activate(ignoringOtherApps: true)
}
}
@main
enum BurrowApplication {
@MainActor
private static let delegate = AppDelegate()
@MainActor
static func main() {
let application = NSApplication.shared
application.delegate = delegate
application.setActivationPolicy(.accessory)
application.finishLaunching()
application.run()
}
}
#endif