Ariver
2026-06-09 dd1c9fc4c531f1aceaa6d64bbee919ec464655b7
Release 7.8.26 helper app filtering
6 files modified
92 ■■■■ changed files
src/Apptag/DataLayer.swift 30 ●●●● patch | view | raw | blame | history
src/Apptag/Info.plist 4 ●●●● patch | view | raw | blame | history
src/Apptag/QuickSearch.swift 2 ●●● patch | view | raw | blame | history
src/CHANGELOG.md 7 ●●●●● patch | view | raw | blame | history
src/Scripts/quick_search_app_name_qa.sh 43 ●●●● patch | view | raw | blame | history
src/TODO.md 6 ●●●● patch | view | raw | blame | history
src/Apptag/DataLayer.swift
@@ -334,7 +334,7 @@
            }
        }
        return deduplicated(apps).sorted {
        return deduplicated(apps.filter { !isInternalHelperAppPath($0.path) }).sorted {
            $0.name.localizedStandardCompare($1.name) == .orderedAscending
        }
    }
@@ -363,10 +363,10 @@
        guard url.pathExtension.lowercased() == "app" else { return }
        let displayURL = url.standardizedFileURL
        guard !isNestedInsideAppBundle(displayURL) else { return }
        guard !isInternalHelperAppPath(displayURL) else { return }
        let resolvedURL = displayURL.resolvingSymlinksInPath().standardizedFileURL
        guard !isNestedInsideAppBundle(resolvedURL) else { return }
        guard !isInternalHelperAppPath(resolvedURL) else { return }
        guard seenResolvedPaths.insert(resolvedURL.path).inserted else { return }
        let bundle = Bundle(url: displayURL) ?? Bundle(url: resolvedURL)
@@ -624,11 +624,29 @@
    }
    static func isNestedInsideAppBundle(_ url: URL) -> Bool {
        let components = url.standardizedFileURL.pathComponents
        guard let lastAppIndex = components.lastIndex(where: { $0.lowercased().hasSuffix(".app") }) else {
        isNestedInsideAppBundlePath(url.standardizedFileURL.path)
    }
    static func isInternalHelperAppPath(_ url: URL) -> Bool {
        let path = url.standardizedFileURL.path
        let lowercasedPath = path.lowercased()
        return isNestedInsideAppBundlePath(path)
            || lowercasedPath.contains("/contents/helpers/")
            || lowercasedPath.contains("/contents/xpcservices/")
            || lowercasedPath.contains("/wrapper/")
    }
    private static func isNestedInsideAppBundlePath(_ path: String) -> Bool {
        let lowercasedPath = path.lowercased()
        guard lowercasedPath.hasSuffix(".app") else { return false }
        let components = lowercasedPath.split(separator: "/", omittingEmptySubsequences: true)
        guard let lastAppIndex = components.lastIndex(where: { $0.hasSuffix(".app") }) else {
            return false
        }
        return components[..<lastAppIndex].contains { $0.lowercased().hasSuffix(".app") }
        if components[..<lastAppIndex].contains(where: { $0.hasSuffix(".app") }) {
            return true
        }
        return lowercasedPath.range(of: ".app/", options: .caseInsensitive) != nil
    }
    private static func deduplicated(_ apps: [AppInfo]) -> [AppInfo] {
src/Apptag/Info.plist
@@ -19,9 +19,9 @@
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>7.8.25</string>
    <string>7.8.26</string>
    <key>CFBundleVersion</key>
    <string>20260609.1741</string>
    <string>20260609.2004</string>
    <key>LSApplicationCategoryType</key>
    <string>public.app-category.utilities</string>
    <key>LSMinimumSystemVersion</key>
src/Apptag/QuickSearch.swift
@@ -295,7 +295,7 @@
enum QuickSearchEngine {
    static func makeDocuments(apps: [AppInfo], store: TagDatabase.Store) -> [QuickSearchDocument] {
        apps
            .filter { !AppIndexer.isNestedInsideAppBundle($0.path) }
            .filter { !AppIndexer.isInternalHelperAppPath($0.path) }
            .map { app in
                let localizedNames = uniqueOrdered(AppDisplayNameResolver.searchAliases(for: app))
                let internalBundleNames = internalBundleNames(for: app)
src/CHANGELOG.md
@@ -1,5 +1,12 @@
# TagLauncher Changelog
## [7.8.26] — 2026-06-09
- 加固 Quick Search wrapper/helper 过滤:除 URL 组件判断外,直接识别 `.app/.../*.app`、`/Wrapper/`、`/Contents/Helpers/`、`/Contents/XPCServices/` 等内部 app 路径,避免内层 helper 继续作为独立搜索结果显示
- App 扫描层和 Quick Search 文档层同时应用内部 helper 过滤,确保“贝锐向日葵被控 / isunclient”这类 iOS-on-Mac wrapper 不再漏出内部英文技术名
- Quick Search 名称 QA 增加 helper path fixture,覆盖 `/Wrapper/`、`/Contents/Helpers/` 和 XPCServices 内部 app
- 版本号更新为 `7.8.26`,Build 更新为 `20260609.2004`
## [7.8.25] — 2026-06-09
- 新增统一 App 显示名解析策略:App Grid 和 Quick Search 标题优先使用当前语种的官方本地化名,再按同语系、系统/Finder/Spotlight 可见名、英文官方名和基础 bundle 名逐级 fallback
src/Scripts/quick_search_app_name_qa.sh
@@ -11,9 +11,10 @@
rg -Fq 'bundleDisplayNames' "$DATA_LAYER"
rg -Fq '.localizedNameKey' "$DATA_LAYER"
rg -Fq 'isNestedInsideAppBundle' "$DATA_LAYER"
rg -Fq 'isInternalHelperAppPath' "$DATA_LAYER"
rg -Fq '.skipsPackageDescendants' "$DATA_LAYER"
rg -Fq 'AppDisplayNameResolver.searchAliases' "$QUICK_SEARCH"
rg -Fq 'AppIndexer.isNestedInsideAppBundle($0.path)' "$QUICK_SEARCH"
rg -Fq 'AppIndexer.isInternalHelperAppPath($0.path)' "$QUICK_SEARCH"
swift - "$SUNLOGIN_APP" <<'SWIFT'
import Foundation
@@ -189,11 +190,29 @@
}
func isNestedInsideAppBundle(_ url: URL) -> Bool {
    let components = url.standardizedFileURL.pathComponents
    guard let lastAppIndex = components.lastIndex(where: { $0.lowercased().hasSuffix(".app") }) else {
    isNestedInsideAppBundlePath(url.standardizedFileURL.path)
}
func isInternalHelperAppPath(_ url: URL) -> Bool {
    let path = url.standardizedFileURL.path
    let lowercasedPath = path.lowercased()
    return isNestedInsideAppBundlePath(path)
        || lowercasedPath.contains("/contents/helpers/")
        || lowercasedPath.contains("/contents/xpcservices/")
        || lowercasedPath.contains("/wrapper/")
}
func isNestedInsideAppBundlePath(_ path: String) -> Bool {
    let lowercasedPath = path.lowercased()
    guard lowercasedPath.hasSuffix(".app") else { return false }
    let components = lowercasedPath.split(separator: "/", omittingEmptySubsequences: true)
    guard let lastAppIndex = components.lastIndex(where: { $0.hasSuffix(".app") }) else {
        return false
    }
    return components[..<lastAppIndex].contains { $0.lowercased().hasSuffix(".app") }
    if components[..<lastAppIndex].contains(where: { $0.hasSuffix(".app") }) {
        return true
    }
    return lowercasedPath.range(of: ".app/", options: .caseInsensitive) != nil
}
func spotlightDisplayName(for url: URL) -> String? {
@@ -215,7 +234,7 @@
func makeDocumentPaths(appPaths: [URL]) -> [String] {
    appPaths
        .filter { !isNestedInsideAppBundle($0) }
        .filter { !isInternalHelperAppPath($0) }
        .map(\.path)
}
@@ -290,11 +309,25 @@
    guard isNestedInsideAppBundle(nestedURL) else {
        fail("nested helper app was not classified as nested: \(nestedURL.path)")
    }
    guard isInternalHelperAppPath(nestedURL) else {
        fail("nested helper app was not classified as internal helper: \(nestedURL.path)")
    }
    guard bundleDisplayName(for: nestedURL) == expectedDisplayName else {
        fail("nested helper bundle display name did not expose localized name")
    }
}
let helperPathFixtures = [
    "/Applications/Outer.app/Contents/Helpers/Inner.app",
    "/Applications/Outer.app/Contents/XPCServices/Inner.app",
    "/Applications/Outer.app/Wrapper/Inner.app"
]
for helperPath in helperPathFixtures {
    guard isInternalHelperAppPath(URL(fileURLWithPath: helperPath)) else {
        fail("helper path fixture was not filtered: \(helperPath)")
    }
}
var enumeratedHits: [String] = []
if let enumerator = FileManager.default.enumerator(
    at: URL(fileURLWithPath: "/Applications"),
src/TODO.md
@@ -24,10 +24,10 @@
## Done
- [2026-06-09] 多语种 App 显示名通用加固。
  - 结果: 新增统一 `AppDisplayNameResolver`;App Grid 与 Quick Search 标题优先走当前语种/同语系/系统可见名/英文官方名/基础 bundle 名 fallback,内部 bundle 名和 bundle id 只作为搜索别名。
  - 范围: `AppInfo` 增加系统显示名与 bundle 显示名候选;Quick Search 文档使用 resolver 生成别名并二次过滤嵌套 `.app`;版本更新为 `7.8.25 (20260609.1741)`。
  - 结果: 新增统一 `AppDisplayNameResolver`;App Grid 与 Quick Search 标题优先走当前语种/同语系/系统可见名/英文官方名/基础 bundle 名 fallback,内部 bundle 名和 bundle id 只作为搜索别名;`7.8.26` 进一步加固 `/Wrapper/`、`/Contents/Helpers/`、`.app/.../*.app` 内部 helper 过滤。
  - 范围: `AppInfo` 增加系统显示名与 bundle 显示名候选;App 扫描层和 Quick Search 文档层都过滤内部 helper;Quick Search 文档使用 resolver 生成别名;版本更新为 `7.8.26 (20260609.2004)`。
  - 不改范围: 未做第三方 App 名称机器翻译,未改 Quick Search 排名算法、App Grid 布局、窗口层级或 Smart Start 分类规则。
  - 验证: `APP_BUILD=20260609.1741 bash build.sh`、`codesign --verify --deep --strict src/build/TagLauncher.app`、`Scripts/quick_search_app_name_qa.sh`、`Scripts/quick_search_system_app_qa.sh` 通过;构建产物 Info.plist 为 `7.8.25 (20260609.1741)`。
  - 验证: `APP_BUILD=20260609.2004 bash build.sh`、`codesign --verify --deep --strict src/build/TagLauncher.app`、`Scripts/quick_search_app_name_qa.sh`、`Scripts/quick_search_system_app_qa.sh` 通过;构建产物 Info.plist 为 `7.8.26 (20260609.2004)`。
- [2026-06-09] 修复首安 Smart Start 提示确认后 App Grid 消失的问题。
  - 结果: Smart Start 自动整理完成提示出现期间会抑制 AppKit 级 backdrop dismiss;点击“OK/好的”只关闭提示弹窗,不再关闭整个 App Grid。