import SwiftUI import AppKit import OnyxDesign import OnyxNotch import OnyxMenuBar @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? func applicationDidFinishLaunching(_ notification: Notification) { NSApp.setActivationPolicy(.accessory) let coordinator = NotchCoordinator(policy: .builtInOnly) { displayID, presentation in NotchHostingView( rootView: NotchContainer(presentation: presentation) { NotchPanelContent(displayID: displayID) }, presentation: presentation) } coordinator.start() 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) } } /// Platzhalterinhalt für Phase 1. Er beweist, dass Fenster, Geometrie und /// Zustandsmaschine zusammenspielen; die Widget-Ebene kommt in Phase 2. private struct NotchPanelContent: View { let displayID: CGDirectDisplayID var body: some View { ZStack { OnyxSurface() VStack(alignment: .leading, spacing: Onyx.Metric.tileSpacing) { Text("Onyx") .font(Onyx.Font.title) .foregroundStyle(Onyx.Color.textPrimary) Text("Phase 1 — Notch-Shell steht.") .font(Onyx.Font.body) .foregroundStyle(Onyx.Color.textSecondary) HStack(spacing: Onyx.Metric.tileSpacing) { ForEach(["Kalender", "Wetter", "Medien"], id: \.self) { name in OnyxTile { Text(name) .font(Onyx.Font.caption) .foregroundStyle(Onyx.Color.textTertiary) } } } } .padding(Onyx.Metric.panelPadding) } } }