| Apptag/ContentView.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/DataLayer.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/Info.plist | ●●●●● 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/ContentView.swift
@@ -376,6 +376,7 @@ @State private var refreshInProgress = false @State private var refreshAgainAfterCurrent = false @State private var refreshAgainForceLayout = false @State private var refreshAgainUseCache = true @FocusState private var bubbleNoteFocused: Bool // Quick Search @@ -532,7 +533,7 @@ } .onAppear { refreshNotchHeight() refreshAppsIfNeeded() refreshAppsForOverlay() if let initialQuickSearchSource, !initialQuickSearchConsumed { initialQuickSearchConsumed = true if initialQuickSearchSource == QuickSearchOpenSource.globalHidden { @@ -545,6 +546,7 @@ } quickSearchOpenedAt = Date() refreshQuickSearchResults() refreshAppsForQuickSearch() NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, object: nil, @@ -558,7 +560,7 @@ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidShow)) { _ in resetTransientDragState() refreshNotchHeight() refreshAppsIfNeeded() refreshAppsForOverlay() } .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidHide)) { _ in resetTransientDragState() @@ -676,7 +678,7 @@ selectedID: quickSearchSelectedID, focusToken: quickSearchFocusToken, selectionScrollToken: quickSearchSelectionScrollToken, isLoading: quickSearchDocuments.isEmpty && refreshInProgress, isLoading: quickSearchShouldShowLoading, maxVisibleRows: quickSearchMaxVisibleRows(in: proxy.size), panelTopY: quickSearchPanelTopY(in: proxy.size), panelHeight: quickSearchPanelContentHeight(in: proxy.size), @@ -695,7 +697,7 @@ } private func quickSearchPanelContentHeight(in size: CGSize) -> CGFloat { let hasResultList = !(quickSearchDocuments.isEmpty && refreshInProgress) let hasResultList = !quickSearchShouldShowLoading && quickSearchErrorMessage == nil && !quickSearchResults.isEmpty let visibleRows = min(max(1, quickSearchResults.count), quickSearchMaxVisibleRows(in: size)) @@ -735,7 +737,7 @@ quickSearchErrorMessage = nil quickSearchFocusToken &+= 1 refreshQuickSearchResults() refreshApps() refreshAppsForQuickSearch() NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, object: nil, @@ -752,6 +754,10 @@ && smartStartNotice == nil && !appGridInteraction.dropRefreshVisible && !quickSearchVisible } private var quickSearchShouldShowLoading: Bool { quickSearchVisible && refreshInProgress && quickSearchResults.isEmpty } private func closeQuickSearch( @@ -1807,6 +1813,16 @@ refreshApps() } private func refreshAppsForOverlay() { guard AppIndexer.shouldRefreshForSearchPathChanges() else { return } refreshApps() } private func refreshAppsForQuickSearch() { guard AppIndexer.shouldRefreshForSearchPathChanges() else { return } refreshApps() } private func refreshNotchHeight() { let mousePoint = NSEvent.mouseLocation let activeScreen = NSScreen.screens.first(where: { @@ -2417,10 +2433,9 @@ func refreshApps(forceLayoutRefresh: Bool = false, useCache: Bool = true) { guard !refreshInProgress else { if forceLayoutRefresh { refreshAgainAfterCurrent = true refreshAgainForceLayout = true } refreshAgainAfterCurrent = true refreshAgainForceLayout = refreshAgainForceLayout || forceLayoutRefresh refreshAgainUseCache = refreshAgainUseCache && useCache return } @@ -2436,9 +2451,11 @@ refreshInProgress = false if refreshAgainAfterCurrent { let shouldForceLayout = refreshAgainForceLayout let shouldUseCache = refreshAgainUseCache refreshAgainAfterCurrent = false refreshAgainForceLayout = false refreshApps(forceLayoutRefresh: shouldForceLayout) refreshAgainUseCache = true refreshApps(forceLayoutRefresh: shouldForceLayout, useCache: shouldUseCache) } } } Apptag/DataLayer.swift
@@ -145,6 +145,16 @@ scanCacheLock.unlock() } static func shouldRefreshForSearchPathChanges() -> Bool { scanCacheLock.lock() let hasCachedApps = cachedScanApps != nil let cachedSignature = cachedSearchPathSignature scanCacheLock.unlock() guard hasCachedApps else { return true } return cachedSignature != currentSearchPathSignature() } private static func cachedScanIfFresh(matching searchPathSignature: [SearchPathSignature]) -> [AppInfo]? { scanCacheLock.lock() defer { scanCacheLock.unlock() } Apptag/Info.plist
@@ -19,9 +19,9 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>7.8.12</string> <string>7.8.13</string> <key>CFBundleVersion</key> <string>20260604.0105</string> <string>20260604.1121</string> <key>LSApplicationCategoryType</key> <string>public.app-category.utilities</string> <key>LSMinimumSystemVersion</key> CHANGELOG.md
@@ -1,5 +1,13 @@ # TagLauncher Changelog ## [7.8.13] — 2026-06-04 - 修复 App 运行期间新安装应用后,App Grid 和 Quick Search 仍复用旧应用索引、无法立即检索到新应用的问题 - App Grid / Quick Search 打开时会轻量检查标准应用目录签名;签名未变化时不刷新也不重建界面,只有目录变化时才后台重扫 - 刷新过程中再次打开 App Grid 或 Quick Search 会合并一个后续刷新请求,避免新安装发生在刷新窗口内被漏掉 - 窗口 QA 对 Dock 点击打开、App Grid 内打开 Quick Search 和 Quick Search 结果坐标读取增加短重试,避免系统事件或窗口列表瞬时抖动导致误判 - 版本号更新为 `7.8.13`,Build 更新为 `20260604.1121` ## [7.8.12] — 2026-06-04 - 修复 Quick Search-only 模式刚打开后可能被过期鼠标关闭事件立即关掉的问题;现在 `Fn + Space` 打开后候选面板会保持稳定,键盘取消和再次按 `Fn + Space` 仍可立即关闭 Scripts/window_logic_qa.sh
@@ -349,6 +349,20 @@ click_xy "$x" "$y" } open_overlay_from_dock_with_retry() { local output="" for _ in {1..3}; do click_taglauncher_dock_tile sleep 0.8 if output="$(wait_swift_assert overlay 2>&1)"; then printf '%s\n' "$output" return 0 fi done printf '%s\n' "$output" >&2 return 1 } assert_frontmost_taglauncher() { local frontmost frontmost="$(osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true')" @@ -792,10 +806,10 @@ return name.isEmpty && width >= 500 && width <= 900 && height >= 120 && height >= 320 && height <= 850 }), let bounds = quickSearch[kCGWindowBounds as String] as? NSDictionary else { fputs("FAIL: could not find quick search bounds\n", stderr) fputs("FAIL: could not find quick search result-list bounds\n", stderr) exit(1) } let x = dimension(bounds, "X") @@ -1029,16 +1043,37 @@ return 1 } open_appgrid_quick_search_with_retry() { local output="" for _ in {1..3}; do send_keycode 49 sleep 0.4 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="" local consecutive_successes=0 for _ in {1..20}; do if ! output="$(swift "$assert_swift" fullscreen-overlay 2>&1)"; then printf '%s\n' "$output" >&2 return 1 if output="$(swift "$assert_swift" fullscreen-overlay 2>&1)"; then consecutive_successes=$((consecutive_successes + 1)) if [[ "$consecutive_successes" -ge 6 ]]; then printf '%s\n' "$output" return 0 fi else consecutive_successes=0 fi sleep 0.1 done printf '%s\n' "$output" printf '%s\n' "$output" >&2 return 1 } show_overlay() { @@ -1077,9 +1112,9 @@ hover_quick_search_results() { local coords x y coords="$(swift "$coords_swift" quick-search-result)" coords="$(quick_search_result_coords_with_retry)" read -r x y <<<"$coords" for offset in 0 42 84 126 84 42 0; do for offset in 0 8 16 8 0; do move_xy "$x" "$((y + offset))" sleep 0.08 done @@ -1087,9 +1122,22 @@ click_quick_search_result() { local coords x y coords="$(swift "$coords_swift" quick-search-result)" coords="$(quick_search_result_coords_with_retry)" read -r x y <<<"$coords" click_xy "$x" "$y" } quick_search_result_coords_with_retry() { local output for _ in {1..8}; do if output="$(swift "$coords_swift" quick-search-result 2>&1)"; then printf '%s\n' "$output" return 0 fi sleep 0.15 done printf '%s\n' "$output" >&2 return 1 } page_scroll_appgrid() { @@ -1193,9 +1241,7 @@ assert_single_dock_tile swift_assert no-overlay log "==> QA Dock reopen: showDockIcon=true opens App Grid from explicit app reopen" click_taglauncher_dock_tile sleep 1.0 wait_swift_assert overlay open_overlay_from_dock_with_retry send_keycode 53 sleep 0.4 wait_swift_assert no-overlay @@ -1254,9 +1300,7 @@ wait_swift_assert fullscreen-overlay assert_fullscreen_overlay_stable log "==> QA fullscreen Space: quick search from appgrid does not switch Space" send_keycode 49 sleep 0.4 wait_swift_assert quick-search open_appgrid_quick_search_with_retry assert_fullscreen_overlay_stable send_keycode 53 sleep 0.3 @@ -1288,8 +1332,7 @@ swift_assert overlay log "==> QA 1/7 and 4/7: settings floats above appgrid and quick search" send_keycode 49 sleep 0.4 open_appgrid_quick_search_with_retry send_cmd_comma sleep 0.7 swift_assert settings @@ -1308,8 +1351,7 @@ assert_frontmost_taglauncher log "==> QA 4/7 and 5/7: appgrid-space quick search and double-Esc behavior" send_keycode 49 sleep 0.4 open_appgrid_quick_search_with_retry send_keycode 53 sleep 0.4 swift_assert overlay @@ -1321,9 +1363,7 @@ show_overlay for _ in {1..5}; do page_scroll_appgrid send_keycode 49 sleep 0.35 swift_assert quick-search open_appgrid_quick_search_with_retry send_keycode 53 sleep 0.25 swift_assert overlay @@ -1335,8 +1375,7 @@ log "==> QA 5/7: clicking outside quick search closes search, not appgrid" show_overlay send_keycode 49 sleep 0.4 open_appgrid_quick_search_with_retry click_overlay_outside_quick_search sleep 0.4 swift_assert overlay TODO.md
@@ -23,6 +23,12 @@ ## Done - [2026-06-04] 修复新安装 App 后 App Grid / Quick Search 不能立即检索到的问题。 - 提交: 本次提交 - 结果: App Grid / Quick Search 打开时先轻量检查标准应用目录签名;签名未变化时不刷新也不重建界面,目录变化时才后台重扫应用索引。 - 范围: 不改搜索匹配规则、不改 App Grid 布局、不改窗口层级策略;只收敛应用索引刷新触发条件和 QA 脚本稳定性。 - 验证: 本地 `7.8.13 (20260604.1121)` targeted 输入 `notch` 命中 `TheBoringNotch` 且无“未找到”;`APP_BUILD=20260604.1121 zsh ./build.sh`、签名/版本校验、`Scripts/window_logic_qa.sh` 全部通过。 - [2026-06-04] 修复 Quick Search 刚打开即被过期 dismiss 事件关闭的 QA 失败。 - 提交: 本次提交 - 结果: quick-search-only 模式会忽略打开初期的过期鼠标/背板关闭事件;键盘 Esc 和再次按 `Fn + Space` 仍立即关闭;QA 脚本对合成 `Fn + Space` 投递增加重试,并把结果点击坐标修正到首条结果行中心。