Ariver
2026-05-10 75881f7d8e152fffc123745470187798e29eb7f2
Apptag/ApptagApp.swift
@@ -216,41 +216,10 @@
            NSMouseInRect(mousePoint, $0.frame, false)
        }) ?? NSScreen.main ?? NSScreen.screens.first else { return }
        if overlayWindow == nil {
            let panel = OverlayPanel(
                contentRect: screen.frame,
                styleMask: [.borderless, .fullSizeContentView, .nonactivatingPanel],
                backing: .buffered,
                defer: false
            )
            panel.isFloatingPanel = true
            panel.hidesOnDeactivate = false
            overlayWindow = panel
            overlayWindow?.collectionBehavior = [
                .canJoinAllSpaces,
                .fullScreenAuxiliary,
                .stationary,
                .transient,
                .ignoresCycle
            ]
            overlayWindow?.isOpaque = false
            overlayWindow?.backgroundColor = .clear
            overlayWindow?.hasShadow = false
            overlayWindow?.titlebarAppearsTransparent = true
            overlayWindow?.titleVisibility = .hidden
            overlayWindow?.isReleasedWhenClosed = false
            overlayWindow?.contentView = DismissibleHostingView(
                rootView: ContentView(hideOverlay: { [weak self] in
                    self?.hideOverlay()
                }),
                onBackdropTap: { [weak self] in
                    self?.hideOverlay()
                }
            )
        }
        overlayWindow?.orderOut(nil)
        overlayWindow = makeOverlayWindow(on: screen)
        overlayWindow?.setFrame(screen.frame, display: true)
        overlayWindow?.level = .screenSaver
        overlayWindow?.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))
        // Local key monitor: catch Escape while overlay is up
        NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
@@ -263,12 +232,47 @@
        overlayWindow?.makeKeyAndOrderFront(nil)
        overlayWindow?.orderFrontRegardless()
        NSApp.activate(ignoringOtherApps: true)
    }
    private func hideOverlay() {
        guard !isInEditMode else { return }
    private func makeOverlayWindow(on screen: NSScreen) -> NSWindow {
        let panel = OverlayPanel(
            contentRect: screen.frame,
            styleMask: [.borderless, .fullSizeContentView, .nonactivatingPanel],
            backing: .buffered,
            defer: false
        )
        panel.isFloatingPanel = true
        panel.hidesOnDeactivate = false
        panel.collectionBehavior = [
            .moveToActiveSpace,
            .fullScreenAuxiliary,
            .stationary,
            .transient,
            .ignoresCycle
        ]
        panel.isOpaque = false
        panel.backgroundColor = .clear
        panel.hasShadow = false
        panel.titlebarAppearsTransparent = true
        panel.titleVisibility = .hidden
        panel.isReleasedWhenClosed = false
        panel.contentView = DismissibleHostingView(
            rootView: ContentView(hideOverlay: { [weak self] in
                self?.hideOverlay(force: true)
            }),
            onBackdropTap: { [weak self] in
                self?.hideOverlay()
            }
        )
        return panel
    }
    private func hideOverlay(force: Bool = false) {
        guard force || !isInEditMode else { return }
        overlayWindow?.orderOut(nil)
        if force {
            overlayWindow = nil
        }
    }
    // MARK: - Global Hotkey (Shift+Option+Space)
@@ -341,19 +345,26 @@
            // Settings/Preferences window → float it above overlay for real-time preview
            if NSApp.windows.contains(keyWindow) {
                if self.overlayWindow?.isVisible == true {
                    keyWindow.level = .popUpMenu
                }
                if self.settingsWindow == nil {
                    keyWindow.minSize = NSSize(width: 660, height: 380)
                    keyWindow.maxSize = NSSize(width: 660, height: CGFloat.greatestFiniteMagnitude)
                }
                self.settingsWindow = keyWindow
                self.prepareSettingsWindow(keyWindow)
                return
            }
            self.hideOverlay()
        }
    }
    /// Settings must always float above the overlay so changes can be previewed live.
    private func prepareSettingsWindow(_ window: NSWindow) {
        if overlayWindow?.isVisible == true {
            let overlayLevel = overlayWindow?.level.rawValue ?? NSWindow.Level.screenSaver.rawValue
            window.level = NSWindow.Level(rawValue: overlayLevel + 1)
            window.orderFrontRegardless()
        }
        if settingsWindow == nil {
            window.minSize = NSSize(width: 660, height: 380)
            window.maxSize = NSSize(width: 660, height: CGFloat.greatestFiniteMagnitude)
        }
        settingsWindow = window
    }
    /// Clean up settingsWindow reference when the Settings window closes.
@@ -384,8 +395,13 @@
    @objc private func openPreferences() {
        // Don't hide overlay — keep it visible for real-time setting preview.
        // observeOtherWindows handles raising the Settings window above the overlay.
        NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            for window in NSApp.windows where window != self.overlayWindow {
                self.prepareSettingsWindow(window)
            }
        }
    }
    @objc private func switchLanguage(_ sender: NSMenuItem) {
@@ -573,9 +589,10 @@
                            Picker("", selection: $displayMode) {
                                Text(tr("settings.flat")).tag("flat")
                                Text(tr("settings.container")).tag("container")
                                Text(tr("settings.coloredContainer")).tag("coloredContainer")
                            }
                            .pickerStyle(.segmented)
                            .frame(width: 280, alignment: .leading)
                            .frame(width: 360, alignment: .leading)
                            Text(tr("settings.flatDesc"))
                                .font(.caption).foregroundStyle(.secondary)
                                .fixedSize(horizontal: false, vertical: true)