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