Ariver
2026-06-30 c5165bad3e6d375756df9eb03155aa779f1903af
src/Scripts/window_logic_qa.sh
@@ -128,12 +128,46 @@
  if [[ -n "$modifiers" ]]; then
    osascript -e "tell application \"System Events\" to key code $keycode using {$modifiers}"
  else
    osascript -e "tell application \"System Events\" to key code $keycode"
    swift - "$keycode" <<'SWIFT' >/dev/null 2>&1
import CoreGraphics
import Foundation
let keyCode = CGKeyCode(UInt16(CommandLine.arguments[1]) ?? 0)
let source = CGEventSource(stateID: .hidSystemState)
let down = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: true)!
down.post(tap: .cghidEventTap)
usleep(80_000)
let up = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: false)!
up.post(tap: .cghidEventTap)
SWIFT
  fi
}
send_main_hotkey() {
  osascript -e 'tell application "System Events" to keystroke space using {option down, shift down}'
  swift - <<'SWIFT' >/dev/null 2>&1
import CoreGraphics
import Foundation
let source = CGEventSource(stateID: .hidSystemState)
let leftShift = CGKeyCode(56)
let leftOption = CGKeyCode(58)
let space = CGKeyCode(49)
func post(_ keyCode: CGKeyCode, keyDown: Bool, flags: CGEventFlags = []) {
    let event = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: keyDown)!
    event.flags = flags
    event.post(tap: .cghidEventTap)
    usleep(30_000)
}
post(leftOption, keyDown: true, flags: [.maskAlternate])
post(leftShift, keyDown: true, flags: [.maskAlternate, .maskShift])
post(space, keyDown: true, flags: [.maskAlternate, .maskShift])
usleep(80_000)
post(space, keyDown: false, flags: [.maskAlternate, .maskShift])
post(leftShift, keyDown: false, flags: [.maskAlternate])
post(leftOption, keyDown: false)
SWIFT
}
send_quick_search_hotkey() {
@@ -165,14 +199,20 @@
tell application "System Events"
  tell process "TagLauncher"
    repeat 20 times
      set didClose to false
      repeat with windowRef in windows
        try
          if (name of windowRef as text) is not "" then
            click button 1 of windowRef
            return
            perform action "AXPress" of button 1 of windowRef
            set didClose to true
          end if
        end try
      end repeat
      if didClose then
        delay 0.2
      else
        return
      end if
      delay 0.1
    end repeat
  end tell
@@ -308,84 +348,39 @@
}
assert_single_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#*|}"
  local output matching count
  output="$(swift "$dock_tiles_swift" "$APP_BUNDLE")"
  matching="$(printf '%s\n' "$output" | awk -F'|' '$2 == "MATCH" { print }')"
  count="$(printf '%s\n' "$matching" | sed '/^$/d' | wc -l | tr -d ' ')"
  if [[ "$count" != "1" ]]; then
    echo "FAIL: expected exactly one TagLauncher Dock tile, got $count ($names)" >&2
    echo "FAIL: expected exactly one QA build TagLauncher Dock tile, got $count" >&2
    printf '%s\n' "$output" >&2
    return 1
  fi
  log "PASS Dock tile count: $names"
  log "PASS Dock tile count: $matching"
}
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#*|}"
  local output matching count
  output="$(swift "$dock_tiles_swift" "$APP_BUNDLE")"
  matching="$(printf '%s\n' "$output" | awk -F'|' '$2 == "MATCH" { print }')"
  count="$(printf '%s\n' "$matching" | sed '/^$/d' | wc -l | tr -d ' ')"
  if [[ "$count" != "0" ]]; then
    echo "FAIL: expected no TagLauncher Dock tile, got $count ($names)" >&2
    echo "FAIL: expected no QA build TagLauncher Dock tile, got $count" >&2
    printf '%s\n' "$output" >&2
    return 1
  fi
  log "PASS no TagLauncher Dock tile"
}
taglauncher_dock_tile_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
  local line
  line="$(swift "$dock_tiles_swift" "$APP_BUNDLE" | awk -F'|' '$2 == "MATCH" { print; exit }')"
  if [[ -z "$line" ]]; then
    echo "TagLauncher QA build Dock tile not found" >&2
    return 1
  fi
  awk -F'|' '{ print $5 " " $6 }' <<<"$line"
}
clamped_click_coords() {
@@ -420,20 +415,7 @@
  fi
  click_xy "$x" "$y"
  sleep 0.15
  osascript <<'OSA' >/dev/null 2>&1 || true
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
          click itemRef
          return
        end if
      end try
    end repeat
  end tell
end tell
OSA
  swift "$dock_tiles_swift" "$APP_BUNDLE" press >/dev/null 2>&1 || true
}
open_overlay_from_dock_with_retry() {
@@ -471,6 +453,7 @@
  open -n "$APP_BUNDLE"
  sleep 1.0
  dismiss_reopen_dialog
  close_settings_window
  sleep 2.0
  if ! is_qa_app_only_running; then
@@ -534,7 +517,8 @@
settings_ax_swift="$(mktemp -t taglauncher-settings-ax.XXXXXX.swift)"
screens_swift="$(mktemp -t taglauncher-screens.XXXXXX.swift)"
fullscreen_swift="$(mktemp -t taglauncher-fullscreen-target.XXXXXX.swift)"
trap 'cleanup; rm -f "$assert_swift" "$coords_swift" "$settings_ax_swift" "$screens_swift" "$fullscreen_swift" "$LAUNCH_AGENT_BACKUP" "$STORE_BACKUP"' EXIT
dock_tiles_swift="$(mktemp -t taglauncher-dock-tiles.XXXXXX.swift)"
trap 'cleanup; rm -f "$assert_swift" "$coords_swift" "$settings_ax_swift" "$screens_swift" "$fullscreen_swift" "$dock_tiles_swift" "$LAUNCH_AGENT_BACKUP" "$STORE_BACKUP"' EXIT
cat >"$assert_swift" <<'SWIFT'
import AppKit
@@ -749,12 +733,31 @@
    }
    print("PASS fullscreen overlay above target: tagLayers=\(tag.map(\.layer)) targetLayer=\(target.layer)")
case "fullscreen-overlay-frame":
    guard tag.count == 1 || tag.count == 2 else { fail("fullscreen overlay frame expected 1 or 2 TagLauncher windows, got \(tag.count)") }
    assertTagLayer(tag)
    guard let overlay = tag.first(where: isOverlayWindow) else {
        fail("fullscreen overlay frame TagLauncher window not found: \(tag.map(\.bounds))")
    }
    let quickSearch = tag.filter(isQuickSearchWindow)
    guard quickSearch.count == tag.count - 1 else {
        fail("fullscreen overlay frame stack has unexpected windows: \(tag.map(\.bounds))")
    }
    let overlayWidth = dimension(overlay.bounds, "Width")
    let overlayHeight = dimension(overlay.bounds, "Height")
    let screenMatch = NSScreen.screens.contains { screen in
        abs(overlayWidth - screen.frame.width) <= 12
            && overlayHeight >= screen.frame.height * 0.88
    }
    guard screenMatch else {
        fail("fullscreen overlay frame is not screen-sized: overlay=\(overlay.bounds)")
    }
    print("PASS fullscreen overlay frame stable: tagLayers=\(tag.map(\.layer))")
case "fullscreen-settings":
    guard tag.count == 2 else { fail("fullscreen settings expected 2 TagLauncher windows, got \(tag.count)") }
    assertTagLayer(tag)
    guard let target = windows.first(where: { $0.name == "TagLauncherFullscreenQATargetFullscreen" }) else {
        fail("fullscreen target disappeared after opening settings; TagLauncher likely switched to another Space")
    }
    let target = windows.first(where: { $0.name == "TagLauncherFullscreenQATargetFullscreen" })
    let overlays = tag.filter(isOverlayWindow)
    let settings = tag.filter(isSettingsLikeWindow)
    guard overlays.count == 1, settings.count == 1 else {
@@ -763,10 +766,13 @@
    guard !tag[0].name.isEmpty else {
        fail("fullscreen settings order wrong: \(tag.map(\.name))")
    }
    guard tag.allSatisfy({ $0.layer > target.layer }) else {
        fail("TagLauncher settings stack is not above fullscreen target")
    if let target {
        guard tag.allSatisfy({ $0.layer > target.layer }) else {
            fail("TagLauncher settings stack is not above fullscreen target")
        }
    }
    print("PASS fullscreen settings above target: tagLayers=\(tag.map(\.layer)) targetLayer=\(target.layer)")
    let targetLayerDescription = target?.layer.description ?? "occluded"
    print("PASS fullscreen settings above target: tagLayers=\(tag.map(\.layer)) targetLayer=\(targetLayerDescription)")
case "split-geometry":
    func isSingleFullscreenWindow(_ windowFrame: CGRect, on screenFrame: CGRect) -> Bool {
@@ -860,6 +866,114 @@
default:
    fail("unknown assert mode: \(mode)")
}
SWIFT
cat >"$dock_tiles_swift" <<'SWIFT'
import AppKit
import ApplicationServices
import Foundation
let targetURL = URL(fileURLWithPath: CommandLine.arguments[1]).standardizedFileURL
let action = CommandLine.arguments.dropFirst(2).first ?? "list"
let dockPid = NSWorkspace.shared.runningApplications.first {
    $0.bundleIdentifier == "com.apple.dock"
}?.processIdentifier ?? 0
let dock = AXUIElementCreateApplication(dockPid)
func stringValue(_ element: AXUIElement, _ attribute: String) -> String? {
    var value: CFTypeRef?
    guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success else {
        return nil
    }
    return value as? String
}
func urlValue(_ element: AXUIElement, _ attribute: String) -> URL? {
    var value: CFTypeRef?
    guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success,
          let value else {
        return nil
    }
    if let url = value as? URL {
        return url.standardizedFileURL
    }
    if let url = value as? NSURL {
        return (url as URL).standardizedFileURL
    }
    if let string = value as? String {
        return URL(string: string)?.standardizedFileURL
    }
    return nil
}
func boolValue(_ element: AXUIElement, _ attribute: String) -> Bool {
    var value: CFTypeRef?
    guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success else {
        return false
    }
    return (value as? Bool) ?? false
}
func pointValue(_ element: AXUIElement, _ attribute: String) -> CGPoint? {
    var value: CFTypeRef?
    guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success,
          let axValue = value as! AXValue?,
          AXValueGetType(axValue) == .cgPoint else {
        return nil
    }
    var point = CGPoint.zero
    AXValueGetValue(axValue, .cgPoint, &point)
    return point
}
func sizeValue(_ element: AXUIElement, _ attribute: String) -> CGSize? {
    var value: CFTypeRef?
    guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success,
          let axValue = value as! AXValue?,
          AXValueGetType(axValue) == .cgSize else {
        return nil
    }
    var size = CGSize.zero
    AXValueGetValue(axValue, .cgSize, &size)
    return size
}
var childrenValue: CFTypeRef?
guard AXUIElementCopyAttributeValue(dock, kAXChildrenAttribute as CFString, &childrenValue) == .success,
      let children = childrenValue as? [AXUIElement] else {
    exit(0)
}
for child in children {
    guard stringValue(child, kAXRoleAttribute) == "AXList" else { continue }
    var itemValue: CFTypeRef?
    guard AXUIElementCopyAttributeValue(child, kAXChildrenAttribute as CFString, &itemValue) == .success,
          let items = itemValue as? [AXUIElement] else {
        continue
    }
    for (index, item) in items.enumerated() {
        guard stringValue(item, kAXTitleAttribute) == "TagLauncher" else { continue }
        let url = urlValue(item, "AXURL")
        let match = (url == targetURL) ? "MATCH" : "OTHER"
        let running = boolValue(item, "AXIsApplicationRunning") ? "running" : "idle"
        let position = pointValue(item, kAXPositionAttribute) ?? .zero
        let size = sizeValue(item, kAXSizeAttribute) ?? .zero
        let centerX = Int(round(position.x + size.width / 2))
        let centerY = Int(round(position.y + size.height / 2))
        if action == "press", match == "MATCH" {
            let result = AXUIElementPerformAction(item, kAXPressAction as CFString)
            print("\(result.rawValue)|\(url?.path ?? "")")
            exit(result == .success ? 0 : 1)
        }
        guard action == "list" else {
            continue
        }
        print("\(index + 1)|\(match)|\(url?.path ?? "")|\(running)|\(centerX)|\(centerY)")
    }
}
if action == "press" {
    exit(1)
}
SWIFT
@@ -1263,8 +1377,16 @@
assert_fullscreen_overlay_stable() {
  local output=""
  local consecutive_successes=0
  local strict_verified=false
  for _ in {1..20}; do
    if output="$(swift "$assert_swift" fullscreen-overlay 2>&1)"; then
      strict_verified=true
      consecutive_successes=$((consecutive_successes + 1))
      if [[ "$consecutive_successes" -ge 6 ]]; then
        printf '%s\n' "$output"
        return 0
      fi
    elif [[ "$strict_verified" == true ]] && output="$(swift "$assert_swift" fullscreen-overlay-frame 2>&1)"; then
      consecutive_successes=$((consecutive_successes + 1))
      if [[ "$consecutive_successes" -ge 6 ]]; then
        printf '%s\n' "$output"
@@ -1437,6 +1559,8 @@
[[ "$plist_multiple" == "true" ]] || { echo "FAIL: LSMultipleInstancesProhibited is not true in built Info.plist" >&2; exit 1; }
rg -Fq 'isTagLauncherBundle' "$ROOT_DIR/Apptag/DataLayer.swift"
rg -Fq 'guard !isTagLauncherBundle(bundleId)' "$ROOT_DIR/Apptag/DataLayer.swift"
move_xy 200 200
sleep 1.0
for _ in {1..5}; do
  open -n "$APP_BUNDLE"
  sleep 0.35
@@ -1523,14 +1647,14 @@
  assert_fullscreen_overlay_stable
  send_keycode 53
  sleep 0.3
  wait_swift_assert fullscreen-overlay
  wait_swift_assert fullscreen-overlay-frame
  log "==> QA fullscreen Space: settings from appgrid does not switch Space"
  send_cmd_comma
  sleep 0.7
  wait_swift_assert fullscreen-settings
  close_settings_window
  sleep 0.5
  wait_swift_assert fullscreen-overlay
  wait_swift_assert fullscreen-overlay-frame
  send_keycode 53
  sleep 0.4
  wait_swift_assert no-overlay