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.
179 lines
5.8 KiB
Swift
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
|