From bcceca83ce966598e00e0319d85ec11d6df9d272 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 30 Jun 2026 03:44:54 +0800
Subject: [PATCH] Freeze TagLauncher 8.2.8 build 20260630.0128
---
src/Scripts/pro_custom_hotkeys_qa.sh | 150 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 147 insertions(+), 3 deletions(-)
diff --git a/src/Scripts/pro_custom_hotkeys_qa.sh b/src/Scripts/pro_custom_hotkeys_qa.sh
index bb797a4..a70de65 100755
--- a/src/Scripts/pro_custom_hotkeys_qa.sh
+++ b/src/Scripts/pro_custom_hotkeys_qa.sh
@@ -98,6 +98,13 @@
require(settings, "modifiers == UInt32(kEventKeyModifierFnMask)", "Fn-only must be restricted explicitly")
require(settings, "hotkey.keyCode == UInt32(kVK_Space)", "Fn-only must only be valid for Space")
require(settings, "isReservedBySystem", "system-reserved shortcuts must be rejected")
+require(settings, "modifiers == UInt32(shiftKey)", "Shift-only must remain rejected")
+if "modifiers == UInt32(shiftKey) || modifiers == UInt32(optionKey)" in settings:
+ fail("Option-only shortcuts such as Option+Space must be allowed")
+require(settings, "private static let functionKeyCodes: Set<Int>", "function keys must use an explicit Carbon key-code set")
+require(settings, "functionKeyCodes.contains(key)", "supported-key validation must check the explicit function key set")
+if "kVK_F1...kVK_F12" in settings:
+ fail("Carbon function key codes are not contiguous; kVK_F1...kVK_F12 crashes at runtime")
effective_body = function_body(settings, "static func effectiveHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkey")
if "validationError(for:" in effective_body:
@@ -131,11 +138,30 @@
require(activate_body, "return .registrationFailed(status)", "registration failure must not save the candidate")
require(app, "observeProEntitlementChanges()", "AppDelegate must observe Pro entitlement changes")
+require(app, "static private(set) weak var shared: AppDelegate?", "Preferences must have a stable AppDelegate bridge for custom hotkey writes")
+require(app, "Self.shared = self", "AppDelegate must publish its stable shared bridge during initialization")
+require(app, "private var configuredHotkeysSuspendedForRecording = false", "AppDelegate must track hotkey recording suspension")
require(app, "registerConfiguredHotkeys()", "Pro entitlement changes must re-register effective hotkeys")
require(app, "LauncherHotkeySettings.effectiveHotkey(for: .main).displayString", "menu title must display the effective App Grid shortcut")
require(app, "LauncherHotkeySettings.effectiveHotkey(for: kind)", "hotkey registration must read effective shortcuts")
require(app, "RegisterEventHotKey", "Carbon RegisterEventHotKey must remain the global hotkey backend")
require(app, "UnregisterEventHotKey(oldRef)", "old hotkey ref must be released after successful replacement")
+
+register_configured_body = function_body(app, "private func registerConfiguredHotkeys()")
+require(register_configured_body, "guard !configuredHotkeysSuspendedForRecording else { return }", "configured hotkeys must stay unregistered while recording")
+
+suspend_body = function_body(app, "func suspendConfiguredHotkeysForRecording()")
+require(suspend_body, "guard !configuredHotkeysSuspendedForRecording else { return }", "recording suspension must be idempotent")
+require(suspend_body, "configuredHotkeysSuspendedForRecording = true", "recording suspension must set the guard flag")
+require(suspend_body, "unregisterHotkey(for: .main)", "recording must suspend the App Grid global hotkey")
+require(suspend_body, "unregisterHotkey(for: .quickSearch)", "recording must suspend the Quick Search global hotkey")
+if "LauncherHotkeySettings.save" in suspend_body or "clearCustomHotkey" in suspend_body:
+ fail("recording suspension must not write custom hotkey storage")
+
+resume_body = function_body(app, "func resumeConfiguredHotkeysAfterRecording()")
+require(resume_body, "guard configuredHotkeysSuspendedForRecording else { return }", "recording resume must be idempotent")
+require(resume_body, "configuredHotkeysSuspendedForRecording = false", "recording resume must clear the guard flag")
+require(resume_body, "registerConfiguredHotkeys()", "recording resume must restore effective hotkeys")
for forbidden in ["CGEventTapCreate", "AXIsProcessTrusted", "IOHIDManagerCreate", "addGlobalMonitorForEvents(matching: .keyDown"]:
if forbidden in app + preferences + settings + quick_search:
@@ -145,14 +171,40 @@
require(preferences, "NSEvent.addLocalMonitorForEvents(matching: .keyDown)", "recording must use local key-down capture in Settings")
require(preferences, "Int(event.keyCode) == kVK_Escape", "Esc must cancel recording")
require(preferences, "LauncherHotkeySettings.candidate(from: event)", "recording must normalize NSEvent into LauncherHotkey")
+require(preferences, "AppDelegate.shared?", "recording must use the stable AppDelegate bridge instead of NSApp.delegate casting")
+require(preferences, "appDelegate.suspendConfiguredHotkeysForRecording()", "recording must suspend existing global hotkeys before capture")
+require(preferences, "AppDelegate.shared?.resumeConfiguredHotkeysAfterRecording()", "recording cleanup must restore effective global hotkeys")
require(preferences, ".applyCustomHotkey(candidate, for: kind)", "recording must call the AppDelegate transactional save path")
require(preferences, "CustomizableHotkeyInfoRow", "Settings hotkey rows must expose custom controls")
require(preferences, "StaticHotkeyInfoRow(", "internal App Grid Space shortcut must remain static")
require(preferences, "LauncherHotkeySettings.effectiveHotkey(for: .main).displayString", "Settings must display effective App Grid shortcut")
require(preferences, "LauncherHotkeySettings.effectiveHotkey(for: .quickSearch).displayString", "Settings must display effective Quick Search shortcut")
require(preferences, "HotkeyPrimaryActionButton", "custom hotkey rows must use the shared action button")
-require(preferences, "ProStatusPill(text: title, style: .locked, compact: true)", "locked custom hotkey action must use the global Pro pill")
+require(preferences, 'customizeTitle: tr("hotkeys.customize")', "locked custom hotkey actions must still show the action label")
+require(preferences, 'proBadgeTitle: tr("pro.card.badge")', "locked custom hotkey actions must compose the global Pro badge beside the action label")
+require(preferences, "ProStatusPill(text: proBadgeTitle, style: .locked, compact: true)", "locked custom hotkey action must use the global Pro pill")
require(preferences, "if canRestore {", "restore-default action must only appear when a custom shortcut exists")
+
+start_recording_body = function_body(preferences, "private func startHotkeyRecording(for kind: LauncherHotkeyKind)")
+suspend_index = start_recording_body.find("appDelegate.suspendConfiguredHotkeysForRecording()")
+monitor_index = start_recording_body.find("NSEvent.addLocalMonitorForEvents(matching: .keyDown)")
+if suspend_index == -1 or monitor_index == -1 or not suspend_index < monitor_index:
+ fail("recording must suspend global hotkeys before installing the local monitor")
+
+stop_recording_body = function_body(preferences, "private func stopHotkeyRecording(showCancelledToast: Bool = false)")
+require(stop_recording_body, "let wasRecording", "recording cleanup must only resume when a recording was active")
+require(stop_recording_body, "NSEvent.removeMonitor(hotkeyRecorderMonitor)", "recording cleanup must remove the local monitor")
+require(stop_recording_body, "recordingHotkeyKind = nil", "recording cleanup must clear recording state")
+require(stop_recording_body, "AppDelegate.shared?.resumeConfiguredHotkeysAfterRecording()", "recording cleanup must restore global hotkeys")
+
+capture_recording_body = function_body(preferences, "private func captureHotkeyEvent(_ event: NSEvent)")
+stop_index = capture_recording_body.find("stopHotkeyRecording(showCancelledToast: false)")
+handle_index = capture_recording_body.find("handleHotkeyCustomizationResult(result)")
+if stop_index == -1 or handle_index == -1 or not stop_index < handle_index:
+ fail("recording capture must restore global hotkeys before showing save/failure feedback")
+
+require(preferences, "if newTab != .hotkeys", "leaving the Hotkeys tab must end recording")
+require(preferences, "stopHotkeyRecording(showCancelledToast: false)", "recording must clean up on tab change, Pro changes, and view disappearance")
custom_row_start = preferences.find("private struct CustomizableHotkeyInfoRow")
custom_row_end = preferences.find("private struct HotkeyStatusPill")
@@ -162,8 +214,15 @@
if ".buttonStyle(.borderedProminent)" in custom_row:
fail("custom hotkey rows must not use the default blue borderedProminent Pro button")
+hotkey_button_start = preferences.find("private struct HotkeyPrimaryActionButton")
+hotkey_button_end = preferences.find("private struct HotkeySecondaryActionButton")
+if hotkey_button_start == -1 or hotkey_button_end == -1 or hotkey_button_end <= hotkey_button_start:
+ fail("could not inspect HotkeyPrimaryActionButton layout")
+hotkey_button = preferences[hotkey_button_start:hotkey_button_end]
+require(hotkey_button, ".buttonStyle(.bordered)", "locked custom hotkey action must visually match Data tab Pro-gated buttons")
+
require(pro, "case customHotkeys", "ProFeature.customHotkeys is missing")
-require(pro, "case .premiumThemes, .layoutImport, .layoutExport, .unlimitedNotes, .persistentAppSorting, .customHotkeys:", "customHotkeys must be unlocked only by the Pro access state")
+require(pro, "case .premiumThemes, .layoutImport, .layoutExport, .customTagColors, .unlimitedNotes, .persistentAppSorting, .customHotkeys:", "customHotkeys must be unlocked only by the Pro access state")
require(pro, "NotificationCenter.default.post(name: .tagLauncherProEntitlementChanged", "entitlement changes must notify hotkey registration")
require(access_views, "case .customHotkeys:", "Pro prompt routing must include custom hotkeys")
@@ -186,5 +245,90 @@
if "%status%" not in data["hotkeys.error.registrationFailed"]:
fail(f"{path.name} hotkeys.error.registrationFailed must keep %status% placeholder")
-print("PASS Pro custom hotkeys QA: Pro gating, transactional Carbon registration, duplicate-safe effective shortcuts, local recording, Pro-consistent row UI, and 29-language copy are wired")
+print("PASS Pro custom hotkeys static QA: Pro gating, recording suspension, transactional save path, duplicate-safe effective shortcuts, local recording, Pro-consistent row UI, and 29-language copy are wired")
PY
+
+swift - <<'SWIFT'
+import Carbon
+import Darwin
+
+func fail(_ message: String) -> Never {
+ fputs("FAIL: \(message)\n", stderr)
+ exit(1)
+}
+
+@discardableResult
+func register(
+ label: String,
+ keyCode: Int,
+ modifiers: UInt32,
+ id: UInt32,
+ expectSuccess: Bool
+) -> EventHotKeyRef? {
+ let hotkeyID = EventHotKeyID(signature: OSType(0x54474C51), id: id) // 'TGLQ'
+ var ref: EventHotKeyRef?
+ let status = RegisterEventHotKey(
+ UInt32(keyCode),
+ modifiers,
+ hotkeyID,
+ GetApplicationEventTarget(),
+ 0,
+ &ref
+ )
+ if expectSuccess {
+ guard status == noErr, let ref else {
+ fail("\(label) should register successfully, got status \(status)")
+ }
+ return ref
+ }
+ if status == noErr {
+ if let ref { UnregisterEventHotKey(ref) }
+ fail("\(label) should not register successfully")
+ }
+ return nil
+}
+
+let modifiers = UInt32(controlKey | optionKey | shiftKey)
+let mainRef = register(
+ label: "main representative shortcut",
+ keyCode: kVK_ANSI_9,
+ modifiers: modifiers,
+ id: 1,
+ expectSuccess: true
+)
+let quickSearchRef = register(
+ label: "quick search representative shortcut",
+ keyCode: kVK_ANSI_8,
+ modifiers: modifiers,
+ id: 2,
+ expectSuccess: true
+)
+let replacementRef = register(
+ label: "same-id replacement shortcut",
+ keyCode: kVK_ANSI_7,
+ modifiers: modifiers,
+ id: 1,
+ expectSuccess: true
+)
+let optionSpaceRef = register(
+ label: "Option-Space shortcut",
+ keyCode: kVK_Space,
+ modifiers: UInt32(optionKey),
+ id: 4,
+ expectSuccess: true
+)
+_ = register(
+ label: "duplicate shortcut conflict",
+ keyCode: kVK_ANSI_7,
+ modifiers: modifiers,
+ id: 3,
+ expectSuccess: false
+)
+
+if let optionSpaceRef { UnregisterEventHotKey(optionSpaceRef) }
+if let replacementRef { UnregisterEventHotKey(replacementRef) }
+if let quickSearchRef { UnregisterEventHotKey(quickSearchRef) }
+if let mainRef { UnregisterEventHotKey(mainRef) }
+
+print("PASS Pro custom hotkeys runtime QA: Carbon accepts representative custom registrations, Option-Space, same-id replacement, and rejects duplicate active shortcuts")
+SWIFT
--
Gitblit v1.9.3