From e5627facd571293ebba8f05f970121716af39603 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 05 Jun 2026 01:30:07 +0800
Subject: [PATCH] Include CoreServices apps in quick search index

---
 Apptag/DataLayer.swift                |    2 +
 Apptag/Info.plist                     |    4 +-
 CHANGELOG.md                          |    6 +++
 Scripts/quick_search_system_app_qa.sh |   97 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/Apptag/DataLayer.swift b/Apptag/DataLayer.swift
index 36fd8c8..b77bf4c 100644
--- a/Apptag/DataLayer.swift
+++ b/Apptag/DataLayer.swift
@@ -109,6 +109,7 @@
     static let searchPaths: [URL] = [
         URL(fileURLWithPath: "/Applications"),
         URL(fileURLWithPath: "/System/Applications"),
+        URL(fileURLWithPath: "/System/Library/CoreServices/Applications"),
         URL(fileURLWithPath: "/System/Cryptexes/App/System/Applications"),
         URL(fileURLWithPath: "/System/Volumes/Preboot/Cryptexes/App/System/Applications"),
         FileManager.default.homeDirectoryForCurrentUser
@@ -117,6 +118,7 @@
 
     private static let systemAppPathPrefixes = [
         "/System/Applications/",
+        "/System/Library/CoreServices/Applications/",
         "/System/Cryptexes/App/System/Applications/",
         "/System/Volumes/Preboot/Cryptexes/App/System/Applications/"
     ]
diff --git a/Apptag/Info.plist b/Apptag/Info.plist
index 11f278e..8cdb717 100644
--- a/Apptag/Info.plist
+++ b/Apptag/Info.plist
@@ -19,9 +19,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>7.8.13</string>
+	<string>7.8.14</string>
 	<key>CFBundleVersion</key>
-	<string>20260604.1121</string>
+	<string>20260605.0122</string>
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.utilities</string>
 	<key>LSMinimumSystemVersion</key>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e21d2ae..90a7920 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # TagLauncher Changelog
 
+## [7.8.14] — 2026-06-05
+
+- 修复 Quick Search 和 App Grid 没有扫描 `/System/Library/CoreServices/Applications`,导致“钥匙串访问 / Keychain Access”等系统应用无法被检索和启动的问题
+- 该目录纳入轻量应用目录签名检查;目录未变化时仍不会触发额外索引刷新,避免拖慢打开 App Grid / Quick Search 的体验
+- 版本号更新为 `7.8.14`,Build 更新为 `20260605.0122`
+
 ## [7.8.13] — 2026-06-04
 
 - 修复 App 运行期间新安装应用后,App Grid 和 Quick Search 仍复用旧应用索引、无法立即检索到新应用的问题
diff --git a/Scripts/quick_search_system_app_qa.sh b/Scripts/quick_search_system_app_qa.sh
new file mode 100755
index 0000000..08e75a1
--- /dev/null
+++ b/Scripts/quick_search_system_app_qa.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+DATA_LAYER="$ROOT_DIR/Apptag/DataLayer.swift"
+KEYCHAIN_APP="/System/Library/CoreServices/Applications/Keychain Access.app"
+
+rg -Fq 'URL(fileURLWithPath: "/System/Library/CoreServices/Applications")' "$DATA_LAYER"
+rg -Fq '"/System/Library/CoreServices/Applications/"' "$DATA_LAYER"
+
+swift - "$KEYCHAIN_APP" <<'SWIFT'
+import Foundation
+import CoreServices
+
+let appPath = CommandLine.arguments[1]
+let appURL = URL(fileURLWithPath: appPath)
+
+func fail(_ message: String) -> Never {
+    FileHandle.standardError.write(Data("FAIL: \(message)\n".utf8))
+    exit(1)
+}
+
+guard FileManager.default.fileExists(atPath: appPath) else {
+    fail("Keychain Access app is missing at \(appPath)")
+}
+
+let indexedSearchPaths = [
+    "/Applications",
+    "/System/Applications",
+    "/System/Library/CoreServices/Applications",
+    "/System/Cryptexes/App/System/Applications",
+    "/System/Volumes/Preboot/Cryptexes/App/System/Applications",
+    FileManager.default.homeDirectoryForCurrentUser
+        .appendingPathComponent("Applications")
+        .path
+]
+
+guard indexedSearchPaths.contains(where: { appPath.hasPrefix($0 + "/") }) else {
+    fail("Keychain Access is not under a configured app search path")
+}
+
+var names: [String] = [
+    appURL.deletingPathExtension().lastPathComponent
+]
+
+if let item = MDItemCreate(nil, appPath as CFString),
+   let displayName = MDItemCopyAttribute(item, kMDItemDisplayName) as? String {
+    names.append(displayName)
+}
+
+if let bundle = Bundle(url: appURL),
+   let loctableURL = bundle.url(forResource: "InfoPlist", withExtension: "loctable"),
+   let loctable = NSDictionary(contentsOf: loctableURL) as? [String: Any],
+   let zhTable = loctable["zh_CN"] as? [String: Any] {
+    if let value = zhTable["CFBundleDisplayName"] as? String { names.append(value) }
+    if let value = zhTable["CFBundleName"] as? String { names.append(value) }
+}
+
+func normalize(_ value: String) -> String {
+    value
+        .trimmingCharacters(in: .whitespacesAndNewlines)
+        .folding(options: [.caseInsensitive, .diacriticInsensitive], locale: .current)
+        .lowercased()
+}
+
+func pinyinCandidates(for value: String) -> [String] {
+    let mutable = NSMutableString(string: value)
+    CFStringTransform(mutable, nil, kCFStringTransformToLatin, false)
+    CFStringTransform(mutable, nil, kCFStringTransformStripCombiningMarks, false)
+    let spaced = normalize(mutable as String)
+        .components(separatedBy: CharacterSet.alphanumerics.inverted)
+        .filter { !$0.isEmpty }
+        .joined(separator: " ")
+    let compact = spaced.replacingOccurrences(of: " ", with: "")
+    let initials = spaced
+        .split(separator: " ")
+        .compactMap(\.first)
+        .map(String.init)
+        .joined()
+    var seen = Set<String>()
+    return [spaced, compact, initials].filter { !$0.isEmpty && seen.insert($0).inserted }
+}
+
+let searchable = names.flatMap { name in
+    [normalize(name)] + pinyinCandidates(for: name)
+}
+
+let queries = ["keychain", "钥匙串", "yaoshichuan", "ysc"]
+for query in queries {
+    let token = normalize(query)
+    guard searchable.contains(where: { $0 == token || $0.hasPrefix(token) || $0.contains(token) }) else {
+        fail("query \(query) did not match Keychain Access candidates: \(searchable)")
+    }
+}
+
+print("PASS Keychain Access indexed search candidates: \(names.joined(separator: " | "))")
+SWIFT

--
Gitblit v1.9.3