Apptag/ApptagApp.swift
@@ -74,6 +74,11 @@ let frame: NSRect } private struct ForeignWindowFrame { let owner: String let frame: NSRect } private var isOverlayVisible: Bool { overlayWindow?.isVisible == true } @@ -882,8 +887,18 @@ private func hasFullscreenWindowOnScreen(_ screen: NSScreen) -> Bool { let screenFrame = screen.frame let windowFrames = foreignLayerZeroWindows(on: screenFrame) if windowFrames.contains(where: { isSingleFullscreenWindow($0.frame, on: screenFrame) }) { return true } return hasSplitViewFullscreenWindows(windowFrames, on: screenFrame) } private func foreignLayerZeroWindows(on screenFrame: NSRect) -> [ForeignWindowFrame] { let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) as? [[String: Any]] ?? [] return windows.contains { info in return windows.compactMap { info in guard let owner = info[kCGWindowOwnerName as String] as? String, owner != AppIdentity.displayName, owner != "Window Server", @@ -892,20 +907,71 @@ let layer = info[kCGWindowLayer as String] as? Int, layer == 0, let bounds = info[kCGWindowBounds as String] as? NSDictionary else { return false } else { return nil } 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 let frame = NSRect( x: cgWindowDimension(bounds, "X"), y: cgWindowDimension(bounds, "Y"), width: cgWindowDimension(bounds, "Width"), height: cgWindowDimension(bounds, "Height") ) guard frame.intersects(screenFrame) else { return nil } return ForeignWindowFrame(owner: owner, frame: frame) } } private func isSingleFullscreenWindow(_ windowFrame: NSRect, on screenFrame: NSRect) -> Bool { let widthMatches = abs(windowFrame.width - screenFrame.width) <= 12 let heightMatches = windowFrame.height >= screenFrame.height * 0.88 let horizontallyAligned = abs(windowFrame.midX - screenFrame.midX) <= 12 let verticallyAligned = abs(windowFrame.maxY - screenFrame.maxY) <= 32 return widthMatches && heightMatches && horizontallyAligned && verticallyAligned } private func hasSplitViewFullscreenWindows(_ windows: [ForeignWindowFrame], on screenFrame: NSRect) -> Bool { let clippedWindows = windows.map { ForeignWindowFrame(owner: $0.owner, frame: $0.frame.intersection(screenFrame)) } let tallWindows = clippedWindows .filter { window in let frame = window.frame return frame.height >= screenFrame.height * 0.86 && frame.width >= screenFrame.width * 0.20 && frame.width <= screenFrame.width * 0.86 && abs(frame.maxY - screenFrame.maxY) <= 32 } .sorted { $0.frame.minX < $1.frame.minX } guard tallWindows.count >= 2 else { return false } for startIndex in tallWindows.indices { var union = tallWindows[startIndex].frame var lastMaxX = union.maxX var distinctOwners = Swift.Set<String>() distinctOwners.insert(tallWindows[startIndex].owner) for window in tallWindows.dropFirst(startIndex + 1) { let gap = window.frame.minX - lastMaxX if gap < -32 || gap > 48 { break } union = union.union(window.frame) lastMaxX = max(lastMaxX, window.frame.maxX) distinctOwners.insert(window.owner) let touchesLeft = abs(union.minX - screenFrame.minX) <= 32 let touchesRight = abs(union.maxX - screenFrame.maxX) <= 32 let coversWidth = union.width >= screenFrame.width * 0.92 let coversHeight = union.height >= screenFrame.height * 0.86 if distinctOwners.count >= 2 && touchesLeft && touchesRight && coversWidth && coversHeight { return true } } } return false } private func cgWindowDimension(_ bounds: NSDictionary, _ key: String) -> CGFloat { if let value = bounds[key] as? CGFloat { return value Release/AppStore-7.6.0-20260524.1652/APP_STORE_CONNECT_METADATA.md
Release/AppStore-7.6.0-20260524.1652/BUILD_SIGN_UPLOAD.md
File was renamed from Release/AppStore-7.6.0-20260523.2335/BUILD_SIGN_UPLOAD.md @@ -50,7 +50,7 @@ APP_STORE=1 \ CODESIGN_IDENTITY="3rd Party Mac Developer Application: <TEAM NAME> (<TEAMID>)" \ APP_BUILD=20260523.2335 \ APP_BUILD=20260524.1652 \ bash build.sh productbuild \ Release/AppStore-7.6.0-20260524.1652/FIRST_TIME_MAC_APP_STORE_TODO.md
Release/AppStore-7.6.0-20260524.1652/QA_RELEASE_EVIDENCE.md
File was renamed from Release/AppStore-7.6.0-20260523.2335/QA_RELEASE_EVIDENCE.md @@ -3,7 +3,7 @@ ## Local Build - Version: `7.6.0` - Build: `20260523.2335` - Build: `20260524.1652` - 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 370adb4ad671ec910cc0edf7e6f1ae3ebdd59cbd063f5d8439f2c7e8b05fd0e1 build/TagLauncher.dmg 9f808b6fc81b587a1508aa31c261cdbe187a3b7727daf8bf163788ba49eb2333 build/TagLauncher.app/Contents/MacOS/TagLauncher 1bf3e53410e18778038c310e1ed083cd89c27b781cc87942dd3dd9fb57d73cb2 build/TagLauncher.dmg 13e74a774662dfec88e69b5f51e5c51c5c46e3e5f8884cbf8cbab5cd44c4442d build/TagLauncher.app/Contents/MacOS/TagLauncher ``` ## Commands Run @@ -42,6 +42,7 @@ - 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. - Split View fullscreen geometry is checked for 50/50 and 33/67 split layouts, while ordinary side-by-side desktop windows are rejected. - 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-20260524.1652/README.md
File was renamed from Release/AppStore-7.6.0-20260523.2335/README.md @@ -8,9 +8,9 @@ - Platform: macOS / Mac App Store - Bundle ID: `com.taglauncher.app` - Version: `7.6.0` - Local QA build: `20260523.2335` - Local QA build: `20260524.1652` - Source branch at preparation time: `main` - Release folder: `Release/AppStore-7.6.0-20260523.2335` - Release folder: `Release/AppStore-7.6.0-20260524.1652` ## What Changed For This Release @@ -22,6 +22,7 @@ - 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. - Added Split View fullscreen detection so two apps sharing one fullscreen Space also keep the app grid on the current 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. Scripts/window_logic_qa.sh
@@ -66,6 +66,11 @@ fi } reset_dock_for_qa() { killall Dock >/dev/null 2>&1 || true sleep 1.5 } send_keycode() { local keycode="$1" local modifiers="${2:-}" @@ -434,6 +439,93 @@ } print("PASS fullscreen overlay above target: overlayLayer=\(tag[0].layer) targetLayer=\(target.layer)") case "split-geometry": func isSingleFullscreenWindow(_ windowFrame: CGRect, on screenFrame: CGRect) -> Bool { let widthMatches = abs(windowFrame.width - screenFrame.width) <= 12 let heightMatches = windowFrame.height >= screenFrame.height * 0.88 let horizontallyAligned = abs(windowFrame.midX - screenFrame.midX) <= 12 let verticallyAligned = abs(windowFrame.maxY - screenFrame.maxY) <= 32 return widthMatches && heightMatches && horizontallyAligned && verticallyAligned } func hasSplitViewFullscreenWindows(_ windows: [CGRect], on screenFrame: CGRect) -> Bool { let clippedWindows = windows.map { $0.intersection(screenFrame) } let tallWindows = clippedWindows .filter { frame in frame.height >= screenFrame.height * 0.86 && frame.width >= screenFrame.width * 0.20 && frame.width <= screenFrame.width * 0.86 && abs(frame.maxY - screenFrame.maxY) <= 32 } .sorted { $0.minX < $1.minX } guard tallWindows.count >= 2 else { return false } for startIndex in tallWindows.indices { var union = tallWindows[startIndex] var lastMaxX = union.maxX for window in tallWindows.dropFirst(startIndex + 1) { let gap = window.minX - lastMaxX if gap < -32 || gap > 48 { break } union = union.union(window) lastMaxX = max(lastMaxX, window.maxX) let touchesLeft = abs(union.minX - screenFrame.minX) <= 32 let touchesRight = abs(union.maxX - screenFrame.maxX) <= 32 let coversWidth = union.width >= screenFrame.width * 0.92 let coversHeight = union.height >= screenFrame.height * 0.86 if touchesLeft && touchesRight && coversWidth && coversHeight { return true } } } return false } let screen = CGRect(x: 0, y: 0, width: 1710, height: 1112) let single = CGRect(x: 0, y: 39, width: 1710, height: 1073) let splitHalf = [ CGRect(x: 0, y: 39, width: 853, height: 1073), CGRect(x: 857, y: 39, width: 853, height: 1073) ] let splitThird = [ CGRect(x: 0, y: 39, width: 568, height: 1073), CGRect(x: 572, y: 39, width: 1138, height: 1073) ] let desktopTiledWithLargeGap = [ CGRect(x: 0, y: 90, width: 700, height: 900), CGRect(x: 900, y: 90, width: 700, height: 900) ] let desktopSideBySideBelowDock = [ CGRect(x: 0, y: 39, width: 856, height: 983), CGRect(x: 854, y: 39, width: 856, height: 983) ] let halfOnly = [CGRect(x: 0, y: 39, width: 853, height: 1073)] guard isSingleFullscreenWindow(single, on: screen) else { fail("split geometry expected single fullscreen window to match") } guard hasSplitViewFullscreenWindows(splitHalf, on: screen) else { fail("split geometry expected 50/50 Split View to match") } guard hasSplitViewFullscreenWindows(splitThird, on: screen) else { fail("split geometry expected 33/67 Split View to match") } guard !hasSplitViewFullscreenWindows(desktopTiledWithLargeGap, on: screen) else { fail("split geometry should not match ordinary tiled desktop windows") } guard !hasSplitViewFullscreenWindows(desktopSideBySideBelowDock, on: screen) else { fail("split geometry should not match side-by-side desktop windows below the Dock") } guard !hasSplitViewFullscreenWindows(halfOnly, on: screen) else { fail("split geometry should not match a single half-width window") } print("PASS split fullscreen geometry detection") case "screen-count": print("INFO screens=\(NSScreen.screens.count) frames=\(NSScreen.screens.map { NSStringFromRect($0.frame) })") @@ -665,6 +757,7 @@ log "==> Preparing QA defaults" defaults write "$DEFAULTS_DOMAIN" showDockIcon -bool true reset_dock_for_qa log "==> Starting clean app instance" prepare_isolated_app_instance @@ -692,6 +785,9 @@ sleep 0.4 wait_swift_assert no-overlay log "==> QA split-view fullscreen geometry detection" swift_assert split-geometry run_fullscreen_space_case() { local dock_value="$1" log "==> QA fullscreen Space: overlay stays above the current fullscreen app (showDockIcon=$dock_value)"