Ariver
2026-05-06 e64cdcb8e172e212bd38b8bd5cec06befa337f7c
Apptag/ApptagApp.swift
@@ -24,8 +24,9 @@
    private var isInEditMode = false  // Suppress auto-dismiss during editing
    func applicationDidFinishLaunching(_ notification: Notification) {
        // Use .accessory so the app appears in Force Quit (unlike LSUIElement)
        NSApp.setActivationPolicy(.accessory)
        L10n.setup()
        let showDock = UserDefaults.standard.bool(forKey: "showDockIcon")
        NSApp.setActivationPolicy(showDock ? .regular : .accessory)
        setupMenuBar()
        registerHotkey()
        observeOtherWindows()
@@ -35,6 +36,10 @@
    // MARK: - Menu Bar
    private func setupMenuBar() {
        // Remove old status item if re-creating (language switch, etc.)
        if let existing = statusItem {
            NSStatusBar.system.removeStatusItem(existing)
        }
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        if let button = statusItem.button {
@@ -50,7 +55,7 @@
        let menu = NSMenu()
        menu.addItem(
            NSMenuItem(
                title: "Show Apptag",
                title: tr("menu.show"),
                action: #selector(toggleOverlay),
                keyEquivalent: ""
            )
@@ -61,7 +66,7 @@
        let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?"
        let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "?"
        let versionItem = NSMenuItem(
            title: "Version \(appVersion) (\(buildNumber))",
            title: "\(tr("menu.version")) \(appVersion) (\(buildNumber))",
            action: nil,
            keyEquivalent: ""
        )
@@ -70,16 +75,31 @@
        menu.addItem(.separator())
        let prefsItem = NSMenuItem(
            title: "Preferences...",
            title: tr("menu.preferences"),
            action: #selector(openPreferences),
            keyEquivalent: ","
        )
        prefsItem.keyEquivalentModifierMask = .command
        menu.addItem(prefsItem)
        menu.addItem(.separator())
        // Language submenu
        let langMenu = NSMenu()
        let currentLang = L10n.currentCode
        for (code, name) in L10n.supported {
            let item = NSMenuItem(title: name, action: #selector(switchLanguage(_:)), keyEquivalent: "")
            item.representedObject = code
            item.state = (code == currentLang) ? .on : .off
            langMenu.addItem(item)
        }
        let langItem = NSMenuItem(title: tr("menu.language"), action: nil, keyEquivalent: "")
        langItem.submenu = langMenu
        menu.addItem(langItem)
        menu.addItem(.separator())
        menu.addItem(
            NSMenuItem(
                title: "Quit",
                title: tr("menu.quit"),
                action: #selector(NSApplication.terminate(_:)),
                keyEquivalent: "q"
            )
@@ -237,6 +257,13 @@
        hideOverlay()
        NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
    }
    @objc private func switchLanguage(_ sender: NSMenuItem) {
        guard let code = sender.representedObject as? String else { return }
        L10n.switchTo(code)
        // Rebuild menu to update checkmarks
        setupMenuBar()
    }
}
// MARK: - NSView-level backdrop dismiss (works even if SwiftUI rendering is slow)
@@ -319,6 +346,9 @@
    @AppStorage("iconSize") private var iconSize: Double = 56
    @AppStorage("tagPosition") private var tagPosition = "left"
    @AppStorage("defaultGroupName") private var defaultGroupName = "Other"
    @AppStorage("displayMode") private var displayMode = "flat"
    @AppStorage("hideAppNames") private var hideAppNames = false
    @AppStorage("showDockIcon") private var showDockIcon = false
    @State private var allApps: [AppInfo] = []
    @State private var tagColors: [String: Int] = [:]
@@ -385,6 +415,20 @@
            // Tab 1: General
            Form {
                Section {
                    LabeledContent("App list style:") {
                        Picker("", selection: $displayMode) {
                            Text("Flat").tag("flat")
                            Text("Container").tag("container")
                        }
                        .pickerStyle(.segmented)
                        .frame(width: 210)
                    }
                    Text("\"Flat\" shows apps directly. \"Container\" wraps each tag group in a rounded box.")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                Section {
                    LabeledContent("Tag position:") {
                        Picker("", selection: $tagPosition) {
                            Text("Left").tag("left")
@@ -412,8 +456,6 @@
                    Text("Adjust the size of group-name labels in the overlay.")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                } header: {
                    Text("Appearance")
                }
                Section {
@@ -429,8 +471,14 @@
                    Text("Icon display size. Grid columns adjust automatically.")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                } header: {
                    Text("Layout")
                }
                Section {
                    Toggle("Show in Dock", isOn: $showDockIcon)
                }
                Section {
                    Toggle("Hide app names", isOn: $hideAppNames)
                }
            }
            .tabItem { Label("General", systemImage: "gearshape") }