From 6f3ed4f3b8eb45734ca77916b4c96fab937cb2e2 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 31 May 2026 02:58:27 +0800
Subject: [PATCH] Prepare 7.8.7 dock policy release
---
Apptag/OverlayWindowController.swift | 8 ++
Release/AppStore-7.8.7-20260531.0252/README.md | 32 ++++++++
Apptag/ApptagApp.swift | 56 ++++++++++++-
Apptag/ProcessSingleton.swift | 3
Release/AppStore-7.8.7-20260531.0252/APP_STORE_ASSET_INVENTORY.md | 17 ++++
Scripts/window_logic_qa.sh | 73 ++++++++++++++++++
Release/AppStore-7.8.7-20260531.0252/Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg | 0
Apptag/Info.plist | 4
Release/AppStore-7.8.7-20260531.0252/QA_RELEASE_EVIDENCE.md | 39 +++++++++
CHANGELOG.md | 8 ++
10 files changed, 231 insertions(+), 9 deletions(-)
diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index 449efca..7008544 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -38,6 +38,7 @@
private static let downloadHelpMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherDownloadHelpMenuItem")
private static let externalActivationNotification = Notification.Name("TagLauncherExternalActivationRequested")
private static let externalActivationObject = AppIdentity.bundleIdentifier
+ private static let duplicateLaunchSuppressReopenKey = "duplicateLaunchSuppressReopenAt"
private static let launcherOverlayLevel = NSWindow.Level(rawValue: NSWindow.Level.mainMenu.rawValue - 1)
private static let overlayDefaultLevel = launcherOverlayLevel
private static let overlayTextInputLevel = launcherOverlayLevel
@@ -208,11 +209,39 @@
configureApplicationMenuWhenAvailable(retries: 12)
}
- /// Reopening the already-running app must not implicitly show App Grid.
- /// App Grid is only opened by explicit launcher commands: main hotkey or status item/menu.
+ /// Dock icon reopen is an explicit App Grid entry only when the user chooses to show the Dock icon.
+ /// Duplicate-instance handoff suppresses this path so repeated launches do not show App Grid.
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
guard Date() >= suppressReopenUntil else { return false }
+ let lastDuplicateLaunch = UserDefaults.standard.double(forKey: Self.duplicateLaunchSuppressReopenKey)
+ if lastDuplicateLaunch > 0,
+ Date().timeIntervalSince1970 - lastDuplicateLaunch < 2.0 {
+ suppressReopenUntil = Date().addingTimeInterval(1.0)
+ return false
+ }
+ guard UserDefaults.standard.bool(forKey: Self.showDockIconKey) else { return false }
+ guard isPointerNearDockArea() else { return false }
+ showOrFocusOverlay()
return false // Suppress default "unhide all windows" behavior
+ }
+
+ private func isPointerNearDockArea() -> Bool {
+ let mouse = NSEvent.mouseLocation
+ return NSScreen.screens.contains { screen in
+ let frame = screen.frame
+ let visible = screen.visibleFrame
+ guard NSMouseInRect(mouse, frame, false) else { return false }
+ let bottomDock = visible.minY > frame.minY
+ && mouse.y >= frame.minY
+ && mouse.y <= visible.minY + 24
+ let leftDock = visible.minX > frame.minX
+ && mouse.x >= frame.minX
+ && mouse.x <= visible.minX + 24
+ let rightDock = visible.maxX < frame.maxX
+ && mouse.x <= frame.maxX
+ && mouse.x >= visible.maxX - 24
+ return bottomDock || leftDock || rightDock
+ }
}
func applicationWillTerminate(_ notification: Notification) {
@@ -272,11 +301,17 @@
}
private func beginLauncherForegroundOwnership(activate: Bool = true, keyWindow: NSWindow? = nil) {
- if NSApp.activationPolicy() != .regular {
- NSApp.setActivationPolicy(.regular)
+ let showDock = UserDefaults.standard.bool(forKey: Self.showDockIconKey)
+ let desiredPolicy: NSApplication.ActivationPolicy = showDock ? .regular : .accessory
+ if NSApp.activationPolicy() != desiredPolicy {
+ NSApp.setActivationPolicy(desiredPolicy)
}
- if activate {
+ if activate, showDock {
claimLauncherForeground(keyWindow: keyWindow)
+ } else if activate {
+ NSApp.activate(ignoringOtherApps: true)
+ keyWindow?.makeKeyAndOrderFront(nil)
+ keyWindow?.orderFrontRegardless()
}
}
@@ -335,15 +370,18 @@
let shouldStayAccessoryForCurrentFullscreenSpace = isOverlayVisible
&& (avoidSpaceSwitch || overlayAvoidsSpaceSwitch)
+ let shouldStayAccessoryForHiddenDockChrome = requiresForegroundOwnership
+ && !showDock
let shouldStayAccessoryForQuickOnlySearch = isOverlayVisible
&& quickSearchOnlyOverlaySession
&& !showDock
let desiredPolicy: NSApplication.ActivationPolicy = shouldStayAccessoryForCurrentFullscreenSpace
? .accessory
+ : (shouldStayAccessoryForHiddenDockChrome ? .accessory
: (shouldStayAccessoryForQuickOnlySearch ? .accessory
: (requiresForegroundOwnership
? .regular
- : (showDock ? .regular : .accessory)))
+ : (showDock ? .regular : .accessory))))
if NSApp.activationPolicy() != desiredPolicy {
NSApp.setActivationPolicy(desiredPolicy)
}
@@ -355,6 +393,7 @@
if activate && requiresForegroundOwnership
&& !shouldStayAccessoryForCurrentFullscreenSpace
+ && !shouldStayAccessoryForHiddenDockChrome
&& !shouldStayAccessoryForQuickOnlySearch {
let keyWindow = isSettingsVisible ? settingsWindow : (isOverlayVisible ? overlayWindow : nil)
claimLauncherForeground(
@@ -1478,7 +1517,10 @@
@objc private func handleExternalActivationRequest(_ notification: Notification) {
let shouldShowOverlay = notification.userInfo?["showOverlay"] as? Bool ?? false
- guard shouldShowOverlay else { return }
+ guard shouldShowOverlay else {
+ suppressReopenUntil = Date().addingTimeInterval(1.0)
+ return
+ }
showOrFocusOverlay()
}
diff --git a/Apptag/Info.plist b/Apptag/Info.plist
index 153ed6e..1d5c6b7 100644
--- a/Apptag/Info.plist
+++ b/Apptag/Info.plist
@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>7.8.6</string>
+ <string>7.8.7</string>
<key>CFBundleVersion</key>
- <string>20260531.0146</string>
+ <string>20260531.0252</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
diff --git a/Apptag/OverlayWindowController.swift b/Apptag/OverlayWindowController.swift
index 99ac779..1472d2e 100644
--- a/Apptag/OverlayWindowController.swift
+++ b/Apptag/OverlayWindowController.swift
@@ -154,6 +154,14 @@
newWindow.setFrame(placementFrame, display: true)
self.orderFront(newWindow)
}
+ for delay in [0.2, 0.5] {
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self, weak newWindow] in
+ guard let self, let newWindow, self.window === newWindow else { return }
+ guard newWindow.frame != placementFrame else { return }
+ newWindow.setFrame(placementFrame, display: true)
+ self.orderFront(newWindow)
+ }
+ }
if let settingsWindow = self.dependencies.settingsWindow(), settingsWindow.isVisible {
self.dependencies.prepareSettingsWindow(settingsWindow)
}
diff --git a/Apptag/ProcessSingleton.swift b/Apptag/ProcessSingleton.swift
index af0e54b..c2111a2 100644
--- a/Apptag/ProcessSingleton.swift
+++ b/Apptag/ProcessSingleton.swift
@@ -6,6 +6,7 @@
private static let lockURL = AppIdentity.applicationSupportDirectory
.appendingPathComponent("TagLauncher.lock")
private static let activationNotification = Notification.Name("TagLauncherExternalActivationRequested")
+ private static let duplicateLaunchSuppressReopenKey = "duplicateLaunchSuppressReopenAt"
static func acquireOrHandOffAndExit() -> FileHandle {
do {
@@ -34,6 +35,8 @@
let arguments = CommandLine.arguments.dropFirst()
let shouldShowOverlay = arguments.contains("--show-overlay")
if !shouldShowOverlay {
+ UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: duplicateLaunchSuppressReopenKey)
+ UserDefaults.standard.synchronize()
NSApplication.shared.setActivationPolicy(.accessory)
}
let ownerInstance = NSRunningApplication
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 455be64..5b3936c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# TagLauncher Changelog
+## [7.8.7] — 2026-05-31
+
+- 修复关闭“Show in Dock”后用主快捷键打开 App Grid 时 TagLauncher Dock 图标仍会闪现的问题;隐藏 Dock 图标设置现在严格生效
+- 恢复“Show in Dock”开启时点击 Dock 图标打开 App Grid 的交互,同时继续阻止程序化重复启动误唤出 App Grid
+- 加固 overlay 显示后的窗口 frame 复核,避免隐藏 Dock / 多屏 / 全屏场景下系统坐标变化导致 overlay 偏移
+- 窗口 QA 增加 Dock 点击、隐藏 Dock 主快捷键、重复启动不弹 App Grid 的组合回归
+- 版本号更新为 `7.8.7`,Build 更新为 `20260531.0252`
+
## [7.8.6] — 2026-05-31
- 修复未按主快捷键时 App Grid 可能被重复启动或外部激活路径误唤出的问题;App Grid 现在只由主快捷键或状态栏显式命令打开
diff --git a/Release/AppStore-7.8.7-20260531.0252/APP_STORE_ASSET_INVENTORY.md b/Release/AppStore-7.8.7-20260531.0252/APP_STORE_ASSET_INVENTORY.md
new file mode 100644
index 0000000..f154e56
--- /dev/null
+++ b/Release/AppStore-7.8.7-20260531.0252/APP_STORE_ASSET_INVENTORY.md
@@ -0,0 +1,17 @@
+# App Store Asset Inventory
+
+This release pack freezes TagLauncher `7.8.7` build `20260531.0252`.
+
+## Included
+
+- `Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg`
+- `README.md`
+- `QA_RELEASE_EVIDENCE.md`
+- `APP_STORE_ASSET_INVENTORY.md`
+
+## External Items Still Required For App Store Upload
+
+- Apple Developer Bundle ID selection/confirmation in App Store Connect.
+- Mac App Store signing certificate and provisioning profile.
+- Final App Store screenshots and metadata review before submission.
+
diff --git a/Release/AppStore-7.8.7-20260531.0252/Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg b/Release/AppStore-7.8.7-20260531.0252/Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg
new file mode 100644
index 0000000..20d99d2
--- /dev/null
+++ b/Release/AppStore-7.8.7-20260531.0252/Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg
Binary files differ
diff --git a/Release/AppStore-7.8.7-20260531.0252/QA_RELEASE_EVIDENCE.md b/Release/AppStore-7.8.7-20260531.0252/QA_RELEASE_EVIDENCE.md
new file mode 100644
index 0000000..0517b38
--- /dev/null
+++ b/Release/AppStore-7.8.7-20260531.0252/QA_RELEASE_EVIDENCE.md
@@ -0,0 +1,39 @@
+# QA Release Evidence
+
+## Build Under Test
+
+- Version: `7.8.7`
+- Build: `20260531.0252`
+- App: `/Users/ar/Projects/Taglauncher/build/TagLauncher.app`
+- DMG: `/Users/ar/Projects/Taglauncher/build/TagLauncher.dmg`
+- Archived DMG: `Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg`
+- DMG SHA-256: `51cb2861eaf9194dc717cf00b96d3adca490ba89b16438c530b24939a25a94ae`
+
+## Checks
+
+- Version metadata:
+ - `CFBundleShortVersionString = 7.8.7`
+ - `CFBundleVersion = 20260531.0252`
+ - `LSUIElement = true`
+- Code signing:
+ - `codesign --verify --deep --strict --verbose=2 build/TagLauncher.app`
+ - Result: passed
+- Disk image:
+ - `hdiutil verify build/TagLauncher.dmg`
+ - Result: passed
+- Window logic:
+ - `APP_BUILD=20260531.0252 Scripts/window_logic_qa.sh`
+ - Result: `ALL WINDOW LOGIC QA PASSED`
+
+## Targeted Fix Coverage
+
+- `Show in Dock = false`, main hotkey:
+ - Expected: App Grid appears and no TagLauncher Dock tile appears.
+ - Result: passed
+- `Show in Dock = true`, Dock icon click:
+ - Expected: App Grid appears.
+ - Result: passed
+- Duplicate launches:
+ - Expected: one Dock tile and no unexpected App Grid.
+ - Result: passed
+
diff --git a/Release/AppStore-7.8.7-20260531.0252/README.md b/Release/AppStore-7.8.7-20260531.0252/README.md
new file mode 100644
index 0000000..9d42303
--- /dev/null
+++ b/Release/AppStore-7.8.7-20260531.0252/README.md
@@ -0,0 +1,32 @@
+# TagLauncher 7.8.7 Release Pack
+
+This folder freezes TagLauncher `7.8.7` build `20260531.0252` for local QA distribution and Mac App Store submission preparation.
+
+## Release Identity
+
+- Product: `TagLauncher`
+- Version: `7.8.7`
+- Build: `20260531.0252`
+- Branch: `codex/fix-quick-search-focus-routing`
+- Local QA DMG: `Archive/TagLauncher-7.8.7-20260531.0252-local-QA.dmg`
+- DMG SHA-256: `51cb2861eaf9194dc717cf00b96d3adca490ba89b16438c530b24939a25a94ae`
+
+## Changes Since 7.8.6
+
+- Hidden-Dock mode no longer flashes the TagLauncher Dock icon when opening App Grid with the main hotkey.
+- Dock icon click opens App Grid when `Show in Dock` is enabled.
+- Programmatic duplicate launches remain blocked from opening App Grid.
+- Overlay frame is rechecked after show-time system coordinate changes to reduce fullscreen/multi-display drift.
+
+## Verification
+
+- App version metadata verified: `7.8.7 (20260531.0252)`
+- `LSUIElement = true`
+- `codesign --verify --deep --strict --verbose=2 build/TagLauncher.app`: passed
+- `hdiutil verify build/TagLauncher.dmg`: passed
+- `Scripts/window_logic_qa.sh`: passed
+- Targeted checks:
+ - `showDockIcon=false` + main hotkey: App Grid appears, no TagLauncher Dock tile.
+ - `showDockIcon=true` + Dock click: App Grid appears.
+ - repeated launches: one Dock tile, no unexpected App Grid.
+
diff --git a/Scripts/window_logic_qa.sh b/Scripts/window_logic_qa.sh
index 89beba4..4a333b0 100755
--- a/Scripts/window_logic_qa.sh
+++ b/Scripts/window_logic_qa.sh
@@ -278,6 +278,61 @@
log "PASS Dock tile count: $names"
}
+assert_no_dock_tile() {
+ local output count names
+ output="$(osascript <<'OSA'
+tell application "System Events"
+ tell process "Dock"
+ set tagCount to 0
+ set tagNames to {}
+ repeat with itemRef in UI elements of list 1
+ try
+ set itemName to name of itemRef as text
+ if itemName is "TagLauncher" then
+ set tagCount to tagCount + 1
+ set end of tagNames to itemName
+ end if
+ end try
+ end repeat
+ return (tagCount as text) & "|" & (tagNames as text)
+ end tell
+end tell
+OSA
+)"
+ count="${output%%|*}"
+ names="${output#*|}"
+ if [[ "$count" != "0" ]]; then
+ echo "FAIL: expected no TagLauncher Dock tile, got $count ($names)" >&2
+ return 1
+ fi
+ log "PASS no TagLauncher Dock tile"
+}
+
+click_taglauncher_dock_tile() {
+ local coords x y
+ coords="$(osascript <<'OSA'
+tell application "System Events"
+ tell process "Dock"
+ repeat with itemRef in UI elements of list 1
+ try
+ if (name of itemRef as text) is "TagLauncher" then
+ set itemPosition to position of itemRef
+ set itemSize to size of itemRef
+ set centerX to (item 1 of itemPosition) + ((item 1 of itemSize) / 2)
+ set centerY to (item 2 of itemPosition) + ((item 2 of itemSize) / 2)
+ return (centerX as integer as text) & " " & (centerY as integer as text)
+ end if
+ end try
+ end repeat
+ error "TagLauncher Dock tile not found"
+ end tell
+end tell
+OSA
+)"
+ read -r x y <<<"$coords"
+ click_xy "$x" "$y"
+}
+
assert_frontmost_taglauncher() {
local frontmost
frontmost="$(osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true')"
@@ -1071,6 +1126,24 @@
assert_single_qa_app_instance
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
+send_keycode 53
+sleep 0.4
+wait_swift_assert no-overlay
+log "==> QA hidden Dock: main hotkey opens App Grid without showing Dock tile"
+defaults write "$DEFAULTS_DOMAIN" showDockIcon -bool false
+prepare_isolated_app_instance
+assert_no_dock_tile
+send_main_hotkey
+sleep 0.8
+wait_swift_assert overlay
+assert_no_dock_tile
+send_keycode 53
+sleep 0.4
+wait_swift_assert no-overlay
kill_all_taglauncher_instances
sleep 0.4
swift_assert no-overlay
--
Gitblit v1.9.3