From 28be00ea53666fd5500a702a3c187c0e27dd90ad Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sat, 27 Jun 2026 16:26:30 +0800
Subject: [PATCH] Simplify SmartStart default categories

---
 src/Apptag/AppleDefaultAppCatalog.swift |  180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 169 insertions(+), 11 deletions(-)

diff --git a/src/Apptag/AppleDefaultAppCatalog.swift b/src/Apptag/AppleDefaultAppCatalog.swift
index 120c2a4..1a9f1ea 100644
--- a/src/Apptag/AppleDefaultAppCatalog.swift
+++ b/src/Apptag/AppleDefaultAppCatalog.swift
@@ -61,6 +61,58 @@
     private static let cacheLock = NSLock()
     private static var cachedBaseSnapshot: BaseSnapshot?
     private static var cachedLocalizationSnapshots: [String: LocalizationSnapshot] = [:]
+    private static let legacyDefaultNoteFingerprintsByBundleIdentifier: [String: Set<String>] = [
+        "com.apple.chess": [
+            "9f72f1448c7e9b47",
+            "a3b754a4f06eee33",
+            "abc8528f6a2ca356"
+        ],
+        "com.apple.freeform": [
+            "32fee0886cf2c6bb",
+            "503c25567e9640c2",
+            "aee5e9a7730252f7"
+        ],
+        "com.apple.home": [
+            "003e996159a187cc",
+            "2707ce5206adfb2a",
+            "5b0207586af532a9"
+        ],
+        "com.apple.music": [
+            "545fa7c8a0d216fa",
+            "8867798c868c8c30",
+            "bce11863b08fac8b"
+        ],
+        "com.apple.passwords": [
+            "58b38191593ec9b9",
+            "a72f91e414c90254",
+            "fa1ce5e1bb5998cd"
+        ],
+        "com.apple.mobilephone": [
+            "703bd99a660e80e4",
+            "80d0df54c7ddf2d1",
+            "9d57741d3114a1a2"
+        ],
+        "com.apple.tv": [
+            "6a29d8ece97cd432",
+            "e1efb690bf1c6b94",
+            "e28785e11f914401"
+        ],
+        "com.apple.backup.launcher": [
+            "78edffa39f28cee2",
+            "cbf81a2b5e77725b",
+            "dc568bb30164e897"
+        ],
+        "com.apple.diskutility": [
+            "77324bc5de72e176",
+            "c43f8e117573f5ed",
+            "fd581938fd34e888"
+        ],
+        "com.apple.screencontinuity": [
+            "171a629bd17970fa",
+            "2965e73ff5566d19",
+            "bb4662c4f15ab83c"
+        ]
+    ]
 
     static func isKnownAppleApp(_ app: AppInfo) -> Bool {
         entry(for: app) != nil
@@ -75,6 +127,7 @@
         let categoryIDs = entry.defaultTag
             .compactMap { SmartCategoryID(rawValue: $0) }
             .filter { $0 != .other }
+            .map(\.smartStartDefaultCategoryID)
         guard !categoryIDs.isEmpty else { return nil }
         return AppleDefaultCategorization(
             canonicalName: entry.canonicalName,
@@ -108,33 +161,78 @@
 
     @discardableResult
     static func relocalizeDefaultNotesForCurrentLanguage(apps: [AppInfo]) -> Bool {
+        guard let base = loadBaseSnapshot() else { return false }
+
         var store = TagDatabase.load()
+        let originalStore = store
         var changed = false
+        var adoptedLegacyDefaultNote = false
 
         for app in apps where app.isAppleApp {
             let path = app.path.path
-            guard let currentNote = normalizedNote(store.appNotes[path]) else { continue }
-            guard let metadata = store.appNoteMetadata[path],
-                  metadata.origin == .appleDefault
+            guard let bundleIdentifier = normalizedBundleIdentifier(app.bundleIdentifier),
+                  base.byBundleIdentifier[bundleIdentifier] != nil,
+                  let currentNote = normalizedNote(store.appNotes[path], limit: base.noteLimit),
+                  let localizedNote = localizedNote(forBundleIdentifier: bundleIdentifier)
             else { continue }
+
+            let metadata = store.appNoteMetadata[path]
+            if metadata?.origin == .manual {
+                continue
+            }
 
             let currentFingerprint = TagDatabase.noteFingerprint(currentNote)
-            let expectedFingerprint = metadata.apple?.noteFingerprint ?? metadata.noteFingerprint
-            guard expectedFingerprint == currentFingerprint else { continue }
-            guard let defaultNote = Self.defaultNote(for: app),
-                  defaultNote.note != currentNote
-            else { continue }
+            let matchesKnownAppleDefault = noteMatchesKnownDefault(
+                currentNote,
+                forBundleIdentifier: bundleIdentifier,
+                base: base
+            )
 
-            store.appNotes[path] = defaultNote.note
+            let canRelocalize: Bool
+            switch metadata?.origin {
+            case .some(.manual):
+                canRelocalize = false
+            case .some(.appleDefault):
+                let expectedFingerprint = metadata?.apple?.noteFingerprint ?? metadata?.noteFingerprint
+                canRelocalize = expectedFingerprint == currentFingerprint || matchesKnownAppleDefault
+                if expectedFingerprint != currentFingerprint && matchesKnownAppleDefault {
+                    adoptedLegacyDefaultNote = true
+                }
+            case .some(.catalogDefault):
+                canRelocalize = matchesKnownAppleDefault
+                if canRelocalize {
+                    adoptedLegacyDefaultNote = true
+                }
+            case .none:
+                canRelocalize = matchesKnownAppleDefault
+                if canRelocalize {
+                    adoptedLegacyDefaultNote = true
+                }
+            }
+
+            guard canRelocalize else { continue }
+
+            let metadataMatchesLocalizedNote = metadata?.origin == .appleDefault
+                && metadata?.apple?.entryID == localizedNote.provenance.entryID
+                && metadata?.apple?.languageCode == localizedNote.provenance.languageCode
+                && metadata?.apple?.notesVersion == localizedNote.provenance.notesVersion
+                && metadata?.apple?.noteFingerprint == localizedNote.provenance.noteFingerprint
+                && metadata?.noteFingerprint == localizedNote.provenance.noteFingerprint
+            guard localizedNote.note != currentNote || !metadataMatchesLocalizedNote else { continue }
+
+            store.appNotes[path] = localizedNote.note
             store.appNoteMetadata[path] = TagDatabase.AppNoteMetadata(
                 origin: .appleDefault,
-                apple: defaultNote.provenance,
-                noteFingerprint: defaultNote.provenance.noteFingerprint
+                apple: localizedNote.provenance,
+                noteFingerprint: localizedNote.provenance.noteFingerprint
             )
             changed = true
         }
 
         if changed {
+            if adoptedLegacyDefaultNote {
+                _ = TagDatabase.backup(originalStore, reason: "apple-default-note-migration")
+            }
             TagDatabase.save(store)
         }
         return changed
@@ -168,6 +266,66 @@
         return nil
     }
 
+    private static func noteMatchesKnownDefault(
+        _ note: String,
+        forBundleIdentifier bundleIdentifier: String,
+        base: BaseSnapshot
+    ) -> Bool {
+        let fingerprint = TagDatabase.noteFingerprint(note)
+        return knownDefaultNoteFingerprints(
+            forBundleIdentifier: bundleIdentifier,
+            base: base
+        ).contains(fingerprint)
+    }
+
+    private static func knownDefaultNoteFingerprints(
+        forBundleIdentifier bundleIdentifier: String,
+        base: BaseSnapshot
+    ) -> Set<String> {
+        var result = Set<String>()
+        for languageCode in base.supportedLanguages {
+            guard let snapshot = loadLocalizationSnapshot(languageCode: languageCode),
+                  let note = normalizedNote(snapshot.notes[bundleIdentifier], limit: base.noteLimit)
+            else { continue }
+            for variant in legacyDefaultNoteVariants(note, limit: base.noteLimit) {
+                result.insert(TagDatabase.noteFingerprint(variant))
+            }
+        }
+        result.formUnion(legacyDefaultNoteFingerprintsByBundleIdentifier[bundleIdentifier] ?? [])
+        return result
+    }
+
+    private static func legacyDefaultNoteVariants(_ note: String, limit: Int) -> [String] {
+        let trimmed = String(note.trimmingCharacters(in: .whitespacesAndNewlines).prefix(limit))
+        guard !trimmed.isEmpty else { return [] }
+
+        var variants = [trimmed]
+        let stripped = noteByRemovingTrailingSentencePunctuation(trimmed)
+        if stripped != trimmed {
+            variants.append(stripped)
+        }
+
+        for suffix in [".", "。"] {
+            let punctuated = String((stripped + suffix).prefix(limit))
+            if !punctuated.isEmpty {
+                variants.append(punctuated)
+            }
+        }
+
+        var seen = Set<String>()
+        return variants.filter { seen.insert($0).inserted }
+    }
+
+    private static func noteByRemovingTrailingSentencePunctuation(_ value: String) -> String {
+        var result = value.trimmingCharacters(in: .whitespacesAndNewlines)
+        let punctuation = CharacterSet(charactersIn: ".。.!!??")
+        while let scalar = result.unicodeScalars.last,
+              punctuation.contains(scalar) {
+            result.removeLast()
+        }
+        return result
+    }
+
     private static func loadBaseSnapshot() -> BaseSnapshot? {
         cacheLock.lock()
         if let snapshot = cachedBaseSnapshot {

--
Gitblit v1.9.3