| Apptag/ApptagApp.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/ContentView.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/Info.plist | ●●●●● patch | view | raw | blame | history | |
| Apptag/QuickSearch.swift | ●●●●● patch | view | raw | blame | history | |
| CHANGELOG.md | ●●●●● patch | view | raw | blame | history | |
| Scripts/window_logic_qa.sh | ●●●●● patch | view | raw | blame | history | |
| TODO.md | ●●●●● patch | view | raw | blame | history |
Apptag/ApptagApp.swift
@@ -430,7 +430,11 @@ } if isQuickSearchOpen { NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.programmatic] ) } if !requiresForegroundOwnership { @@ -987,7 +991,11 @@ isQuickSearchOpen = false removeQuickSearchExternalMouseMonitor() updateOverlayLevelForTextInput() NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.keyboard] ) if shouldHideOverlayAfterDismiss { quickSearchShouldHideOverlayOnClose = false quickSearchOnlyOverlaySession = false @@ -1526,7 +1534,11 @@ ) { [weak self] _ in DispatchQueue.main.async { guard self?.isQuickSearchOpen == true else { return } NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.mouse] ) } } } @@ -1538,7 +1550,11 @@ ) { [weak self] event in guard let self, self.isQuickSearchOpen else { return event } if event.window === self.overlayWindow { NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.mouse] ) return nil } return event @@ -1633,7 +1649,11 @@ updateOverlayLevelForTextInput() quickSearchShouldHideOverlayOnClose = false quickSearchOnlyOverlaySession = false NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.programmatic] ) } @objc private func switchLanguage(_ sender: NSMenuItem) { @@ -1695,7 +1715,11 @@ } if hit == self { if quickSearchSuppressesBackdropDismiss { NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) NotificationCenter.default.post( name: .tagLauncherQuickSearchDismissRequested, object: nil, userInfo: ["source": QuickSearchDismissSource.backdrop] ) return } if suppressBackdropDismiss { Apptag/ContentView.swift
@@ -389,6 +389,7 @@ @State private var quickSearchSelectionScrollToken = 0 @State private var quickSearchCloseHidesOverlay = false @State private var quickSearchOnlySession = false @State private var quickSearchOpenedAt: Date? = nil @State private var quickSearchErrorMessage: String? = nil @State private var initialQuickSearchConsumed = false @@ -542,6 +543,7 @@ quickSearchVisible = true quickSearchFocusToken &+= 1 } quickSearchOpenedAt = Date() refreshQuickSearchResults() NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, @@ -568,8 +570,9 @@ let source = notification.userInfo?["source"] as? String ?? QuickSearchOpenSource.mainOverlay openQuickSearch(source: source) } .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchDismissRequested)) { _ in closeQuickSearch() .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchDismissRequested)) { notification in let source = notification.userInfo?["source"] as? String closeQuickSearch(dismissalSource: source) } .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in guard allApps.isEmpty, !refreshInProgress else { return } @@ -663,7 +666,7 @@ .ignoresSafeArea() .contentShape(Rectangle()) .onTapGesture { closeQuickSearch() closeQuickSearch(dismissalSource: QuickSearchDismissSource.backdrop) } .zIndex(899) @@ -726,6 +729,7 @@ quickSearchCloseHidesOverlay = quickSearchCloseHidesOverlay || source == QuickSearchOpenSource.globalHidden quickSearchVisible = true quickSearchOpenedAt = Date() quickSearchQuery = "" quickSearchManualSelection = false quickSearchErrorMessage = nil @@ -750,10 +754,26 @@ && !quickSearchVisible } private func closeQuickSearch(notify: Bool = true, hideOverlayIfNeeded: Bool = true) { private func closeQuickSearch( notify: Bool = true, hideOverlayIfNeeded: Bool = true, dismissalSource: String? = nil ) { Diagnostics.log("content.quickSearch.closeRequest", [ "source": dismissalSource, "notify": notify, "hideOverlayIfNeeded": hideOverlayIfNeeded, "quickSearchVisible": quickSearchVisible, "quickSearchCloseHidesOverlay": quickSearchCloseHidesOverlay, "quickSearchOnlySession": quickSearchOnlySession ]) if shouldIgnoreEarlyQuickSearchDismiss(from: dismissalSource) { return } let shouldHideOverlay = quickSearchVisible && quickSearchCloseHidesOverlay && hideOverlayIfNeeded guard quickSearchVisible else { return } quickSearchVisible = false quickSearchOpenedAt = nil quickSearchQuery = "" quickSearchResults = [] quickSearchSelectedID = nil @@ -774,6 +794,23 @@ } else { quickSearchCloseHidesOverlay = false } } private func shouldIgnoreEarlyQuickSearchDismiss(from source: String?) -> Bool { guard quickSearchVisible, let quickSearchOpenedAt, source == QuickSearchDismissSource.mouse || source == QuickSearchDismissSource.backdrop else { return false } let age = Date().timeIntervalSince(quickSearchOpenedAt) let gracePeriod: TimeInterval = quickSearchOnlySession ? 1.25 : 0.30 guard age < gracePeriod else { return false } Diagnostics.log("content.quickSearch.ignoredEarlyDismiss", [ "source": source, "age": String(format: "%.3f", age), "gracePeriod": String(format: "%.2f", gracePeriod) ]) return true } private func refreshQuickSearchResults() { @@ -800,6 +837,11 @@ } private func handleQuickSearchCommand(_ command: QuickSearchCommand) { Diagnostics.log("content.quickSearch.command", [ "command": String(describing: command), "quickSearchCloseHidesOverlay": quickSearchCloseHidesOverlay, "quickSearchOnlySession": quickSearchOnlySession ]) switch command { case .moveUp: moveQuickSearchSelection(by: -1) Apptag/Info.plist
@@ -19,9 +19,9 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>7.8.11</string> <string>7.8.12</string> <key>CFBundleVersion</key> <string>20260603.2149</string> <string>20260604.0105</string> <key>LSApplicationCategoryType</key> <string>public.app-category.utilities</string> <key>LSMinimumSystemVersion</key> Apptag/QuickSearch.swift
@@ -15,6 +15,13 @@ static let globalVisible = "globalVisible" } enum QuickSearchDismissSource { static let keyboard = "keyboard" static let mouse = "mouse" static let backdrop = "backdrop" static let programmatic = "programmatic" } // MARK: - Hotkeys struct LauncherHotkey: Equatable { CHANGELOG.md
@@ -1,5 +1,12 @@ # TagLauncher Changelog ## [7.8.12] — 2026-06-04 - 修复 Quick Search-only 模式刚打开后可能被过期鼠标关闭事件立即关掉的问题;现在 `Fn + Space` 打开后候选面板会保持稳定,键盘取消和再次按 `Fn + Space` 仍可立即关闭 - 窗口 QA 增加对合成 `Fn + Space` 投递的重试,并把 Quick Search 结果点击坐标修正到首条结果行中心,避免测试误点面板背景 - 本次只收敛 Quick Search 打开/关闭时序和 QA 覆盖,不改搜索匹配、App Grid 布局、Settings、文件面板或窗口层级策略 - 版本号更新为 `7.8.12`,Build 更新为 `20260604.0105` ## [7.8.11] — 2026-06-03 - 优化 Quick Search 结果行右侧标签显示:短标签完整显示,右侧标签列右对齐并减少不必要留白 Scripts/window_logic_qa.sh
@@ -801,7 +801,7 @@ let x = dimension(bounds, "X") let y = dimension(bounds, "Y") let width = dimension(bounds, "Width") print("\(Int(round(x + width * 0.35))) \(Int(round(y + 125)))") print("\(Int(round(x + width * 0.35))) \(Int(round(y + 190)))") case "fullscreen-target-center": guard let target = raw.first(where: { ($0[kCGWindowName as String] as? String) == "TagLauncherFullscreenQATargetFullscreen" }), let bounds = target[kCGWindowBounds as String] as? NSDictionary else { @@ -1015,6 +1015,20 @@ return 1 } open_quick_search_with_retry() { local output="" for _ in {1..3}; do send_quick_search_hotkey sleep 0.8 if output="$(wait_swift_assert quick-search 2>&1)"; then printf '%s\n' "$output" return 0 fi done printf '%s\n' "$output" >&2 return 1 } assert_fullscreen_overlay_stable() { local output="" for _ in {1..20}; do @@ -1202,9 +1216,7 @@ prepare_isolated_app_instance assert_no_dock_tile log "==> QA hidden Dock: Fn+Space Quick Search hover does not resurrect App Grid" send_quick_search_hotkey sleep 0.8 wait_swift_assert quick-search open_quick_search_with_retry hover_quick_search_results sleep 0.8 wait_swift_assert quick-search @@ -1219,9 +1231,7 @@ prepare_isolated_app_instance assert_no_dock_tile log "==> QA hidden Dock: clicking Quick Search result closes without showing App Grid" send_quick_search_hotkey sleep 0.8 wait_swift_assert quick-search open_quick_search_with_retry click_quick_search_result sleep 1.4 wait_swift_assert no-overlay TODO.md
@@ -23,6 +23,12 @@ ## Done - [2026-06-04] 修复 Quick Search 刚打开即被过期 dismiss 事件关闭的 QA 失败。 - 提交: 本次提交 - 结果: quick-search-only 模式会忽略打开初期的过期鼠标/背板关闭事件;键盘 Esc 和再次按 `Fn + Space` 仍立即关闭;QA 脚本对合成 `Fn + Space` 投递增加重试,并把结果点击坐标修正到首条结果行中心。 - 范围: 不改搜索匹配、App Grid 布局、Settings、文件面板或窗口层级策略。 - 验证: targeted 两段式 `Fn + Space` 打开/关闭通过;targeted 点击 Quick Search 结果后窗口数为 0;`Scripts/window_logic_qa.sh` 全部通过。 - [2026-05-29] 版本拆分与 7.8.1 默认图标大小调整。 - 提交: 本次提交 - 结果: `CHANGELOG.md` 已补充 `7.7.0`、`7.8.0`、`7.8.1` 三段版本记录;源码版本号更新为 `7.8.1`;新用户默认 `iconSize` 从 `80` 调整为 `64`。