import SwiftUI import AppKit import OnyxCore import OnyxDesign import OnyxNotch import OnyxMenuBar import OnyxWidgetKit @main struct OnyxApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate var body: some Scene { // Onyx hat kein Hauptfenster. Alles lebt im Notch-Panel und in der // Menüleiste; die Einstellungen kommen in Phase 2 als eigenes Fenster. Settings { EmptyView() } } } @MainActor final class AppDelegate: NSObject, NSApplicationDelegate { private var coordinator: NotchCoordinator? private var menuBar: MenuBarController? private var onyxItem: NSStatusItem? private var layout: [WidgetPlacement] = [] func applicationDidFinishLaunching(_ notification: Notification) { NSApp.setActivationPolicy(.accessory) // Vor allem anderen: sicherstellen, dass wir allein sind. Zwei Instanzen // bringen zwei Zustandsmaschinen und zwei Animationen auf dieselbe // Notch — das sieht aus wie ein Ruckler und ist keiner. if case .blocked(let pids) = SingleInstance.claim() { SingleInstance.explainBlocked(by: pids) NSApp.terminate(nil) return } PlaceholderWidgets.registerAll() let known = Set(WidgetRegistry.shared.all.map(\.id)) layout = LayoutStore.standard().load(knownWidgetIDs: known) let placements = layout let coordinator = NotchCoordinator(policy: .builtInOnly) { _, presentation in NotchHostingView( rootView: NotchContainer(presentation: presentation) { WidgetGrid(placements: placements) }, presentation: presentation) } coordinator.start() // Die Panelgröße folgt dem Layout, nicht umgekehrt: wer ein Widget // hinzufügt, soll nicht auch noch die Fenstergröße nachziehen müssen. let size = WidgetGrid.panelSize(for: layout) coordinator.activeControllers.forEach { $0.contentSize = size } self.coordinator = coordinator let menuBar = MenuBarController() menuBar.start() self.menuBar = menuBar installOnyxStatusItem() } func applicationWillTerminate(_ notification: Notification) { coordinator?.stop() menuBar?.stop() } /// Das feste Onyx-Element: Panel öffnen, Einstellungen, Beenden. Es ist auch /// das Sicherheitsnetz, falls die Notch einmal nicht reagiert. private func installOnyxStatusItem() { let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) item.autosaveName = "onyx.menubar.main" item.button?.image = NSImage(systemSymbolName: "circle.hexagongrid.fill", accessibilityDescription: "Onyx") item.button?.image?.isTemplate = true let menu = NSMenu() menu.addItem(withTitle: "Panel öffnen", action: #selector(togglePanel), keyEquivalent: "") .target = self menu.addItem(.separator()) menu.addItem(withTitle: "Einstellungen …", action: #selector(openSettings), keyEquivalent: ",") .target = self menu.addItem(.separator()) menu.addItem(withTitle: "Onyx beenden", action: #selector(quit), keyEquivalent: "q") .target = self item.menu = menu onyxItem = item } @objc private func togglePanel() { coordinator?.togglePanelUnderPointer() } @objc private func openSettings() { NSApp.activate(ignoringOtherApps: true) NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) } @objc private func quit() { NSApp.terminate(nil) } }