Ariver
2026-06-30 70a1cc2e63d2c49a135bd89cec3ffb1d427d90ee
src/Apptag/ContentView.swift
@@ -19,6 +19,8 @@
    static let theme = "theme"
    static let tags = "tags"
    static let data = "data"
    static let pro = "pro"
    static let about = "about"
}
// MARK: - Edit Phase
@@ -213,19 +215,21 @@
struct TagPill: View {
    let name: String
    let colorIndex: Int
    var customColor: TagCustomColor? = nil
    var dragModeActive: Bool = false
    var isDragging: Bool = false
    let action: () -> Void
    private var resolvedNSColor: NSColor {
        TagColor.nsColor(for: colorIndex, customColor: customColor)
    }
    private var bgColor: Color {
        Color(nsColor: TagColor.nsColor(for: colorIndex))
        Color(nsColor: resolvedNSColor)
    }
    private var textColor: Color {
        if colorIndex == 0 || colorIndex == 5 {
            return .primary
        }
        return .white
        TagColor.prefersDarkText(for: resolvedNSColor) ? .primary : .white
    }
    var body: some View {
@@ -252,15 +256,21 @@
struct SideTagPill: View {
    let name: String
    let colorIndex: Int
    var customColor: TagCustomColor? = nil
    var dragModeActive: Bool = false
    var isDragging: Bool = false
    let action: () -> Void
    private var bgColor: Color {
        Color(nsColor: TagColor.nsColor(for: colorIndex))
    private var resolvedNSColor: NSColor {
        TagColor.nsColor(for: colorIndex, customColor: customColor)
    }
    private var bgColor: Color {
        Color(nsColor: resolvedNSColor)
    }
    private var textColor: Color {
        colorIndex == 0 || colorIndex == 5 ? .primary : .white
        TagColor.prefersDarkText(for: resolvedNSColor) ? .primary : .white
    }
    var body: some View {
@@ -375,6 +385,7 @@
    @State private var allApps: [AppInfo] = []
    @State private var displayGroups: [TagGroup] = []
    @State private var tagColors: [String: Int] = [:]
    @State private var tagCustomColors: [String: TagCustomColor] = [:]
    @State private var tagDefinitions: [String: TagDatabase.TagDef] = [:]
    @State private var containerAppOrder: [String: [String]] = [:]
    @State private var groupLayoutVersion = 0
@@ -447,6 +458,8 @@
    @AppStorage("showUncommonAppBubbles") private var showUncommonAppBubbles = AppDefaults.showUncommonAppBubbles
    @AppStorage("hideUsageTips") private var hideUsageTips = AppDefaults.hideUsageTips
    @AppStorage(AppGridTheme.storageKey) private var appGridThemeID = AppDefaults.appGridThemeID
    @AppStorage(AppGridTheme.deepBlueTuningStorageKey) private var deepBlueThemeTuning = AppGridTheme.deepBlue.defaultColorTuning
    @AppStorage(AppGridTheme.colorfulTuningStorageKey) private var colorfulThemeTuning = AppGridTheme.colorful.defaultColorTuning
    @AppStorage("useAppKitTagNavigation") private var useAppKitTagNavigation = AppDefaults.useAppKitTagNavigation
    @AppStorage("skipTagRemovalDropConfirm") private var skipTagRemovalDropConfirm = false
    @AppStorage("skipUncategorizedDropConfirm") private var skipUncategorizedDropConfirm = false
@@ -488,6 +501,29 @@
            : .defaultLight
    }
    private var renderedAppGridThemeTuning: Double? {
        guard editPhase == .none else { return nil }
        return ProEntitlementPolicy.effectiveThemeTuning(
            for: appGridTheme,
            storedTuning: storedThemeTuning(for: appGridTheme)
        )
    }
    private var renderedDisplayMode: String {
        ProEntitlementPolicy.effectiveDisplayMode(for: displayMode)
    }
    private func storedThemeTuning(for theme: AppGridTheme) -> Double? {
        switch theme {
        case .deepBlue:
            return deepBlueThemeTuning
        case .colorful:
            return colorfulThemeTuning
        case .defaultLight, .black, .pink, .purple, .green, .blue:
            return nil
        }
    }
    private var isSideLayout: Bool {
        tagPosition == "left" || tagPosition == "right"
    }
@@ -497,7 +533,7 @@
    }
    private var isColorlessContainerMode: Bool {
        displayMode == "container" || displayMode == "gridContainer"
        renderedDisplayMode == "container" || renderedDisplayMode == "gridContainer"
    }
    private var shouldShowUsageTips: Bool {
@@ -718,6 +754,8 @@
            // Always sync tag list from database when entering edit mode
            let store = TagDatabase.load()
            tagColors = store.tags.mapValues { $0.color }
            tagCustomColors = store.tags.compactMapValues { $0.customColor }
            tagDefinitions = store.tags
            draggedTagNames = TagEditor.orderedTagNames()
        }
        editPhase = phase
@@ -991,6 +1029,7 @@
    private var appGridBackground: some View {
        let theme = renderedAppGridTheme
        let tuning = renderedAppGridThemeTuning
        return ZStack {
            if theme.usesVisualEffectBackdrop {
                VisualEffectView(
@@ -1003,12 +1042,12 @@
            }
            if !theme.isDefaultLight {
                LinearGradient(
                    colors: theme.backgroundBaseColors,
                    colors: theme.backgroundBaseColors(tuning: tuning),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
                LinearGradient(
                    colors: theme.backgroundAccentColors,
                    colors: theme.backgroundAccentColors(tuning: tuning),
                    startPoint: .topTrailing,
                    endPoint: .bottomLeading
                )
@@ -1162,6 +1201,7 @@
            HStack(spacing: 8) {
                ForEach(tagLabels) { tag in
                    TagPill(name: tag.name, colorIndex: tag.colorIndex,
                            customColor: tag.customColor,
                            dragModeActive: tagNavDragModeActive && canReorderTag(tag.name),
                            isDragging: tagNavDragItem == tag.name,
                            action: {
@@ -1189,6 +1229,7 @@
            VStack(spacing: 6) {
                ForEach(tagLabels) { tag in
                    SideTagPill(name: tag.name, colorIndex: tag.colorIndex,
                                customColor: tag.customColor,
                                dragModeActive: tagNavDragModeActive && canReorderTag(tag.name),
                                isDragging: tagNavDragItem == tag.name,
                                action: {
@@ -1226,7 +1267,8 @@
                AppGridCollectionView(
                    groups: displayGroups,
                    tagColors: tagColors,
                    displayMode: displayMode,
                    tagCustomColors: tagCustomColors,
                    displayMode: renderedDisplayMode,
                    iconSize: iconSize,
                    showNames: !hideAppNames,
                    appGridTheme: renderedAppGridTheme,
@@ -1353,7 +1395,10 @@
            TagEditorView(
                tagColors: $tagColors,
                tagCustomColors: $tagCustomColors,
                excludedTagNames: ["Mac自带", defaultGroupName],
                isCustomColorUnlocked: proEntitlement.isUnlocked,
                onLockedCustomColor: { presentProPrompt(for: .customTagColors) },
                onRefresh: { refreshApps() }
            )
        }
@@ -1986,7 +2031,13 @@
    }
    private var tagLabels: [TagNavigationItem] {
        displayGroups.map { TagNavigationItem(name: $0.name, colorIndex: tagColors[$0.name] ?? 0) }
        displayGroups.map {
            TagNavigationItem(
                name: $0.name,
                colorIndex: tagColors[$0.name] ?? 0,
                customColor: tagCustomColors[$0.name]
            )
        }
    }
    private var editGroups: [TagGroup] {
@@ -2206,6 +2257,10 @@
        if let preview = proEntitlement.themePreviewState {
            appGridThemeID = preview.theme.rawValue
            proEntitlement.stopThemePreview()
        }
        if let preview = proEntitlement.displayModePreviewState {
            displayMode = preview.displayMode
            proEntitlement.stopDisplayModePreview()
        }
        let hadPrompt = appGridInteraction.proPrompt != nil
@@ -2923,6 +2978,7 @@
        quickSearchDocuments = snapshot.quickSearchDocuments
        tagColors = snapshot.tagColors
        tagDefinitions = snapshot.tagDefinitions
        tagCustomColors = snapshot.tagDefinitions.compactMapValues { $0.customColor }
        containerAppOrder = snapshot.containerAppOrder
        draggedTagNames = snapshot.tagOrder
        rebuildDisplayGroups(apps: snapshot.apps, tagOrder: snapshot.tagOrder)
@@ -2953,6 +3009,15 @@
            closeQuickSearch(hideOverlayIfNeeded: true)
            hideOverlay()
        }
        if isCurrentApp(app) {
            if closeQuickSearchOnSuccess && !closeQuickSearchAndOverlayBeforeOpening {
                closeQuickSearch(hideOverlayIfNeeded: closeOverlayOnSuccess)
            }
            if closeOverlayOnSuccess && !closeQuickSearchAndOverlayBeforeOpening {
                hideOverlay()
            }
            return
        }
        let configuration = NSWorkspace.OpenConfiguration()
        NSWorkspace.shared.openApplication(at: app.path, configuration: configuration) { _, error in
            DispatchQueue.main.async {
@@ -2974,6 +3039,16 @@
        }
    }
    private func isCurrentApp(_ app: AppInfo) -> Bool {
        if app.bundleIdentifier?.caseInsensitiveCompare(AppIdentity.bundleIdentifier) == .orderedSame {
            return true
        }
        let currentURL = Bundle.main.bundleURL.standardizedFileURL.resolvingSymlinksInPath()
        let appURL = app.path.standardizedFileURL.resolvingSymlinksInPath()
        return currentURL == appURL
    }
    func openApp(_ app: AppInfo) {
        hideOverlay()
        launchApp(app, closeOverlayOnSuccess: false)