Ariver
2026-06-29 18c90acda54cb46815a081ecfc36ea8f2b9e93fd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env bash
set -euo pipefail
 
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
 
python3 - "$ROOT_DIR" <<'PY'
import json
import re
import sys
from pathlib import Path
 
root = Path(sys.argv[1])
app_dir = root / "Apptag"
localization_dir = app_dir / "Localization"
 
EXPECTED_LANGUAGE_COUNT = 29
REQUIRED_HOTKEY_KEYS = [
    "hotkeys.customize",
    "hotkeys.restoreDefault",
    "hotkeys.recording",
    "hotkeys.recordingInline",
    "hotkeys.recordingCancelled",
    "hotkeys.saved",
    "hotkeys.restored",
    "hotkeys.proRequired",
    "hotkeys.error.modifierRequired",
    "hotkeys.error.unsupportedKey",
    "hotkeys.error.reserved",
    "hotkeys.error.duplicate",
    "hotkeys.error.registrationFailed",
    "pro.feature.customHotkeys",
    "pro.feature.customHotkeys.benefit",
    "pro.prompt.customHotkeys",
]
 
 
def fail(message: str) -> None:
    raise SystemExit(f"FAIL: {message}")
 
 
def read(path: Path) -> str:
    try:
        return path.read_text(encoding="utf-8")
    except Exception as error:
        fail(f"could not read {path}: {error}")
 
 
def require(source: str, needle: str, message: str) -> None:
    if needle not in source:
        fail(message)
 
 
def function_body(source: str, signature: str) -> str:
    start = source.find(signature)
    if start == -1:
        fail(f"missing function signature: {signature}")
    brace = source.find("{", start)
    if brace == -1:
        fail(f"missing function body for: {signature}")
    depth = 0
    for index in range(brace, len(source)):
        char = source[index]
        if char == "{":
            depth += 1
        elif char == "}":
            depth -= 1
            if depth == 0:
                return source[brace + 1:index]
    fail(f"unterminated function body for: {signature}")
 
 
def load_json(path: Path) -> dict[str, str]:
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except Exception as error:
        fail(f"{path.name} is not valid JSON: {error}")
    if not isinstance(data, dict):
        fail(f"{path.name} must contain a JSON object")
    return data
 
 
settings = read(app_dir / "LauncherHotkeySettings.swift")
app = read(app_dir / "ApptagApp.swift")
preferences = read(app_dir / "PreferencesView.swift")
pro = read(app_dir / "ProEntitlement.swift")
access_views = read(app_dir / "ProAccessViews.swift")
defaults = read(app_dir / "AppDefaults.swift")
quick_search = read(app_dir / "QuickSearch.swift")
 
require(settings, 'private static let namespace = "customHotkeys.v1"', "custom hotkeys must use the new v1 namespace")
require(settings, "defaultHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkey", "default hotkey helper is missing")
require(settings, "kind.hotkey", "default hotkey must remain the fixed built-in shortcut")
require(settings, "ProEntitlementPolicy.isUnlocked(.customHotkeys)", "effective hotkey must be gated by Pro entitlement")
require(settings, "structuralValidationError(for: saved) == nil", "stored hotkeys must pass structural validation before use")
require(settings, "!duplicatesOtherEffectiveHotkey(saved, kind: kind)", "effective hotkey must reject duplicated saved shortcuts")
require(settings, "effectiveHotkeyIgnoringDuplicateCheck(for:", "duplicate validation must avoid recursive effectiveHotkey calls")
require(settings, "kEventKeyModifierFnMask", "Fn modifier support is required for the default Quick Search hotkey")
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")
 
effective_body = function_body(settings, "static func effectiveHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkey")
if "validationError(for:" in effective_body:
    fail("effectiveHotkey must not call validationError because duplicate checks can recurse")
 
validation_body = function_body(settings, "static func validationError(for hotkey: LauncherHotkey, kind: LauncherHotkeyKind) -> LauncherHotkeyValidationError?")
require(validation_body, "structuralValidationError(for:", "public validation must run structural checks")
require(validation_body, "duplicatesOtherEffectiveHotkey", "public validation must reject duplicates")
 
apply_body = function_body(app, "func applyCustomHotkey(_ hotkey: LauncherHotkey, for kind: LauncherHotkeyKind) -> LauncherHotkeyCustomizationResult")
require(apply_body, "guard ProEntitlementPolicy.isUnlocked(.customHotkeys) else", "AppDelegate save path must be Pro-gated")
require(apply_body, "LauncherHotkeySettings.validationError(for: hotkey, kind: kind)", "AppDelegate save path must validate the candidate")
require(apply_body, "activateHotkey(hotkey, for: kind, persistCustom: true)", "custom save must go through transactional activation")
 
restore_body = function_body(app, "func restoreDefaultHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkeyCustomizationResult")
require(restore_body, "guard ProEntitlementPolicy.isUnlocked(.customHotkeys) else", "restore default path must be Pro-gated")
require(restore_body, "persistCustom: false", "restore default must clear custom storage after successful registration")
 
activate_body = function_body(app, "private func activateHotkey(")
try_register_index = activate_body.find("tryRegisterHotkey(hotkey")
success_index = activate_body.find("case .success(let newRef):")
if try_register_index == -1 or success_index == -1:
    fail("transactional registration body is incomplete")
success_body = activate_body[success_index:]
unregister_index = success_body.find("UnregisterEventHotKey(oldRef)")
save_index = success_body.find("LauncherHotkeySettings.save")
if unregister_index == -1 or save_index == -1:
    fail("transactional success branch is incomplete")
if not (try_register_index < success_index and unregister_index < save_index):
    fail("custom hotkey must register first, then unregister old ref, then persist")
require(activate_body, "return .registrationFailed(status)", "registration failure must not save the candidate")
 
require(app, "observeProEntitlementChanges()", "AppDelegate must observe Pro entitlement changes")
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")
 
for forbidden in ["CGEventTapCreate", "AXIsProcessTrusted", "IOHIDManagerCreate", "addGlobalMonitorForEvents(matching: .keyDown"]:
    if forbidden in app + preferences + settings + quick_search:
        fail(f"forbidden API used for custom hotkeys: {forbidden}")
 
require(preferences, "presentSettingsProPrompt(for: .customHotkeys)", "free users must see the Pro prompt instead of recording")
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, ".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, "if canRestore {", "restore-default action must only appear when a custom shortcut exists")
 
custom_row_start = preferences.find("private struct CustomizableHotkeyInfoRow")
custom_row_end = preferences.find("private struct HotkeyStatusPill")
if custom_row_start == -1 or custom_row_end == -1 or custom_row_end <= custom_row_start:
    fail("could not inspect CustomizableHotkeyInfoRow layout")
custom_row = preferences[custom_row_start:custom_row_end]
if ".buttonStyle(.borderedProminent)" in custom_row:
    fail("custom hotkey rows must not use the default blue borderedProminent Pro button")
 
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, "NotificationCenter.default.post(name: .tagLauncherProEntitlementChanged", "entitlement changes must notify hotkey registration")
require(access_views, "case .customHotkeys:", "Pro prompt routing must include custom hotkeys")
 
cleanup_body = function_body(defaults, "private static func removeShortcutCustomizationDefaults()")
if "customHotkeys.v1" in cleanup_body:
    fail("legacy cleanup must not delete new custom hotkey storage")
 
language_files = sorted(localization_dir.glob("*.json"))
if len(language_files) != EXPECTED_LANGUAGE_COUNT:
    fail(f"expected {EXPECTED_LANGUAGE_COUNT} localization files, found {len(language_files)}")
 
for path in language_files:
    data = load_json(path)
    missing = [
        key for key in REQUIRED_HOTKEY_KEYS
        if not isinstance(data.get(key), str) or not data[key].strip()
    ]
    if missing:
        fail(f"{path.name} missing custom hotkey translations: {', '.join(missing)}")
    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")
PY