Ariver
2026-06-27 28be00ea53666fd5500a702a3c187c0e27dd90ad
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) {