Apptag/ApptagApp.swift
@@ -258,13 +258,18 @@ } } private func refreshLauncherChromeState(activate: Bool = false) { private func refreshLauncherChromeState(activate: Bool = false, avoidSpaceSwitch: Bool = false) { let showDock = UserDefaults.standard.bool(forKey: Self.showDockIconKey) lastShowDockIcon = showDock let desiredPolicy: NSApplication.ActivationPolicy = requiresForegroundOwnership let shouldStayAccessoryForCurrentFullscreenSpace = avoidSpaceSwitch && isOverlayVisible && !isSettingsVisible let desiredPolicy: NSApplication.ActivationPolicy = shouldStayAccessoryForCurrentFullscreenSpace ? .accessory : (requiresForegroundOwnership ? .regular : (showDock ? .regular : .accessory) : (showDock ? .regular : .accessory)) if NSApp.activationPolicy() != desiredPolicy { NSApp.setActivationPolicy(desiredPolicy) } @@ -274,7 +279,7 @@ NSApp.presentationOptions = desiredPresentation } if activate && requiresForegroundOwnership { if activate && requiresForegroundOwnership && !shouldStayAccessoryForCurrentFullscreenSpace { let keyWindow = isSettingsVisible ? settingsWindow : (isOverlayVisible ? overlayWindow : nil) claimLauncherForeground( keyWindow: keyWindow, @@ -749,10 +754,12 @@ } private func showOrFocusOverlay(preferredScreen: NSScreen? = nil) { let placement = overlayPlacementContextForNextOverlay(preferredScreen: preferredScreen) let shouldAvoidSpaceSwitch = placement.map { hasFullscreenWindowOnScreen($0.screen) } ?? false if let overlayWindow, overlayWindow.isVisible { moveOverlayToCurrentPlacement(preferredScreen: preferredScreen) overlayWindow.level = currentOverlayLevel refreshLauncherChromeState(activate: true) refreshLauncherChromeState(activate: !shouldAvoidSpaceSwitch, avoidSpaceSwitch: shouldAvoidSpaceSwitch) overlayWindow.makeKeyAndOrderFront(nil) overlayWindow.orderFrontRegardless() if let settingsWindow, settingsWindow.isVisible { @@ -778,7 +785,8 @@ ) { guard let placement = overlayPlacementContextForNextOverlay(preferredScreen: preferredScreen) else { return } let shouldStageAsAccessory = shouldStageOverlayAsAccessory let shouldAvoidSpaceSwitch = hasFullscreenWindowOnScreen(placement.screen) let shouldStageAsAccessory = shouldStageOverlayAsAccessory || shouldAvoidSpaceSwitch if !stagedForAllSpaces && shouldStageAsAccessory { // Let the all-spaces panel attach to the pointer's display before the app reclaims focus. @@ -831,7 +839,7 @@ let placementFrame = placement.frame let finishForegroundClaim: () -> Void = { [weak self, weak window] in guard let self, let window, self.overlayWindow === window else { return } self.refreshLauncherChromeState(activate: true) self.refreshLauncherChromeState(activate: !shouldAvoidSpaceSwitch, avoidSpaceSwitch: shouldAvoidSpaceSwitch) if window.frame != placementFrame { window.setFrame(placementFrame, display: true) window.makeKeyAndOrderFront(nil) @@ -848,6 +856,42 @@ } else { finishForegroundClaim() } } private func hasFullscreenWindowOnScreen(_ screen: NSScreen) -> Bool { let screenFrame = screen.frame let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) as? [[String: Any]] ?? [] return windows.contains { info in guard let owner = info[kCGWindowOwnerName as String] as? String, owner != AppIdentity.displayName, owner != "Window Server", owner != "Dock", owner != "loginwindow", let layer = info[kCGWindowLayer as String] as? Int, layer == 0, let bounds = info[kCGWindowBounds as String] as? NSDictionary else { return false } let x = cgWindowDimension(bounds, "X") let y = cgWindowDimension(bounds, "Y") let width = cgWindowDimension(bounds, "Width") let height = cgWindowDimension(bounds, "Height") let windowFrame = NSRect(x: x, y: y, width: width, height: height) let widthMatches = abs(windowFrame.width - screenFrame.width) <= 12 let heightMatches = windowFrame.height >= screenFrame.height * 0.88 let horizontallyAligned = abs(windowFrame.midX - screenFrame.midX) <= 12 return widthMatches && heightMatches && horizontallyAligned } } private func cgWindowDimension(_ bounds: NSDictionary, _ key: String) -> CGFloat { if let value = bounds[key] as? CGFloat { return value } if let value = bounds[key] as? NSNumber { return CGFloat(truncating: value) } return 0 } private func installOverlayKeyMonitor() { @@ -1119,6 +1163,11 @@ return } if self.isAppOwnedDocumentWindow(keyWindow) { self.prepareSettingsWindow(keyWindow) return } // Settings/Preferences window -> float it above overlay for real-time preview. if self.isSettingsWindowCandidate(keyWindow) { self.prepareSettingsWindow(keyWindow) @@ -1176,6 +1225,7 @@ private func isSettingsWindowCandidate(_ window: NSWindow) -> Bool { if window == settingsWindow { return true } if window.identifier?.rawValue == "TagLauncherPreferencesWindow" { return true } if isAppOwnedDocumentWindow(window) { return true } guard NSApp.windows.contains(window), window != overlayWindow, window.isVisible, @@ -1184,6 +1234,13 @@ return settingsWindowTitleCandidates().contains(normalizedWindowTitle(window.title)) } private func isAppOwnedDocumentWindow(_ window: NSWindow) -> Bool { NSApp.windows.contains(window) && window != overlayWindow && window.isVisible && !(window is NSPanel) } private func settingsWindowTitleCandidates() -> Set<String> { let keys = [ "menu.preferences", Release/AppStore-7.6.0-20260523.2335/APP_STORE_CONNECT_METADATA.md
Release/AppStore-7.6.0-20260523.2335/BUILD_SIGN_UPLOAD.md
File was renamed from Release/AppStore-7.6.0-20260523.1803/BUILD_SIGN_UPLOAD.md @@ -50,7 +50,7 @@ APP_STORE=1 \ CODESIGN_IDENTITY="3rd Party Mac Developer Application: <TEAM NAME> (<TEAMID>)" \ APP_BUILD=20260523.1803 \ APP_BUILD=20260523.2335 \ bash build.sh productbuild \ Release/AppStore-7.6.0-20260523.2335/FIRST_TIME_MAC_APP_STORE_TODO.md
Release/AppStore-7.6.0-20260523.2335/QA_RELEASE_EVIDENCE.md
File was renamed from Release/AppStore-7.6.0-20260523.1803/QA_RELEASE_EVIDENCE.md @@ -3,7 +3,7 @@ ## Local Build - Version: `7.6.0` - Build: `20260523.1803` - Build: `20260523.2335` - App path: `/Users/ar/Projects/Taglauncher/build/TagLauncher.app` - DMG path: `/Users/ar/Projects/Taglauncher/build/TagLauncher.dmg` - App executable size: `1.4M` @@ -12,8 +12,8 @@ ## Hashes ```text c848f902a8219547b59767b172d5b26453748e7f34ee1ce7d10c421fc61005de build/TagLauncher.dmg 8a541e00734f0bd5f461b13881e194b29cb9c33b79fc0203cd59864680928d4e build/TagLauncher.app/Contents/MacOS/TagLauncher 370adb4ad671ec910cc0edf7e6f1ae3ebdd59cbd063f5d8439f2c7e8b05fd0e1 build/TagLauncher.dmg 9f808b6fc81b587a1508aa31c261cdbe187a3b7727daf8bf163788ba49eb2333 build/TagLauncher.app/Contents/MacOS/TagLauncher ``` ## Commands Run @@ -40,6 +40,8 @@ - Repeated self-launch keeps exactly one process. - Repeated self-launch keeps exactly one Dock tile. - Fullscreen Space overlay appears above the current fullscreen app. - Fullscreen Space overlay is checked with `showDockIcon=true` and `showDockIcon=false`. - Fullscreen Space overlay bounds must match the fullscreen target display and remain stable across repeated samples. - App grid claims foreground, hides Dock, and keeps menu bar visible. - Settings floats above app grid. - File panel floats above Settings. Release/AppStore-7.6.0-20260523.2335/README.md
File was renamed from Release/AppStore-7.6.0-20260523.1803/README.md @@ -8,9 +8,9 @@ - Platform: macOS / Mac App Store - Bundle ID: `com.taglauncher.app` - Version: `7.6.0` - Local QA build: `20260523.1803` - Local QA build: `20260523.2335` - Source branch at preparation time: `main` - Release folder: `Release/AppStore-7.6.0-20260523.1803` - Release folder: `Release/AppStore-7.6.0-20260523.2335` ## What Changed For This Release @@ -21,6 +21,8 @@ - Removed the invalid all-spaces + move-to-active-space window behavior combination that caused the 07:23 crash. - Removed the broken macOS system Help menu entry. - Added an About-page Help PDF button that opens the online PDF matching the current app language, plus a copy-link button. - Hardened fullscreen Space presentation so the app grid stays above the current fullscreen app instead of moving to another Space. - Expanded window QA to cover fullscreen Space behavior with Dock icon both enabled and disabled, including stability checks. - Strengthened the window logic QA script to cover fullscreen Space, duplicate Dock tiles, repeated self-launch, force quit layering, Settings/file panel layering, Quick Search, and two-display pointer-following behavior. ## Folder Contents Scripts/window_logic_qa.sh
@@ -417,6 +417,17 @@ guard let target = windows.first(where: { $0.name == "TagLauncherFullscreenQATargetFullscreen" }) else { fail("fullscreen target disappeared; TagLauncher likely switched to another Space") } let overlayWidth = dimension(tag[0].bounds, "Width") let overlayHeight = dimension(tag[0].bounds, "Height") let overlayMidX = dimension(tag[0].bounds, "X") + overlayWidth / 2 let targetWidth = dimension(target.bounds, "Width") let targetHeight = dimension(target.bounds, "Height") let targetMidX = dimension(target.bounds, "X") + targetWidth / 2 guard abs(overlayWidth - targetWidth) <= 12, overlayHeight >= targetHeight * 0.88, abs(overlayMidX - targetMidX) <= 12 else { fail("fullscreen overlay is not on the target fullscreen display: overlay=\(tag[0].bounds) target=\(target.bounds)") } guard tag[0].layer > target.layer else { fail("TagLauncher layer \(tag[0].layer) is not above fullscreen target layer \(target.layer)") } @@ -562,6 +573,18 @@ return 1 } assert_fullscreen_overlay_stable() { local output="" for _ in {1..20}; do if ! output="$(swift "$assert_swift" fullscreen-overlay 2>&1)"; then printf '%s\n' "$output" >&2 return 1 fi sleep 0.1 done printf '%s\n' "$output" } show_overlay() { send_main_hotkey if wait_swift_assert overlay >/dev/null 2>&1; then @@ -650,16 +673,27 @@ sleep 0.4 wait_swift_assert no-overlay log "==> QA fullscreen Space: overlay stays above the current fullscreen app" start_fullscreen_qa_target send_main_hotkey wait_swift_assert fullscreen-overlay frontmost="$(osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true')" [[ "$frontmost" == "TagLauncher" ]] || { echo "FAIL: fullscreen overlay frontmost app is $frontmost, expected TagLauncher" >&2; exit 1; } send_keycode 53 sleep 0.4 wait_swift_assert no-overlay kill_fullscreen_qa_target run_fullscreen_space_case() { local dock_value="$1" log "==> QA fullscreen Space: overlay stays above the current fullscreen app (showDockIcon=$dock_value)" defaults write "$DEFAULTS_DOMAIN" showDockIcon -bool "$dock_value" prepare_isolated_app_instance start_fullscreen_qa_target send_main_hotkey wait_swift_assert fullscreen-overlay assert_fullscreen_overlay_stable send_keycode 53 sleep 0.4 wait_swift_assert no-overlay kill_fullscreen_qa_target } run_fullscreen_space_case true run_fullscreen_space_case false log "==> Restoring dock-visible QA app instance for remaining checks" defaults write "$DEFAULTS_DOMAIN" showDockIcon -bool true prepare_isolated_app_instance log "==> QA 1/7: overlay claims foreground, hides Dock, keeps menu bar visible" show_overlay