From 9c317d89a39d0871f15f8282aec6c7f3dde7768c Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 26 Jun 2026 22:42:44 +0800
Subject: [PATCH] Polish Pro status UI and app grid layout

---
 src/Apptag/Localization/fr.json        |    2 
 src/Apptag/Localization/ru.json        |    2 
 src/Apptag/AppGridCollectionView.swift |   24 +++
 src/Apptag/Localization/ar.json        |    2 
 src/Apptag/Localization/ro.json        |    2 
 src/Apptag/Localization/en.json        |    2 
 src/Apptag/Localization/sv.json        |    2 
 src/Apptag/Localization/zh-Hant.json   |    2 
 src/Apptag/ApptagApp.swift             |   56 ++++++++
 src/Apptag/Localization/pl.json        |    2 
 src/Apptag/Localization/es.json        |    2 
 src/Apptag/Localization/th.json        |    2 
 src/Apptag/Localization/nn.json        |    2 
 src/CHANGELOG.md                       |   10 +
 src/Apptag/Info.plist                  |    4 
 src/Apptag/Localization/cs.json        |    2 
 src/Apptag/Localization/ms.json        |    2 
 src/Apptag/Localization/it.json        |    2 
 src/Apptag/Localization/tr.json        |    2 
 src/Apptag/QuickSearch.swift           |    9 +
 src/Apptag/Localization/da.json        |    2 
 src/Apptag/Localization/id.json        |    2 
 src/Scripts/window_logic_qa.sh         |   74 ++++++++++
 src/Apptag/Localization/ja.json        |    2 
 src/Apptag/PreferencesView.swift       |  109 ++++++++-------
 src/Apptag/Localization/de.json        |    2 
 src/Apptag/Localization/pt-BR.json     |    2 
 CODEGRAPH.md                           |   10 +
 src/Apptag/ProAccessViews.swift        |   30 +++-
 src/Apptag/Localization/ar-Najdi.json  |    2 
 src/Apptag/Localization/nb.json        |    2 
 src/Apptag/Localization/ko.json        |    2 
 src/Apptag/Localization/no.json        |    2 
 src/Apptag/ContentView.swift           |   20 ++
 src/Apptag/Localization/sr-Cyrl.json   |    2 
 src/Apptag/Localization/zh-Hans.json   |    2 
 src/Apptag/Localization/nl.json        |    2 
 src/Apptag/Localization/uk.json        |    2 
 src/Apptag/Localization/vi.json        |    2 
 39 files changed, 338 insertions(+), 66 deletions(-)

diff --git a/CODEGRAPH.md b/CODEGRAPH.md
index df98068..d6b92c7 100644
--- a/CODEGRAPH.md
+++ b/CODEGRAPH.md
@@ -21,6 +21,7 @@
   - `AppDelegate`:菜单栏状态项、Dock 显示策略、热键注册、窗口打开、Quick Search 触发、App 生命周期。
   - 支持从标签导航双击进入设置页的 Tags tab。
   - 启动时初始化 `ProEntitlementCenter`,保证免费 / Pro / 老用户自动 Pro 状态进入全局同步快照。
+  - 菜单栏下拉菜单包含免费 / Pro 当前身份状态项;免费状态点击进入 About 页的 Pro 卡片,Pro 状态使用金色皇冠图标。
 - `src/Apptag/OverlayWindowController.swift`
   - `OverlayPanel` / `OverlayWindowController`:AppGrid 浮层窗口与显示/隐藏控制。
 - `src/Apptag/ContentView.swift`
@@ -29,9 +30,11 @@
   - 编辑模式使用 runtime theme override:任何主题下进入编辑都临时显示默认浅色毛玻璃。
   - 使用最近一次完整 `AppLibrarySnapshot` 先渲染 AppGrid,再后台刷新,避免启动/重开时立刻显示转圈。
   - 使用技巧关闭提醒由 `ContentView` 统一弹 modal 并写入 `hideUsageTips` / `skipUsageTipsCloseReminder`。
+  - Quick Search 点击 TagLauncher 自身结果时只关闭当前浮层,不走外部自启动,避免 AppGrid 被重新拉起。
 - `src/Apptag/PreferencesView.swift`
   - `PreferencesView`:设置窗口、语言、通用、主题、快捷键、标签、数据、关于等设置页。
   - Theme tab 只写视觉偏好 `appGridThemeID`,不得改变标签、排序、备注或分类数据。
+  - Pro 升级 / 权益卡位于 About tab;Data tab 只承载分类方案、导入导出和气泡显示位置等数据管理入口。
 
 ## AppGrid 显示与交互
 
@@ -42,6 +45,7 @@
   - `AppGridIconNSView`:应用图标单元、点击、hover 气泡、拖拽起点。
   - 底部使用技巧是原生 AppKit overlay,不允许改成 SwiftUI;必须拦截自身点击区域。
   - 不拥有 AppGrid 全屏渐变背景,只接收 theme 推导出的玻璃和文本可读性 token。
+  - 样式 2 / 样式 3 的瀑布流容器布局会把 Mac 自带容器固定到底部并横向占满。
 - `src/Apptag/AppGridTheme.swift`
   - AppGrid 主题事实源:theme id、localization key、设置页 swatch、全屏渐变背景、玻璃明暗、编辑模式 token。
   - 持久化 key:`appGridThemeID`。
@@ -51,7 +55,7 @@
   - `ProEntitlementPolicy`:数据层可同步调用的 Pro 门禁;覆盖主题、导入 / 导出、备注额度和应用排序持久化。
   - `ProEntitlementConfig`:一次性买断商品 ID、免费 + Pro 首个版本、免费主题集合、免费备注额度和 QA 环境变量的单一配置点。
 - `src/Apptag/ProAccessViews.swift`
-  - `ProStatusPill`:高级功能入口的小型 Pro 胶囊。
+  - `ProStatusPill`:高级功能入口的小型 Pro 胶囊;Pro / 已锁定高级功能使用金色皇冠体系,免费状态使用中性胶囊。
   - `ProUpgradePromptView`:功能被锁时的统一解锁提示,购买 / 恢复动作由调用方闭包绑定。
 - `src/Apptag/AppGridSupport.swift`
   - 图标气泡、基础 metrics、气泡 placement 支撑。
@@ -85,6 +89,7 @@
   - `LauncherHotkeyRegistrationStore`:快捷键注册状态持久化。
   - `QuickSearchDocument` / `QuickSearchEngine`:搜索索引与排序。
   - `QuickSearchPanelPresentationView` / `QuickSearchOverlayView` / `QuickSearchResultListHostView`:Quick Search 浮层 UI 与结果列表。
+  - `QuickSearchResultRowView`:结果行内部文字、图标、标签区域统一命中到整行,保证点击任意可见区域都能启动并关闭浮层。
 
 ## 智能分类与 Apple 默认应用资料
 
@@ -171,6 +176,7 @@
 - 不得把主题、视觉偏好、窗口状态混入 `TagDatabase` 的分类/标签业务数据。
 - 主题变化只允许影响 AppGrid 视觉,不得改变标签数据、排序、备注、SmartStart/category scheme、Quick Search、拖拽语义或导入导出。
 - Pro 门禁不得写脏免费用户真实数据:主题预览不写 `appGridThemeID`,排序预览不写 `containerAppOrder`,第 6 个新备注不写 `appNotes` / `appNoteMetadata`,导入 / 导出拦截必须早于文件面板和文件读写。
+- Pro 升级 / 权益卡应放在 About tab;Data tab 不承载 Pro 营销卡,避免挤压数据管理布局。
 - 免费主题固定为默认、深蓝、黑色;其它主题只能预览或在 Pro 解锁后持久化。
 - `originalAppVersion` 早于免费 + Pro 首个版本的历史用户必须自动获得 Pro;`originalPurchaseDate` 只能作为版本不可判断时的兜底。
 - 默认主题必须保留原浅色毛玻璃 AppGrid。
@@ -180,6 +186,8 @@
 - 旧用户 `useDarkAppGrid=true` 必须迁移到 `deepBlue` 主题。
 - AppGrid 启动或重开不应因为新 `ContentView` 初始 `allApps.isEmpty` 立刻显示转圈;应先复用最近一次完整快照,并延迟显示 loading。
 - Quick Search 显示标题应优先使用解析后的 `AppInfo.displayName`,内部 bundle 名只作为搜索字段。
+- Quick Search 结果行内部文字 / 图标区域不得吞掉点击;点中行内任意可见区域都应按结果启动路径处理。
+- Quick Search-only 点击 TagLauncher 自身结果必须关闭浮层,不得重新打开 AppGrid。
 - Apple 默认应用备注必须按当前语言显示,不允许 A 语言出现 B 语言备注。
 - SmartStart/default system tag 初始化标签必须按当前语言显示;升级用户启动后应重刷带 `systemCategoryID` 的系统标签,不能长期保留旧英文初始化标签。
 - 标签导航单击必须立即滚动;hover 必须保持受控自动滚动;长按排序必须保留;双击可以打开标签设置页但不得替代前三者。
diff --git a/src/Apptag/AppGridCollectionView.swift b/src/Apptag/AppGridCollectionView.swift
index d124a61..8e95bc8 100644
--- a/src/Apptag/AppGridCollectionView.swift
+++ b/src/Apptag/AppGridCollectionView.swift
@@ -1913,8 +1913,12 @@
         let columnWidth = floor((availableWidth - gap * CGFloat(columnCount - 1)) / CGFloat(columnCount))
         var columnHeights = Array(repeating: outerPadding, count: columnCount)
         var items: [LayoutItem] = []
+        let bottomPinnedGroups = groups.enumerated().filter { $0.element.containerID == AppContainerID.appleBuiltIn }
+        let bottomPinnedIndexes = Set(bottomPinnedGroups.map { $0.offset })
 
         for (index, group) in groups.enumerated() {
+            if bottomPinnedIndexes.contains(index) { continue }
+
             let columnIndex = columnHeights.indices.min { columnHeights[$0] < columnHeights[$1] } ?? 0
             let x = outerPadding + CGFloat(columnIndex) * (columnWidth + gap)
             let y = columnHeights[columnIndex]
@@ -1932,8 +1936,24 @@
             columnHeights[columnIndex] = y + height + gap
         }
 
-        let tallest = columnHeights.max() ?? outerPadding
-        let contentHeight = (groups.isEmpty ? outerPadding * 2 : tallest - gap + outerPadding)
+        var contentBottom = columnHeights.max() ?? outerPadding
+        for (index, group) in bottomPinnedGroups {
+            let y = max(outerPadding, contentBottom)
+            let height = AppGridCollectionMetrics.cardHeight(
+                appCount: group.apps.count,
+                width: availableWidth,
+                iconSize: iconSize
+            )
+            items.append(
+                LayoutItem(
+                    index: index,
+                    frame: NSRect(x: outerPadding, y: y, width: availableWidth, height: height)
+                )
+            )
+            contentBottom = y + height + gap
+        }
+
+        let contentHeight = (items.isEmpty ? outerPadding * 2 : contentBottom - gap + outerPadding)
             + max(0, bottomContentPadding)
         return LayoutPlan(
             items: items,
diff --git a/src/Apptag/ApptagApp.swift b/src/Apptag/ApptagApp.swift
index c5b38a3..bb6b32b 100644
--- a/src/Apptag/ApptagApp.swift
+++ b/src/Apptag/ApptagApp.swift
@@ -34,6 +34,7 @@
     private static let statusItemButtonIdentifier = NSUserInterfaceItemIdentifier("TagLauncherStatusItemButton")
     private static let statusItemAccessibilityLabel = AppIdentity.displayName
     private static let showAppListMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherShowAppListMenuItem")
+    private static let proStatusMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherProStatusMenuItem")
     private static let helpMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherHelpMenu")
     private static let downloadHelpMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherDownloadHelpMenuItem")
     private static let externalActivationNotification = Notification.Name("TagLauncherExternalActivationRequested")
@@ -661,6 +662,9 @@
         versionItem.isEnabled = false
         menu.addItem(versionItem)
 
+        let proStatusItem = makeProStatusMenuItem()
+        menu.addItem(proStatusItem)
+
         menu.addItem(.separator())
         let prefsItem = NSMenuItem(
             title: tr("menu.preferences"),
@@ -697,6 +701,7 @@
     }
 
     func menuWillOpen(_ menu: NSMenu) {
+        configureProStatusMenuItem(in: menu)
         statusMenuScreenForNextOverlay = overlayController.screenContainingCurrentPointer()
             ?? statusItem?.button?.window?.screen
             ?? NSScreen.main
@@ -766,6 +771,53 @@
 
         image.isTemplate = true
         image.accessibilityDescription = "TagLauncher"
+        return image
+    }
+
+    private func makeProStatusMenuItem() -> NSMenuItem {
+        let item = NSMenuItem(
+            title: "",
+            action: #selector(openProStatusFromStatusMenu(_:)),
+            keyEquivalent: ""
+        )
+        item.identifier = Self.proStatusMenuItemIdentifier
+        item.target = self
+        configureProStatusMenuItem(item)
+        return item
+    }
+
+    private func configureProStatusMenuItem(in menu: NSMenu) {
+        guard let item = menu.items.first(where: { $0.identifier == Self.proStatusMenuItemIdentifier }) else {
+            return
+        }
+        configureProStatusMenuItem(item)
+    }
+
+    private func configureProStatusMenuItem(_ item: NSMenuItem) {
+        let isUnlocked = ProEntitlementPolicy.accessState().isUnlocked
+        item.title = tr(isUnlocked ? "menu.proStatus.unlocked" : "menu.proStatus.free")
+        item.action = #selector(openProStatusFromStatusMenu(_:))
+        item.target = self
+        item.isEnabled = true
+        item.image = isUnlocked ? makeProStatusMenuIcon() : nil
+    }
+
+    private func makeProStatusMenuIcon() -> NSImage? {
+        guard let symbol = NSImage(systemSymbolName: "crown.fill", accessibilityDescription: "Pro") else {
+            return nil
+        }
+        let configuration = NSImage.SymbolConfiguration(pointSize: 13, weight: .semibold)
+        let configuredSymbol = symbol.withSymbolConfiguration(configuration) ?? symbol
+        let image = NSImage(size: NSSize(width: 16, height: 16))
+        image.lockFocus()
+        NSColor(red: 0.96, green: 0.63, blue: 0.10, alpha: 1.0).set()
+        let iconRect = NSRect(x: 1.5, y: 1.5, width: 13, height: 13)
+        configuredSymbol.draw(in: iconRect, from: .zero, operation: .sourceOver, fraction: 1.0)
+        NSColor(red: 0.96, green: 0.63, blue: 0.10, alpha: 1.0).set()
+        iconRect.fill(using: .sourceAtop)
+        image.unlockFocus()
+        image.isTemplate = false
+        image.accessibilityDescription = "Pro"
         return image
     }
 
@@ -1700,6 +1752,10 @@
         openPreferences(targetTab: nil)
     }
 
+    @objc private func openProStatusFromStatusMenu(_ sender: NSMenuItem) {
+        openPreferences(targetTab: SettingsTabTarget.about)
+    }
+
     private func openPreferences(targetTab: String?) {
         dismissQuickSearchIfNeeded()
         TagDatabase.flushPendingCategorySchemeBackupBatch()
diff --git a/src/Apptag/ContentView.swift b/src/Apptag/ContentView.swift
index 80fb99e..2560bb9 100644
--- a/src/Apptag/ContentView.swift
+++ b/src/Apptag/ContentView.swift
@@ -19,6 +19,7 @@
     static let theme = "theme"
     static let tags = "tags"
     static let data = "data"
+    static let about = "about"
 }
 
 // MARK: - Edit Phase
@@ -2953,6 +2954,15 @@
             closeQuickSearch(hideOverlayIfNeeded: true)
             hideOverlay()
         }
+        if isCurrentApp(app) {
+            if closeQuickSearchOnSuccess && !closeQuickSearchAndOverlayBeforeOpening {
+                closeQuickSearch(hideOverlayIfNeeded: closeOverlayOnSuccess)
+            }
+            if closeOverlayOnSuccess && !closeQuickSearchAndOverlayBeforeOpening {
+                hideOverlay()
+            }
+            return
+        }
         let configuration = NSWorkspace.OpenConfiguration()
         NSWorkspace.shared.openApplication(at: app.path, configuration: configuration) { _, error in
             DispatchQueue.main.async {
@@ -2974,6 +2984,16 @@
         }
     }
 
+    private func isCurrentApp(_ app: AppInfo) -> Bool {
+        if app.bundleIdentifier?.caseInsensitiveCompare(AppIdentity.bundleIdentifier) == .orderedSame {
+            return true
+        }
+
+        let currentURL = Bundle.main.bundleURL.standardizedFileURL.resolvingSymlinksInPath()
+        let appURL = app.path.standardizedFileURL.resolvingSymlinksInPath()
+        return currentURL == appURL
+    }
+
     func openApp(_ app: AppInfo) {
         hideOverlay()
         launchApp(app, closeOverlayOnSuccess: false)
diff --git a/src/Apptag/Info.plist b/src/Apptag/Info.plist
index 903dc0f..d2037c6 100644
--- a/src/Apptag/Info.plist
+++ b/src/Apptag/Info.plist
@@ -19,9 +19,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>8.1.0</string>
+	<string>8.1.1</string>
 	<key>CFBundleVersion</key>
-	<string>20260626.1726</string>
+	<string>20260626.2229</string>
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.utilities</string>
 	<key>LSMinimumSystemVersion</key>
diff --git a/src/Apptag/Localization/ar-Najdi.json b/src/Apptag/Localization/ar-Najdi.json
index 2e559b2..4bb424c 100644
--- a/src/Apptag/Localization/ar-Najdi.json
+++ b/src/Apptag/Localization/ar-Najdi.json
@@ -8,6 +8,8 @@
   "menu.language": "اللغة",
   "menu.help": "المساعدة",
   "menu.version": "الإصدار",
+  "menu.proStatus.free": "أنت حالياً مستخدم مجاني (الترقية إلى Pro)",
+  "menu.proStatus.unlocked": "أنت حالياً مستخدم Pro مميز",
   "settings.language": "اللغة",
   "settings.languagePicker": "لغة التطبيق:",
   "settings.languageDesc": "اختر لغة القوائم والإعدادات.",
diff --git a/src/Apptag/Localization/ar.json b/src/Apptag/Localization/ar.json
index 7f0410f..3bd3052 100644
--- a/src/Apptag/Localization/ar.json
+++ b/src/Apptag/Localization/ar.json
@@ -8,6 +8,8 @@
   "menu.language": "اللغة",
   "menu.help": "المساعدة",
   "menu.version": "الإصدار",
+  "menu.proStatus.free": "أنت حالياً مستخدم مجاني (الترقية إلى Pro)",
+  "menu.proStatus.unlocked": "أنت حالياً مستخدم Pro مميز",
   "settings.language": "اللغة",
   "settings.languagePicker": "لغة التطبيق:",
   "settings.languageDesc": "اختر اللغة المستخدمة في القوائم والإعدادات.",
diff --git a/src/Apptag/Localization/cs.json b/src/Apptag/Localization/cs.json
index 01e49be..9593aee 100644
--- a/src/Apptag/Localization/cs.json
+++ b/src/Apptag/Localization/cs.json
@@ -8,6 +8,8 @@
   "menu.language": "Jazyk",
   "menu.help": "Nápověda",
   "menu.version": "Verze",
+  "menu.proStatus.free": "Nyní jste uživatel bezplatné verze (upgradovat na Pro)",
+  "menu.proStatus.unlocked": "Nyní jste vážený uživatel verze Pro",
   "settings.language": "Jazyk",
   "settings.languagePicker": "Jazyk aplikace:",
   "settings.languageDesc": "Vyberte jazyk používaný v nabídkách a nastaveních.",
diff --git a/src/Apptag/Localization/da.json b/src/Apptag/Localization/da.json
index 68d9249..edd7934 100644
--- a/src/Apptag/Localization/da.json
+++ b/src/Apptag/Localization/da.json
@@ -8,6 +8,8 @@
   "menu.language": "Sprog",
   "menu.help": "Hjælp",
   "menu.version": "Version",
+  "menu.proStatus.free": "Du er i øjeblikket gratisbruger (opgrader til Pro)",
+  "menu.proStatus.unlocked": "Du er i øjeblikket en værdsat Pro-bruger",
   "settings.language": "Sprog",
   "settings.languagePicker": "Appsprog:",
   "settings.languageDesc": "Vælg sproget til menuer og indstillinger.",
diff --git a/src/Apptag/Localization/de.json b/src/Apptag/Localization/de.json
index 1bf72f8..5d04e9a 100644
--- a/src/Apptag/Localization/de.json
+++ b/src/Apptag/Localization/de.json
@@ -8,6 +8,8 @@
   "menu.language": "Sprache",
   "menu.help": "Hilfe",
   "menu.version": "Version",
+  "menu.proStatus.free": "Sie sind derzeit Nutzer der Gratisversion (Upgrade auf Pro)",
+  "menu.proStatus.unlocked": "Sie sind derzeit ein geschätzter Pro-Nutzer",
   "settings.language": "Sprache",
   "settings.languagePicker": "App-Sprache:",
   "settings.languageDesc": "Wähle die Sprache für Menüs und Einstellungen.",
diff --git a/src/Apptag/Localization/en.json b/src/Apptag/Localization/en.json
index e546bdf..bba7486 100644
--- a/src/Apptag/Localization/en.json
+++ b/src/Apptag/Localization/en.json
@@ -8,6 +8,8 @@
   "menu.language": "Language",
   "menu.help": "Help",
   "menu.version": "Version",
+  "menu.proStatus.free": "You are currently a Free user (upgrade to Pro)",
+  "menu.proStatus.unlocked": "You are currently a valued Pro user",
   "settings.language": "Language",
   "settings.languagePicker": "App language:",
   "settings.languageDesc": "Choose the language used by menus and settings.",
diff --git a/src/Apptag/Localization/es.json b/src/Apptag/Localization/es.json
index ae0f75f..9635fee 100644
--- a/src/Apptag/Localization/es.json
+++ b/src/Apptag/Localization/es.json
@@ -8,6 +8,8 @@
   "menu.language": "Idioma",
   "menu.help": "Ayuda",
   "menu.version": "Versión",
+  "menu.proStatus.free": "Actualmente eres usuario gratuito (actualizar a Pro)",
+  "menu.proStatus.unlocked": "Actualmente eres un usuario Pro distinguido",
   "settings.language": "Idioma",
   "settings.languagePicker": "Idioma de la app:",
   "settings.languageDesc": "Elige el idioma usado en los menús y ajustes.",
diff --git a/src/Apptag/Localization/fr.json b/src/Apptag/Localization/fr.json
index 7c74c73..de9a0a7 100644
--- a/src/Apptag/Localization/fr.json
+++ b/src/Apptag/Localization/fr.json
@@ -8,6 +8,8 @@
   "menu.language": "Langue",
   "menu.help": "Aide",
   "menu.version": "Version",
+  "menu.proStatus.free": "Vous utilisez actuellement la version gratuite (passer à Pro)",
+  "menu.proStatus.unlocked": "Vous êtes actuellement un utilisateur Pro privilégié",
   "settings.language": "Langue",
   "settings.languagePicker": "Langue de l'app :",
   "settings.languageDesc": "Choisissez la langue utilisée par les menus et les réglages.",
diff --git a/src/Apptag/Localization/id.json b/src/Apptag/Localization/id.json
index d8a8208..950bbc7 100644
--- a/src/Apptag/Localization/id.json
+++ b/src/Apptag/Localization/id.json
@@ -8,6 +8,8 @@
   "menu.language": "Bahasa",
   "menu.help": "Bantuan",
   "menu.version": "Versi",
+  "menu.proStatus.free": "Saat ini Anda pengguna Gratis (tingkatkan ke Pro)",
+  "menu.proStatus.unlocked": "Saat ini Anda pengguna Pro istimewa",
   "settings.language": "Bahasa",
   "settings.languagePicker": "Bahasa aplikasi:",
   "settings.languageDesc": "Pilih bahasa yang digunakan untuk menu dan pengaturan.",
diff --git a/src/Apptag/Localization/it.json b/src/Apptag/Localization/it.json
index 30583c6..cb27fae 100644
--- a/src/Apptag/Localization/it.json
+++ b/src/Apptag/Localization/it.json
@@ -8,6 +8,8 @@
   "menu.language": "Lingua",
   "menu.help": "Aiuto",
   "menu.version": "Versione",
+  "menu.proStatus.free": "Attualmente sei un utente gratuito (passa a Pro)",
+  "menu.proStatus.unlocked": "Attualmente sei un utente Pro privilegiato",
   "settings.language": "Lingua",
   "settings.languagePicker": "Lingua app:",
   "settings.languageDesc": "Scegli la lingua usata da menu e impostazioni.",
diff --git a/src/Apptag/Localization/ja.json b/src/Apptag/Localization/ja.json
index 258d99d..e52078f 100644
--- a/src/Apptag/Localization/ja.json
+++ b/src/Apptag/Localization/ja.json
@@ -8,6 +8,8 @@
   "menu.language": "言語",
   "menu.help": "ヘルプ",
   "menu.version": "バージョン",
+  "menu.proStatus.free": "現在は無料版ユーザーです(Pro にアップグレード)",
+  "menu.proStatus.unlocked": "現在は特別な Pro ユーザーです",
   "settings.language": "言語",
   "settings.languagePicker": "アプリの言語:",
   "settings.languageDesc": "メニューと設定で使う言語を選択します。",
diff --git a/src/Apptag/Localization/ko.json b/src/Apptag/Localization/ko.json
index f4dd987..5b92d99 100644
--- a/src/Apptag/Localization/ko.json
+++ b/src/Apptag/Localization/ko.json
@@ -8,6 +8,8 @@
   "menu.language": "언어",
   "menu.help": "도움말",
   "menu.version": "버전",
+  "menu.proStatus.free": "현재 무료 사용자입니다(Pro로 업그레이드)",
+  "menu.proStatus.unlocked": "현재 특별 Pro 사용자입니다",
   "settings.language": "언어",
   "settings.languagePicker": "앱 언어:",
   "settings.languageDesc": "메뉴와 설정에서 사용할 언어를 선택합니다.",
diff --git a/src/Apptag/Localization/ms.json b/src/Apptag/Localization/ms.json
index c15a6d2..4d95c02 100644
--- a/src/Apptag/Localization/ms.json
+++ b/src/Apptag/Localization/ms.json
@@ -8,6 +8,8 @@
   "menu.language": "Bahasa",
   "menu.help": "Bantuan",
   "menu.version": "Versi",
+  "menu.proStatus.free": "Anda kini pengguna Percuma (naik taraf ke Pro)",
+  "menu.proStatus.unlocked": "Anda kini pengguna Pro istimewa",
   "settings.language": "Bahasa",
   "settings.languagePicker": "Bahasa aplikasi:",
   "settings.languageDesc": "Pilih bahasa yang digunakan untuk menu dan tetapan.",
diff --git a/src/Apptag/Localization/nb.json b/src/Apptag/Localization/nb.json
index 69b4ca6..7276bf4 100644
--- a/src/Apptag/Localization/nb.json
+++ b/src/Apptag/Localization/nb.json
@@ -8,6 +8,8 @@
   "menu.language": "Språk",
   "menu.help": "Hjelp",
   "menu.version": "Versjon",
+  "menu.proStatus.free": "Du er nå gratisbruker (oppgrader til Pro)",
+  "menu.proStatus.unlocked": "Du er nå en verdsatt Pro-bruker",
   "settings.language": "Språk",
   "settings.languagePicker": "Appspråk:",
   "settings.languageDesc": "Velg språket som brukes i menyer og innstillinger.",
diff --git a/src/Apptag/Localization/nl.json b/src/Apptag/Localization/nl.json
index 07c4b32..3bfdbfa 100644
--- a/src/Apptag/Localization/nl.json
+++ b/src/Apptag/Localization/nl.json
@@ -8,6 +8,8 @@
   "menu.language": "Taal",
   "menu.help": "Help",
   "menu.version": "Versie",
+  "menu.proStatus.free": "U bent momenteel een gratis gebruiker (upgrade naar Pro)",
+  "menu.proStatus.unlocked": "U bent momenteel een gewaardeerde Pro-gebruiker",
   "settings.language": "Taal",
   "settings.languagePicker": "App-taal:",
   "settings.languageDesc": "Kies de taal voor menu’s en instellingen.",
diff --git a/src/Apptag/Localization/nn.json b/src/Apptag/Localization/nn.json
index 316807d..bb62912 100644
--- a/src/Apptag/Localization/nn.json
+++ b/src/Apptag/Localization/nn.json
@@ -8,6 +8,8 @@
   "menu.language": "Språk",
   "menu.help": "Hjelp",
   "menu.version": "Versjon",
+  "menu.proStatus.free": "Du er no gratisbrukar (oppgrader til Pro)",
+  "menu.proStatus.unlocked": "Du er no ein verdsett Pro-brukar",
   "settings.language": "Språk",
   "settings.languagePicker": "Appspråk:",
   "settings.languageDesc": "Velg språket som brukes i menyer og innstillinger.",
diff --git a/src/Apptag/Localization/no.json b/src/Apptag/Localization/no.json
index 69b4ca6..7276bf4 100644
--- a/src/Apptag/Localization/no.json
+++ b/src/Apptag/Localization/no.json
@@ -8,6 +8,8 @@
   "menu.language": "Språk",
   "menu.help": "Hjelp",
   "menu.version": "Versjon",
+  "menu.proStatus.free": "Du er nå gratisbruker (oppgrader til Pro)",
+  "menu.proStatus.unlocked": "Du er nå en verdsatt Pro-bruker",
   "settings.language": "Språk",
   "settings.languagePicker": "Appspråk:",
   "settings.languageDesc": "Velg språket som brukes i menyer og innstillinger.",
diff --git a/src/Apptag/Localization/pl.json b/src/Apptag/Localization/pl.json
index 0c7bb7d..544ce9e 100644
--- a/src/Apptag/Localization/pl.json
+++ b/src/Apptag/Localization/pl.json
@@ -8,6 +8,8 @@
   "menu.language": "Język",
   "menu.help": "Pomoc",
   "menu.version": "Wersja",
+  "menu.proStatus.free": "Obecnie korzystasz z wersji bezpłatnej (uaktualnij do Pro)",
+  "menu.proStatus.unlocked": "Obecnie jesteś cenionym użytkownikiem Pro",
   "settings.language": "Język",
   "settings.languagePicker": "Język aplikacji:",
   "settings.languageDesc": "Wybierz język menu i ustawień.",
diff --git a/src/Apptag/Localization/pt-BR.json b/src/Apptag/Localization/pt-BR.json
index d1b05eb..7b46e9e 100644
--- a/src/Apptag/Localization/pt-BR.json
+++ b/src/Apptag/Localization/pt-BR.json
@@ -8,6 +8,8 @@
   "menu.language": "Idioma",
   "menu.help": "Ajuda",
   "menu.version": "Versão",
+  "menu.proStatus.free": "Você está na versão gratuita (atualizar para Pro)",
+  "menu.proStatus.unlocked": "Você é um usuário Pro especial",
   "settings.language": "Idioma",
   "settings.languagePicker": "Idioma do app:",
   "settings.languageDesc": "Escolha o idioma usado nos menus e ajustes.",
diff --git a/src/Apptag/Localization/ro.json b/src/Apptag/Localization/ro.json
index c0f8c8e..8509fd6 100644
--- a/src/Apptag/Localization/ro.json
+++ b/src/Apptag/Localization/ro.json
@@ -8,6 +8,8 @@
   "menu.language": "Limbă",
   "menu.help": "Ajutor",
   "menu.version": "Versiune",
+  "menu.proStatus.free": "În prezent sunteți utilizator gratuit (treceți la Pro)",
+  "menu.proStatus.unlocked": "În prezent sunteți un utilizator Pro apreciat",
   "settings.language": "Limbă",
   "settings.languagePicker": "Limba aplicației:",
   "settings.languageDesc": "Alege limba folosită pentru meniuri și setări.",
diff --git a/src/Apptag/Localization/ru.json b/src/Apptag/Localization/ru.json
index ad88403..ddc4598 100644
--- a/src/Apptag/Localization/ru.json
+++ b/src/Apptag/Localization/ru.json
@@ -8,6 +8,8 @@
   "menu.language": "Язык",
   "menu.help": "Справка",
   "menu.version": "Версия",
+  "menu.proStatus.free": "Сейчас вы пользователь бесплатной версии (перейти на Pro)",
+  "menu.proStatus.unlocked": "Сейчас вы ценный пользователь Pro",
   "settings.language": "Язык",
   "settings.languagePicker": "Язык приложения:",
   "settings.languageDesc": "Выберите язык меню и настроек.",
diff --git a/src/Apptag/Localization/sr-Cyrl.json b/src/Apptag/Localization/sr-Cyrl.json
index 7fa91bd..ee023c3 100644
--- a/src/Apptag/Localization/sr-Cyrl.json
+++ b/src/Apptag/Localization/sr-Cyrl.json
@@ -8,6 +8,8 @@
   "menu.language": "Језик",
   "menu.help": "Помоћ",
   "menu.version": "Верзија",
+  "menu.proStatus.free": "Тренутно сте корисник бесплатне верзије (надоградите на Pro)",
+  "menu.proStatus.unlocked": "Тренутно сте цењени Pro корисник",
   "settings.language": "Језик",
   "settings.languagePicker": "Језик апликације:",
   "settings.languageDesc": "Изаберите језик менија и подешавања.",
diff --git a/src/Apptag/Localization/sv.json b/src/Apptag/Localization/sv.json
index 82af41b..ca1ea1d 100644
--- a/src/Apptag/Localization/sv.json
+++ b/src/Apptag/Localization/sv.json
@@ -8,6 +8,8 @@
   "menu.language": "Språk",
   "menu.help": "Hjälp",
   "menu.version": "Version",
+  "menu.proStatus.free": "Du är för närvarande gratisanvändare (uppgradera till Pro)",
+  "menu.proStatus.unlocked": "Du är för närvarande en uppskattad Pro-användare",
   "settings.language": "Språk",
   "settings.languagePicker": "Appspråk:",
   "settings.languageDesc": "Välj språket som används i menyer och inställningar.",
diff --git a/src/Apptag/Localization/th.json b/src/Apptag/Localization/th.json
index cb42deb..ef7e228 100644
--- a/src/Apptag/Localization/th.json
+++ b/src/Apptag/Localization/th.json
@@ -8,6 +8,8 @@
   "menu.language": "ภาษา",
   "menu.help": "ช่วยเหลือ",
   "menu.version": "เวอร์ชัน",
+  "menu.proStatus.free": "ตอนนี้คุณเป็นผู้ใช้ฟรี (อัปเกรดเป็น Pro)",
+  "menu.proStatus.unlocked": "ตอนนี้คุณเป็นผู้ใช้ Pro ระดับพิเศษ",
   "settings.language": "ภาษา",
   "settings.languagePicker": "ภาษาของแอป:",
   "settings.languageDesc": "เลือกภาษาที่ใช้ในเมนูและการตั้งค่า",
diff --git a/src/Apptag/Localization/tr.json b/src/Apptag/Localization/tr.json
index 49663a6..6dc43e8 100644
--- a/src/Apptag/Localization/tr.json
+++ b/src/Apptag/Localization/tr.json
@@ -8,6 +8,8 @@
   "menu.language": "Dil",
   "menu.help": "Yardım",
   "menu.version": "Sürüm",
+  "menu.proStatus.free": "Şu anda ücretsiz kullanıcısınız (Pro’ya yükseltin)",
+  "menu.proStatus.unlocked": "Şu anda değerli bir Pro kullanıcısınız",
   "settings.language": "Dil",
   "settings.languagePicker": "Uygulama dili:",
   "settings.languageDesc": "Menülerde ve ayarlarda kullanılan dili seçin.",
diff --git a/src/Apptag/Localization/uk.json b/src/Apptag/Localization/uk.json
index d2ac906..9169ccb 100644
--- a/src/Apptag/Localization/uk.json
+++ b/src/Apptag/Localization/uk.json
@@ -8,6 +8,8 @@
   "menu.language": "Мова",
   "menu.help": "Довідка",
   "menu.version": "Версія",
+  "menu.proStatus.free": "Зараз ви користувач безкоштовної версії (оновити до Pro)",
+  "menu.proStatus.unlocked": "Зараз ви цінний користувач Pro",
   "settings.language": "Мова",
   "settings.languagePicker": "Мова застосунку:",
   "settings.languageDesc": "Оберіть мову меню та налаштувань.",
diff --git a/src/Apptag/Localization/vi.json b/src/Apptag/Localization/vi.json
index bb046b0..37a643c 100644
--- a/src/Apptag/Localization/vi.json
+++ b/src/Apptag/Localization/vi.json
@@ -8,6 +8,8 @@
   "menu.language": "Ngôn ngữ",
   "menu.help": "Trợ giúp",
   "menu.version": "Phiên bản",
+  "menu.proStatus.free": "Bạn hiện là người dùng Miễn phí (nâng cấp lên Pro)",
+  "menu.proStatus.unlocked": "Bạn hiện là người dùng Pro danh giá",
   "settings.language": "Ngôn ngữ",
   "settings.languagePicker": "Ngôn ngữ ứng dụng:",
   "settings.languageDesc": "Chọn ngôn ngữ dùng cho menu và cài đặt.",
diff --git a/src/Apptag/Localization/zh-Hans.json b/src/Apptag/Localization/zh-Hans.json
index 679c46e..94b0c07 100644
--- a/src/Apptag/Localization/zh-Hans.json
+++ b/src/Apptag/Localization/zh-Hans.json
@@ -8,6 +8,8 @@
   "menu.language": "语言",
   "menu.help": "帮助",
   "menu.version": "版本",
+  "menu.proStatus.free": "当前您是:免费版(升级到 Pro)用户",
+  "menu.proStatus.unlocked": "当前您是:尊贵的 Pro 版用户",
   "settings.language": "语言",
   "settings.languagePicker": "应用语言:",
   "settings.languageDesc": "选择菜单和设置面板使用的语言。",
diff --git a/src/Apptag/Localization/zh-Hant.json b/src/Apptag/Localization/zh-Hant.json
index c205797..892f5c0 100644
--- a/src/Apptag/Localization/zh-Hant.json
+++ b/src/Apptag/Localization/zh-Hant.json
@@ -8,6 +8,8 @@
   "menu.language": "語言",
   "menu.help": "說明",
   "menu.version": "版本",
+  "menu.proStatus.free": "目前您是:免費版(升級到 Pro)使用者",
+  "menu.proStatus.unlocked": "目前您是:尊貴的 Pro 版使用者",
   "settings.language": "語言",
   "settings.languagePicker": "應用程式語言:",
   "settings.languageDesc": "選擇選單和設定面板使用的語言。",
diff --git a/src/Apptag/PreferencesView.swift b/src/Apptag/PreferencesView.swift
index 0ac3ceb..52b09ab 100644
--- a/src/Apptag/PreferencesView.swift
+++ b/src/Apptag/PreferencesView.swift
@@ -578,7 +578,7 @@
         }
         switch proEntitlement.accessState {
         case .free:
-            return .locked
+            return .neutral
         case .purchased:
             return .unlocked
         case .legacy:
@@ -803,7 +803,7 @@
         )
     }
 
-    private var dataProStatusCard: some View {
+    private var proStatusCard: some View {
         HStack(alignment: .top, spacing: 18) {
             VStack(alignment: .leading, spacing: 10) {
                 HStack(spacing: 10) {
@@ -1139,8 +1139,6 @@
                 Spacer(minLength: 36)
 
                 VStack(alignment: .center, spacing: 24) {
-                    dataProStatusCard
-
                     dataSection(minHeight: 124) {
                         HStack(alignment: .center, spacing: 12) {
                             dataSectionTitle(tr("settings.initialLayout"))
@@ -1291,62 +1289,67 @@
                     case .about:
             // Tab 6: About
             VStack(spacing: 0) {
-                Spacer(minLength: 72)
+                Spacer(minLength: 44)
 
-                HStack {
-                    if let icon = NSImage(named: NSImage.applicationIconName) {
-                        Image(nsImage: icon)
-                            .resizable()
-                            .frame(width: 128, height: 128)
-                    }
-                    VStack(alignment: .leading, spacing: 4) {
-                        Text("TagLauncher")
-                            .font(.title2)
-                            .fontWeight(.semibold)
-                        Text(tr("app.description"))
-                            .font(.body)
-                            .foregroundStyle(.secondary)
-                        Text("\(tr("app.version")) \(appVersion) (\(tr("app.build")) \(buildVersion))")
-                            .font(.callout)
-                            .foregroundStyle(.tertiary)
+                VStack(alignment: .center, spacing: 24) {
+                    proStatusCard
 
-                        Divider()
-                            .padding(.vertical, 4)
-
-                        HStack(spacing: 8) {
-                            Button {
-                                NSWorkspace.shared.open(helpPDFURL)
-                            } label: {
-                                Label(tr("help.openPDF"), systemImage: "questionmark.circle")
-                            }
-                            .buttonStyle(.borderedProminent)
-                            .controlSize(.regular)
-
-                            Button {
-                                NSPasteboard.general.clearContents()
-                                NSPasteboard.general.setString(helpPDFURL.absoluteString, forType: .string)
-                            } label: {
-                                Image(systemName: "link")
-                            }
-                            .buttonStyle(.bordered)
-                            .controlSize(.regular)
-                            .help(tr("help.copyLink"))
+                    HStack(alignment: .center, spacing: 24) {
+                        if let icon = NSImage(named: NSImage.applicationIconName) {
+                            Image(nsImage: icon)
+                                .resizable()
+                                .frame(width: 112, height: 112)
                         }
+                        VStack(alignment: .leading, spacing: 4) {
+                            Text("TagLauncher")
+                                .font(.title2)
+                                .fontWeight(.semibold)
+                            Text(tr("app.description"))
+                                .font(.body)
+                                .foregroundStyle(.secondary)
+                            Text("\(tr("app.version")) \(appVersion) (\(tr("app.build")) \(buildVersion))")
+                                .font(.callout)
+                                .foregroundStyle(.tertiary)
 
-                        Text(tr("help.currentLanguage"))
-                            .font(.caption)
-                            .foregroundStyle(.secondary)
-                            .padding(.bottom, 2)
+                            Divider()
+                                .padding(.vertical, 4)
 
-                        Text("万物之中,希望最美")
-                            .font(.caption)
-                            .foregroundStyle(.secondary)
-                        Text("永桔@shanghai3168@gmail.com")
-                            .font(.caption)
-                            .foregroundStyle(.secondary)
+                            HStack(spacing: 8) {
+                                Button {
+                                    NSWorkspace.shared.open(helpPDFURL)
+                                } label: {
+                                    Label(tr("help.openPDF"), systemImage: "questionmark.circle")
+                                }
+                                .buttonStyle(.borderedProminent)
+                                .controlSize(.regular)
+
+                                Button {
+                                    NSPasteboard.general.clearContents()
+                                    NSPasteboard.general.setString(helpPDFURL.absoluteString, forType: .string)
+                                } label: {
+                                    Image(systemName: "link")
+                                }
+                                .buttonStyle(.bordered)
+                                .controlSize(.regular)
+                                .help(tr("help.copyLink"))
+                            }
+
+                            Text(tr("help.currentLanguage"))
+                                .font(.caption)
+                                .foregroundStyle(.secondary)
+                                .padding(.bottom, 2)
+
+                            Text("万物之中,希望最美")
+                                .font(.caption)
+                                .foregroundStyle(.secondary)
+                            Text("永桔@shanghai3168@gmail.com")
+                                .font(.caption)
+                                .foregroundStyle(.secondary)
+                        }
                     }
+                    .frame(width: dataPanelWidth, alignment: .leading)
                 }
-                .frame(width: 560, alignment: .leading)
+                .frame(width: dataPanelWidth, alignment: .center)
 
                 Spacer(minLength: 30)
                 Spacer(minLength: 18)
diff --git a/src/Apptag/ProAccessViews.swift b/src/Apptag/ProAccessViews.swift
index 1d4bcd6..51c1daa 100644
--- a/src/Apptag/ProAccessViews.swift
+++ b/src/Apptag/ProAccessViews.swift
@@ -14,7 +14,14 @@
     var compact = false
 
     var body: some View {
-        Text(text)
+        HStack(spacing: compact ? 4 : 5) {
+            if showsCrown {
+                Image(systemName: "crown.fill")
+                    .font(.system(size: compact ? 9 : 10, weight: .bold))
+            }
+
+            Text(text)
+        }
             .font(.system(size: compact ? 10 : 11, weight: .semibold))
             .foregroundStyle(foregroundColor)
             .lineLimit(1)
@@ -33,11 +40,11 @@
     private var foregroundColor: Color {
         switch style {
         case .locked:
-            return Color.accentColor
+            return Color(red: 0.56, green: 0.35, blue: 0.03)
         case .preview:
             return Color(red: 0.30, green: 0.22, blue: 0.08)
         case .unlocked, .legacy:
-            return Color(red: 0.10, green: 0.42, blue: 0.24)
+            return Color(red: 0.56, green: 0.35, blue: 0.03)
         case .neutral:
             return .secondary
         }
@@ -46,11 +53,11 @@
     private var backgroundColor: Color {
         switch style {
         case .locked:
-            return Color.accentColor.opacity(0.10)
+            return Color(red: 1.0, green: 0.78, blue: 0.22).opacity(0.18)
         case .preview:
             return Color.orange.opacity(0.14)
         case .unlocked, .legacy:
-            return Color.green.opacity(0.12)
+            return Color(red: 1.0, green: 0.78, blue: 0.22).opacity(0.18)
         case .neutral:
             return Color.secondary.opacity(0.10)
         }
@@ -59,15 +66,24 @@
     private var strokeColor: Color {
         switch style {
         case .locked:
-            return Color.accentColor.opacity(0.26)
+            return Color(red: 0.93, green: 0.62, blue: 0.10).opacity(0.42)
         case .preview:
             return Color.orange.opacity(0.28)
         case .unlocked, .legacy:
-            return Color.green.opacity(0.26)
+            return Color(red: 0.93, green: 0.62, blue: 0.10).opacity(0.42)
         case .neutral:
             return Color.secondary.opacity(0.18)
         }
     }
+
+    private var showsCrown: Bool {
+        switch style {
+        case .locked, .unlocked, .legacy:
+            return text.localizedCaseInsensitiveContains("Pro")
+        case .preview, .neutral:
+            return false
+        }
+    }
 }
 
 extension ProFeature {
diff --git a/src/Apptag/QuickSearch.swift b/src/Apptag/QuickSearch.swift
index ccbf454..5df8d3b 100644
--- a/src/Apptag/QuickSearch.swift
+++ b/src/Apptag/QuickSearch.swift
@@ -1432,6 +1432,15 @@
         trackingAreaRef = area
     }
 
+    override func hitTest(_ point: NSPoint) -> NSView? {
+        guard !isHidden, alphaValue > 0, bounds.contains(point) else { return nil }
+        return self
+    }
+
+    override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
+        true
+    }
+
     override func mouseEntered(with event: NSEvent) {
         super.mouseEntered(with: event)
         if let result {
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index 5ec896c..407ba80 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -2,6 +2,16 @@
 
 ## [Unreleased]
 
+## [8.1.1] — 2026-06-26
+
+- 优化样式 2 / 样式 3 的 Mac 自带容器布局:瀑布流容器样式下固定到底部并横向占满,避免窄列过高挤压其它分组
+- 调整 Pro 状态入口:升级卡从“数据”页迁移到“关于”页,数据页重新专注于分类方案、导入导出和气泡显示位置设置
+- 菜单栏下拉项新增当前免费 / Pro 身份状态:免费用户可点击进入“关于”页升级;Pro 用户显示金色皇冠图标和 Pro 身份文案
+- 统一 Pro 胶囊为金色皇冠体系;免费版状态保持中性胶囊,避免把免费状态误读为已解锁 Pro
+- 修复窗口回归中发现的 Quick Search 点击边界:结果行内文字 / 图标区域也能稳定触发启动和关闭;点击 TagLauncher 自身结果时只关闭当前浮层,不再通过外部激活把 AppGrid 重新拉起
+- 补齐 29 个语言包的菜单栏 Pro 状态文案,并修正土耳其语免费用户文案 typo
+- 版本号更新为 `8.1.1`,Build 更新为 `20260626.2229`
+
 ## [8.1.0] — 2026-06-26
 
 - 新增免费版 + Pro 一次性买断解锁体系:核心标签整理、基础启动和 Quick Search 保持免费可用,高级主题、导入 / 导出布局、无限自定义备注和应用图标排序持久化纳入 Pro
diff --git a/src/Scripts/window_logic_qa.sh b/src/Scripts/window_logic_qa.sh
index 81962ab..7149bde 100755
--- a/src/Scripts/window_logic_qa.sh
+++ b/src/Scripts/window_logic_qa.sh
@@ -7,10 +7,14 @@
 LAUNCH_AGENT_LABEL="com.taglauncher.app"
 LAUNCH_AGENT_PLIST="$HOME/Library/LaunchAgents/$LAUNCH_AGENT_LABEL.plist"
 SAVED_STATE_DIR="$HOME/Library/Saved Application State/$LAUNCH_AGENT_LABEL.savedState"
+STORE_DIR="$HOME/Library/Application Support/TagLauncher"
+STORE_PATH="$STORE_DIR/tags.json"
 USER_GUI_DOMAIN="gui/$(id -u)"
 RESTORE_LAUNCH_AGENT=false
 LAUNCH_AGENT_PLIST_WAS_PRESENT=false
 LAUNCH_AGENT_BACKUP="$(mktemp -t taglauncher-launchagent.XXXXXX.plist)"
+STORE_WAS_PRESENT=false
+STORE_BACKUP="$(mktemp -t taglauncher-tags.XXXXXX.json)"
 DEFAULTS_DOMAIN="$LAUNCH_AGENT_LABEL"
 SHOW_DOCK_ICON_WAS_SET=false
 SHOW_DOCK_ICON_VALUE=""
@@ -46,6 +50,13 @@
   if [[ -f "$LAUNCH_AGENT_PLIST" ]]; then
     LAUNCH_AGENT_PLIST_WAS_PRESENT=true
     cp "$LAUNCH_AGENT_PLIST" "$LAUNCH_AGENT_BACKUP"
+  fi
+}
+
+backup_store() {
+  if [[ -f "$STORE_PATH" ]]; then
+    STORE_WAS_PRESENT=true
+    cp "$STORE_PATH" "$STORE_BACKUP"
   fi
 }
 
@@ -85,6 +96,16 @@
     fi
   else
     rm -f "$LAUNCH_AGENT_PLIST"
+  fi
+}
+
+restore_store() {
+  kill_all_taglauncher_instances
+  if [[ "$STORE_WAS_PRESENT" == true ]]; then
+    mkdir -p "$STORE_DIR"
+    cp "$STORE_BACKUP" "$STORE_PATH" || true
+  else
+    rm -f "$STORE_PATH"
   fi
 }
 
@@ -239,6 +260,7 @@
   restore_user_defaults
   restore_qa_environment
   restore_launch_agent_plist
+  restore_store
   if [[ "$RESTORE_LAUNCH_AGENT" == true && -f "$LAUNCH_AGENT_PLIST" ]]; then
     launchctl bootstrap "$USER_GUI_DOMAIN" "$LAUNCH_AGENT_PLIST" >/dev/null 2>&1 || true
   fi
@@ -246,6 +268,7 @@
 trap cleanup EXIT
 backup_user_defaults
 backup_launch_agent_plist
+backup_store
 backup_qa_environment
 
 kill_all_taglauncher_instances() {
@@ -458,12 +481,60 @@
   assert_single_qa_app_instance
 }
 
+seed_quick_search_recent_result() {
+  local app_path=""
+  for candidate in \
+    "/System/Applications/System Settings.app" \
+    "/System/Applications/Utilities/Terminal.app" \
+    "/System/Applications/Calculator.app" \
+    "/System/Applications/TextEdit.app"
+  do
+    if [[ -d "$candidate" ]]; then
+      app_path="$candidate"
+      break
+    fi
+  done
+
+  if [[ -z "$app_path" ]]; then
+    echo "FAIL: could not find a stable system app to seed Quick Search history" >&2
+    exit 1
+  fi
+
+  mkdir -p "$STORE_DIR"
+  python3 - "$STORE_PATH" "$app_path" <<'PY'
+import json
+import os
+import sys
+from datetime import datetime, timezone
+
+store_path, app_path = sys.argv[1], sys.argv[2]
+if os.path.exists(store_path):
+    with open(store_path, "r", encoding="utf-8") as handle:
+        try:
+            store = json.load(handle)
+        except json.JSONDecodeError:
+            store = {}
+else:
+    store = {}
+
+store.setdefault("version", 1)
+store.setdefault("appOpenCounts", {})
+store.setdefault("appLastOpenedAt", {})
+store["appOpenCounts"][app_path] = max(1, int(store["appOpenCounts"].get(app_path, 0)) + 1)
+reference = datetime(2001, 1, 1, tzinfo=timezone.utc)
+store["appLastOpenedAt"][app_path] = (datetime.now(timezone.utc) - reference).total_seconds()
+
+with open(store_path, "w", encoding="utf-8") as handle:
+    json.dump(store, handle, ensure_ascii=False, indent=2, sort_keys=True)
+PY
+}
+
 assert_swift="$(mktemp -t taglauncher-window-assert.XXXXXX.swift)"
 coords_swift="$(mktemp -t taglauncher-window-coords.XXXXXX.swift)"
 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"' EXIT
+trap 'cleanup; rm -f "$assert_swift" "$coords_swift" "$settings_ax_swift" "$screens_swift" "$fullscreen_swift" "$LAUNCH_AGENT_BACKUP" "$STORE_BACKUP"' EXIT
 
 cat >"$assert_swift" <<'SWIFT'
 import AppKit
@@ -1422,6 +1493,7 @@
 kill_all_taglauncher_instances
 sleep 0.4
 swift_assert no-overlay
+seed_quick_search_recent_result
 prepare_isolated_app_instance
 assert_no_dock_tile
 log "==> QA hidden Dock: clicking Quick Search result closes without showing App Grid"

--
Gitblit v1.9.3