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/DataLayer.swift |   61 +++++++++++++++++++++++++++---
 1 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/src/Apptag/DataLayer.swift b/src/Apptag/DataLayer.swift
index 9b88064..1357d95 100644
--- a/src/Apptag/DataLayer.swift
+++ b/src/Apptag/DataLayer.swift
@@ -821,6 +821,26 @@
             }
         }
 
+        for tagName in orderedTagDefinitionNames(
+            tagDefinitions: effectiveTagDefinitions,
+            tagOrder: tagOrder
+        ) {
+            let displayName = nameOverrides[tagName] ?? tagName
+            guard displayName != macCategory,
+                  displayName != defaultGroupName
+            else { continue }
+
+            if containerIDsByGroupName[displayName] == nil {
+                containerIDsByGroupName[displayName] = AppContainerID.forTag(
+                    tagName,
+                    definition: effectiveTagDefinitions[tagName]
+                )
+            }
+            if dict[displayName] == nil {
+                dict[displayName] = []
+            }
+        }
+
         // Build sort index from tagOrder: lower index = appears first
         var orderIndex: [String: Int] = [:]
         for (i, name) in tagOrder.enumerated() {
@@ -878,6 +898,18 @@
         let remaining = apps
             .filter { !orderedPaths.contains($0.path.path) }
             .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending }
+        return ordered + remaining
+    }
+
+    private static func orderedTagDefinitionNames(
+        tagDefinitions: [String: TagDatabase.TagDef],
+        tagOrder: [String]
+    ) -> [String] {
+        let ordered = tagOrder.filter { tagDefinitions[$0] != nil }
+        let orderedSet = Set(ordered)
+        let remaining = tagDefinitions.keys
+            .filter { !orderedSet.contains($0) }
+            .sorted()
         return ordered + remaining
     }
 
@@ -1519,12 +1551,14 @@
     // MARK: Export / Import
 
     static func exportTo(_ url: URL) throws {
+        try ProEntitlementPolicy.requireUnlocked(.layoutExport)
         let store = loadWithEnsuredCategoryScheme()
         let data = try JSONEncoder().encode(store)
         try data.write(to: url, options: .atomic)
     }
 
     static func importFrom(_ url: URL) throws -> Store {
+        try ProEntitlementPolicy.requireUnlocked(.layoutImport)
         let previousStore = loadWithEnsuredCategoryScheme()
         let data = try Data(contentsOf: url)
         var store = try JSONDecoder().decode(Store.self, from: data)
@@ -1721,12 +1755,11 @@
         guard !FileManager.default.fileExists(atPath: storeURL.path) else { return }
 
         let starterCategoryIDs: [SmartCategoryID] = [
-            .uiPrototyping,
-            .ide,
-            .writing,
-            .game,
+            .design,
+            .development,
+            .office,
             .entertainment,
-            .system,
+            .systemEnhancement,
             .gtd
         ]
 
@@ -1776,6 +1809,8 @@
 
     /// Persist the visible app order for one stable container.
     static func reorderApps(inContainer containerID: String, orderedPaths: [String]) {
+        guard ProEntitlementPolicy.isUnlocked(.persistentAppSorting) else { return }
+
         var store = TagDatabase.load()
         _ = TagDatabase.normalizeContainerAppOrder(in: &store)
         let previousStore = store
@@ -2105,11 +2140,22 @@
         TagDatabase.save(store)
     }
 
-    static func setAppNote(_ note: String, for path: String) {
+    @discardableResult
+    static func setAppNote(_ note: String, for path: String) -> ProNoteSaveDecision {
         var store = TagDatabase.load()
-        let previousStore = store
         let trimmed = note.trimmingCharacters(in: .whitespacesAndNewlines)
         let limited = String(trimmed.prefix(TagDatabase.maxAppNoteLength))
+        let decision = ProEntitlementPolicy.noteSaveDecision(
+            note: limited,
+            for: path,
+            in: store
+        )
+
+        guard case .allow = decision else {
+            return decision
+        }
+
+        let previousStore = store
         if limited.isEmpty {
             store.appNotes.removeValue(forKey: path)
             store.appNoteMetadata[path] = TagDatabase.AppNoteMetadata(
@@ -2135,6 +2181,7 @@
             previous: previousStore,
             reason: "edit-app-note"
         )
+        return decision
     }
 
     static func moveApp(path: String, from sourceTag: String, to targetTag: String, color: Int, copy: Bool) {

--
Gitblit v1.9.3