| | |
| | | "layoutImport", |
| | | "layoutExport", |
| | | "customTagColors", |
| | | "customContainerQuota", |
| | | "unlimitedNotes", |
| | | "persistentAppSorting", |
| | | "customHotkeys", |
| | |
| | | require(pro, "ProEntitlementSnapshotStore", "sync entitlement snapshot for data-layer policy is missing") |
| | | require(pro, "manualNonEmptyNoteCount", "note quota must count existing manual notes") |
| | | require(pro, "manualNonEmptyNotePaths", "note quota must avoid double-counting existing notes") |
| | | require(pro, "case .premiumThemes, .layoutImport, .layoutExport, .customTagColors, .unlimitedNotes, .persistentAppSorting, .customHotkeys:", "custom tag colors and custom hotkeys must be unlocked only by the Pro access state") |
| | | require(pro, "case .premiumThemes, .layoutImport, .layoutExport, .customTagColors, .customContainerQuota, .unlimitedNotes, .persistentAppSorting, .customHotkeys:", "custom tag colors, custom container quota, and custom hotkeys must be unlocked only by the Pro access state") |
| | | require(pro, "NotificationCenter.default.post(name: .tagLauncherProEntitlementChanged", "Pro entitlement changes must notify hotkey registration") |
| | | |
| | | effective_theme = function_body(pro, "static func effectiveTheme(for storedTheme: AppGridTheme) -> AppGridTheme") |
| | |
| | | if 'Text(tr("settings.language"))' in preferences: |
| | | fail("Language tab must not render the removed header/title block") |
| | | require(preferences, "centeredSettingsScrollContent<Content: View>", "settings pages with sparse content must use the shared global centering container") |
| | | require(preferences, "centeredSettingsScrollContent(width: settingsContentWidth)", "language picker matrix must use the shared centered settings container") |
| | | require(preferences, "private let languageContentWidth: CGFloat = 820", "language picker matrix must have its own centered width aligned with fixed settings panels") |
| | | require(preferences, "centeredSettingsScrollContent(width: languageContentWidth)", "language picker matrix must use the centered settings container with the language-specific width") |
| | | if preferences.count("centeredSettingsScrollContent(width: dataPanelWidth)") < 2: |
| | | fail("Pro and About pages must both use the shared centered settings container") |
| | | require(preferences, ".frame(minHeight: proxy.size.height, alignment: .center)", "centered settings content must be vertically centered in the visible area") |
| | | if "topLeadingAccessory: AnyView(" in preferences: |
| | | fail("Tags page must not render a second Pro guidance row inside the tag editor") |
| | | if "topLeadingAccessory: AnyView(settingsProStatusOverview" in preferences: |
| | | fail("Tags page must not render a second bulky Pro guidance row inside the tag editor") |
| | | if preferences.count("ScrollView(.vertical, showsIndicators: true)") < 3: |
| | | fail("hotkeys/data/about pages must use vertical scroll containers to avoid clipping") |
| | | require(preferences, "NSEvent.addLocalMonitorForEvents(matching: .keyDown)", "custom hotkey recording must be local to Settings") |
| | | require(preferences, "settingsEscapeMonitor", "Settings must own a local Escape monitor for overlay presentation") |
| | | require(preferences, "installSettingsEscapeMonitor()", "Settings Escape monitor must be installed when Preferences appears") |
| | | require(preferences, "removeSettingsEscapeMonitor()", "Settings Escape monitor must be removed when Preferences disappears") |
| | | settings_escape_body = function_body(preferences, "private func handleSettingsEscapeKey() -> Bool") |
| | | for needle, message in [ |
| | | ("guard !isDataFilePanelPresented, window.attachedSheet == nil else { return false }", "Settings Escape must let file panels/sheets handle Escape first"), |
| | | ("dismissSettingsProPrompt()", "Settings Escape must close the in-window Pro prompt before closing Settings"), |
| | | ("showApplySystemSchemeConfirmation = false", "Settings Escape must close apply-confirmation overlays before closing Settings"), |
| | | ("showResetToUncategorizedConfirmation = false", "Settings Escape must close reset-confirmation overlays before closing Settings"), |
| | | ("stopHotkeyRecording(showCancelledToast: true)", "Settings Escape must cancel hotkey recording before closing Settings"), |
| | | ("window.performClose(nil)", "Settings Escape must close the Settings window in the normal overlay case"), |
| | | ]: |
| | | require(settings_escape_body, needle, message) |
| | | settings_escape_filter_body = function_body(preferences, "private func isSettingsOverlayEscapeEvent(_ event: NSEvent) -> Bool") |
| | | for needle, message in [ |
| | | ("let parent = window.parent as? OverlayPanel", "Settings Escape must be limited to Settings shown over the AppGrid overlay"), |
| | | ("parent.isVisible", "Settings Escape must require the overlay parent to be visible"), |
| | | ("eventWindow == window || eventWindow == parent", "Settings Escape must handle both Settings-key and overlay-key event routing"), |
| | | ]: |
| | | require(settings_escape_filter_body, needle, message) |
| | | require(preferences, "proEntitlement.startThemePreview(for: theme, tuning:", "premium theme free preview is missing") |
| | | require(preferences, "proEntitlement.stopThemePreview()", "theme preview cleanup is missing") |
| | | require(preferences, "proEntitlement.startDisplayModePreview(for: mode)", "colored display mode free preview is missing") |
| | |
| | | if "localizedCaseInsensitiveContains" in access_views: |
| | | fail("ProStatusPill must not hide the crown based on localized text content") |
| | | require(content, "onUnlock: { proEntitlement.purchasePro() }", "AppGrid Pro prompt is not wired to purchase action") |
| | | require(content, "onRestore: { proEntitlement.restorePurchases() }", "AppGrid Pro prompt is not wired to restore action") |
| | | require(content, "onRestore: { handleProPromptSecondaryAction(prompt) }", "AppGrid Pro prompt is not wired to secondary action handler") |
| | | require(content, "proEntitlement.restorePurchases()", "AppGrid Pro prompt secondary handler must still support restore purchase") |
| | | |
| | | tag_editor = read("TagEditorView.swift") |
| | | require(tag_editor, "ColorPicker(", "Pro custom tag colors must use a native macOS color picker path") |
| | |
| | | require(tag_nav, "let customColor: TagCustomColor?", "Tag navigation items must carry custom colors") |
| | | require(tag_nav, "TagColor.nsColor(for: item.colorIndex, customColor: item.customColor)", "Tag navigation must render custom colors") |
| | | |
| | | print("PASS Pro feature gate QA: StoreKit entitlement, legacy unlock, theme gating, import/export locks, 3-note quota, custom tag colors, custom hotkeys, sorting preview, and Pro UI prompts are wired") |
| | | print("PASS Pro feature gate QA: StoreKit entitlement, legacy unlock, theme gating, import/export locks, 3-note quota, custom tag colors, custom container quota, custom hotkeys, sorting preview, and Pro UI prompts are wired") |
| | | PY |