From 2774b3ea8fbcc13dc69bbd2cde9e641eeb105679 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 11 Jun 2026 19:06:33 +0800
Subject: [PATCH] Add uncategorized layout reset option

---
 src/Apptag/PreferencesView.swift |  122 +++++++++++++++++++++++++++++++++++-----
 1 files changed, 106 insertions(+), 16 deletions(-)

diff --git a/src/Apptag/PreferencesView.swift b/src/Apptag/PreferencesView.swift
index 7c4b398..915db67 100644
--- a/src/Apptag/PreferencesView.swift
+++ b/src/Apptag/PreferencesView.swift
@@ -36,8 +36,14 @@
     }
 }
 
+private enum InitialLayoutMode: String {
+    case uncategorized
+    case smart
+}
+
 struct PreferencesView: View {
     private let settingsWindowWidth: CGFloat = 1000
+    private let settingsWindowHeight: CGFloat = 640
     private let settingsContentWidth: CGFloat = 940
     private let generalContentWidth: CGFloat = 720
     private let generalLabelWidth: CGFloat = 190
@@ -64,11 +70,14 @@
     @State private var tagColors: [String: Int] = [:]
     @State private var categoryScheme = TagDatabase.CategorySchemeState()
     @State private var isApplyingSystemScheme = false
+    @State private var isResettingToUncategorized = false
     @State private var showApplySystemSchemeConfirmation = false
+    @State private var showResetToUncategorizedConfirmation = false
     @State private var isDataFilePanelPresented = false
     @State private var hotkeyStatusToast: String? = nil
     @State private var hotkeyStatusToastToken: UUID? = nil
     @State private var selectedTab: SettingsTab = .general
+    @State private var selectedInitialLayoutMode: InitialLayoutMode = .smart
 
     private func scanApps() {
         DispatchQueue.global(qos: .userInitiated).async {
@@ -298,6 +307,18 @@
         }
     }
 
+    private func applySelectedInitialLayout() {
+        switch selectedInitialLayoutMode {
+        case .uncategorized:
+            guard !isResettingToUncategorized else { return }
+            withAnimation(.easeOut(duration: 0.16)) {
+                showResetToUncategorizedConfirmation = true
+            }
+        case .smart:
+            applySystemInitialScheme()
+        }
+    }
+
     private func performApplySystemInitialScheme() {
         guard !isApplyingSystemScheme else { return }
         showApplySystemSchemeConfirmation = false
@@ -325,6 +346,29 @@
                         message: tr("settings.systemSchemeApplyFailed")
                     )
                 }
+            }
+        }
+    }
+
+    private func performResetToUncategorized() {
+        guard !isResettingToUncategorized else { return }
+        showResetToUncategorizedConfirmation = false
+        TagDatabase.flushPendingCategorySchemeBackupBatch()
+        isResettingToUncategorized = true
+
+        DispatchQueue.global(qos: .userInitiated).async {
+            let result = AppLibraryController.resetToUncategorized()
+
+            DispatchQueue.main.async {
+                isResettingToUncategorized = false
+                allApps = result.snapshot.apps
+                tagColors = result.snapshot.tagColors
+                refreshDataState()
+                notifyDataChanged()
+                showDataAlert(
+                    title: tr("settings.resetToUncategorizedApplied"),
+                    message: tr("settings.resetToUncategorizedAppliedMessage")
+                )
             }
         }
     }
@@ -382,6 +426,29 @@
                     .frame(width: 16, height: 16)
                 Text(title)
                     .font(.system(size: 13, weight: .medium))
+            }
+            .foregroundStyle(isSelected ? Color.accentColor : Color.primary)
+            .contentShape(Rectangle())
+        }
+        .buttonStyle(.plain)
+    }
+
+    private func initialLayoutOption(
+        _ title: String,
+        mode: InitialLayoutMode
+    ) -> some View {
+        let isSelected = selectedInitialLayoutMode == mode
+        return Button {
+            selectedInitialLayoutMode = mode
+        } label: {
+            HStack(spacing: 6) {
+                Image(systemName: isSelected ? "largecircle.fill.circle" : "circle")
+                    .font(.system(size: 13, weight: .medium))
+                    .frame(width: 16, height: 16)
+                Text(title)
+                    .font(.system(size: 13, weight: .medium))
+                    .lineLimit(1)
+                    .minimumScaleFactor(0.85)
             }
             .foregroundStyle(isSelected ? Color.accentColor : Color.primary)
             .contentShape(Rectangle())
@@ -528,12 +595,12 @@
     }
 
     private var settingsTabBar: some View {
-        HStack(spacing: 18) {
+        HStack(spacing: 8) {
             ForEach(SettingsTab.allCases) { tab in
                 settingsTabButton(tab)
             }
         }
-        .frame(height: 104, alignment: .center)
+        .frame(height: 96, alignment: .center)
         .frame(maxWidth: .infinity)
         .background(Color(nsColor: .windowBackgroundColor))
     }
@@ -550,11 +617,12 @@
                     .frame(width: 32, height: 32)
                 Text(tr(tab.titleKey))
                     .font(.system(size: 13, weight: .semibold))
-                    .lineLimit(1)
+                    .lineLimit(2)
+                    .multilineTextAlignment(.center)
                     .minimumScaleFactor(0.8)
             }
             .foregroundStyle(isSelected ? Color.accentColor : Color.secondary)
-            .frame(width: 72, height: 70)
+            .frame(width: 108, height: 74)
             .background(
                 RoundedRectangle(cornerRadius: 8, style: .continuous)
                     .fill(isSelected ? Color.accentColor.opacity(0.10) : Color.clear)
@@ -761,7 +829,7 @@
                 VStack(alignment: .center, spacing: 0) {
                     VStack(alignment: .leading, spacing: 12) {
                         HStack(alignment: .center, spacing: 12) {
-                            Text(tr("settings.systemScheme"))
+                            Text(tr("settings.initialLayout"))
                                 .font(.system(size: 13, weight: .semibold))
                                 .foregroundStyle(.secondary)
                                 .lineLimit(1)
@@ -769,19 +837,24 @@
                                 .multilineTextAlignment(.trailing)
                                 .frame(width: dataLabelWidth, alignment: .trailing)
 
-                            Text(SmartStartService.systemInitialSchemeName)
-                                .font(.system(size: 13, weight: .medium))
-                                .foregroundStyle(.primary)
-                                .lineLimit(1)
-                                .truncationMode(.middle)
+                            HStack(spacing: 22) {
+                                initialLayoutOption(
+                                    tr("settings.initialLayoutUncategorized"),
+                                    mode: .uncategorized
+                                )
+                                initialLayoutOption(
+                                    tr("settings.initialLayoutSmart"),
+                                    mode: .smart
+                                )
+                            }
                                 .frame(maxWidth: .infinity, alignment: .leading)
-                                .layoutPriority(0)
+                                .layoutPriority(1)
 
                             dataActionButton(
                                 tr("settings.applyScheme"),
                                 prominent: true,
-                                enabled: !isApplyingSystemScheme,
-                                action: applySystemInitialScheme
+                                enabled: !isApplyingSystemScheme && !isResettingToUncategorized,
+                                action: applySelectedInitialLayout
                             )
                             .frame(width: dataActionWidth, alignment: .trailing)
                             .layoutPriority(2)
@@ -993,7 +1066,7 @@
             }
 
             if showApplySystemSchemeConfirmation {
-                ApplySystemSchemeConfirmationView(
+                SettingsConfirmationView(
                     title: tr("settings.applySystemSchemeWarningTitle"),
                     message: tr("settings.applySystemSchemeWarningMessage"),
                     confirmTitle: tr("settings.confirmApplyScheme"),
@@ -1002,6 +1075,23 @@
                     onCancel: {
                         withAnimation(.easeOut(duration: 0.14)) {
                             showApplySystemSchemeConfirmation = false
+                        }
+                    }
+                )
+                .zIndex(3)
+                .transition(.opacity)
+            }
+
+            if showResetToUncategorizedConfirmation {
+                SettingsConfirmationView(
+                    title: tr("settings.resetToUncategorizedWarningTitle"),
+                    message: tr("settings.resetToUncategorizedWarningMessage"),
+                    confirmTitle: tr("settings.confirmResetToUncategorized"),
+                    cancelTitle: tr("settings.cancel"),
+                    onConfirm: performResetToUncategorized,
+                    onCancel: {
+                        withAnimation(.easeOut(duration: 0.14)) {
+                            showResetToUncategorizedConfirmation = false
                         }
                     }
                 )
@@ -1031,7 +1121,7 @@
                 .transition(.opacity.combined(with: .move(edge: .bottom)))
             }
         }
-        .frame(width: settingsWindowWidth, height: 480)
+        .frame(width: settingsWindowWidth, height: settingsWindowHeight)
         .onAppear {
             syncSelectedLanguage()
             showPendingHotkeyWarningIfNeeded()
@@ -1116,7 +1206,7 @@
     }
 }
 
-private struct ApplySystemSchemeConfirmationView: View {
+private struct SettingsConfirmationView: View {
     let title: String
     let message: String
     let confirmTitle: String

--
Gitblit v1.9.3