Ariver
2026-05-18 9d6f138e40a5322ee731f8096eaa3332c94a6532
Speed up repeated app list opens
6 files modified
99 ■■■■ changed files
Apptag/ApptagApp.swift 19 ●●●●● patch | view | raw | blame | history
Apptag/ContentView.swift 26 ●●●● patch | view | raw | blame | history
Apptag/DataLayer.swift 41 ●●●●● patch | view | raw | blame | history
Apptag/Info.plist 4 ●●●● patch | view | raw | blame | history
Apptag/PreferencesView.swift 2 ●●● patch | view | raw | blame | history
CHANGELOG.md 7 ●●●●● patch | view | raw | blame | history
Apptag/ApptagApp.swift
@@ -499,15 +499,22 @@
            NSMouseInRect(mousePoint, $0.frame, false)
        }) ?? NSScreen.main ?? NSScreen.screens.first else { return }
        overlayWindow?.orderOut(nil)
        overlayWindow = makeOverlayWindow(on: screen)
        overlayWindow?.setFrame(screen.frame, display: true)
        overlayWindow?.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
        let window: NSWindow
        if let existingWindow = overlayWindow {
            window = existingWindow
        } else {
            window = makeOverlayWindow(on: screen)
            overlayWindow = window
        }
        window.setFrame(screen.frame, display: true)
        window.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
        installOverlayKeyMonitor()
        overlayWindow?.makeKeyAndOrderFront(nil)
        overlayWindow?.orderFrontRegardless()
        window.makeKeyAndOrderFront(nil)
        window.orderFrontRegardless()
        NotificationCenter.default.post(name: .tagLauncherOverlayDidShow, object: nil)
    }
    private func installOverlayKeyMonitor() {
Apptag/ContentView.swift
@@ -9,6 +9,7 @@
    static let tagLauncherAppNoteEditingChanged = Notification.Name("TagLauncherAppNoteEditingChanged")
    static let tagLauncherDataDidChange = Notification.Name("TagLauncherDataDidChange")
    static let tagLauncherOpenPreferencesRequested = Notification.Name("TagLauncherOpenPreferencesRequested")
    static let tagLauncherOverlayDidShow = Notification.Name("TagLauncherOverlayDidShow")
}
// MARK: - Edit Phase
@@ -483,14 +484,15 @@
            }
        }
        .onAppear {
            let mousePoint = NSEvent.mouseLocation
            let activeScreen = NSScreen.screens.first(where: {
                NSMouseInRect(mousePoint, $0.frame, false)
            }) ?? NSScreen.main
            notchHeight = activeScreen?.safeAreaInsets.top ?? 0
            refreshNotchHeight()
            refreshApps()
        }
        .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidShow)) { _ in
            refreshNotchHeight()
            refreshApps()
        }
        .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
            guard allApps.isEmpty, !refreshInProgress else { return }
            refreshApps()
        }
        .onReceive(NotificationCenter.default.publisher(for: .tagLauncherDataDidChange)) { _ in
@@ -1853,6 +1855,14 @@
    // MARK: - Actions
    private func refreshNotchHeight() {
        let mousePoint = NSEvent.mouseLocation
        let activeScreen = NSScreen.screens.first(where: {
            NSMouseInRect(mousePoint, $0.frame, false)
        }) ?? NSScreen.main
        notchHeight = activeScreen?.safeAreaInsets.top ?? 0
    }
    private func handleBubbleHover(app: AppInfo, frame: CGRect, hovering: Bool) {
        guard !appBubbleDisabled else {
            clearAppBubbleState()
@@ -2222,8 +2232,10 @@
    func refreshApps(forceLayoutRefresh: Bool = false) {
        guard !refreshInProgress else {
            refreshAgainAfterCurrent = true
            refreshAgainForceLayout = refreshAgainForceLayout || forceLayoutRefresh
            if forceLayoutRefresh {
                refreshAgainAfterCurrent = true
                refreshAgainForceLayout = true
            }
            return
        }
Apptag/DataLayer.swift
@@ -51,6 +51,10 @@
// MARK: - App Scanner
enum AppIndexer {
    private static let scanCacheLock = NSLock()
    private static var cachedScanApps: [AppInfo]? = nil
    private static var cachedScanAt: Date? = nil
    private static let scanCacheTTL: TimeInterval = 1800
    static let searchPaths: [URL] = [
        URL(fileURLWithPath: "/Applications"),
@@ -72,7 +76,42 @@
    }
    /// Scan all standard locations. Tags are annotated from TagDatabase by the caller.
    static func scan() -> [AppInfo] {
    static func scan(useCache: Bool = true) -> [AppInfo] {
        if useCache, let cached = cachedScanIfFresh() {
            return cached
        }
        let apps = performScan()
        updateScanCache(apps)
        return apps
    }
    static func invalidateScanCache() {
        scanCacheLock.lock()
        cachedScanApps = nil
        cachedScanAt = nil
        scanCacheLock.unlock()
    }
    private static func cachedScanIfFresh() -> [AppInfo]? {
        scanCacheLock.lock()
        defer { scanCacheLock.unlock() }
        guard let cachedScanApps,
              let cachedScanAt,
              Date().timeIntervalSince(cachedScanAt) < scanCacheTTL
        else { return nil }
        return cachedScanApps
    }
    private static func updateScanCache(_ apps: [AppInfo]) {
        scanCacheLock.lock()
        cachedScanApps = apps
        cachedScanAt = Date()
        scanCacheLock.unlock()
    }
    private static func performScan() -> [AppInfo] {
        var seenResolvedPaths = Set<String>()
        var apps: [AppInfo] = []
Apptag/Info.plist
@@ -19,9 +19,9 @@
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>7.3.3</string>
    <string>7.3.4</string>
    <key>CFBundleVersion</key>
    <string>742</string>
    <string>743</string>
    <key>LSMinimumSystemVersion</key>
    <string>15.0</string>
    <key>NSHighResolutionCapable</key>
Apptag/PreferencesView.swift
@@ -140,7 +140,7 @@
        isApplyingSystemScheme = true
        DispatchQueue.global(qos: .userInitiated).async {
            let scannedApps = AppIndexer.scan()
            let scannedApps = AppIndexer.scan(useCache: false)
            let result = SmartStartService.applySystemInitialScheme(apps: scannedApps)
            let store = result.store
            let apps = TagEditor.annotate(apps: scannedApps, store: store)
CHANGELOG.md
@@ -1,5 +1,12 @@
# Apptag Changelog
## [7.3.4] — 2026-05-18
- 优化应用列表打开速度:覆盖层窗口不再每次显示都销毁重建,减少重复初始化
- App 扫描增加会话缓存,避免短时间内反复加载全部应用图标导致每次打开都长时间等待
- 避免应用激活通知和首次显示同时触发重复全量刷新;刷新进行中时只保留必要的强制刷新请求
- 版本号更新为 `7.3.4`,Build 更新为 `743`
## [7.3.3] — 2026-05-18
- 拖拽 App 时会立即关闭并禁用 App 用途气泡,避免拖向目标容器时旧气泡继续遮挡视线