From fb4eaa3834c5c62c5421dc67c3cdb546b1a5a0f9 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 29 May 2026 05:46:37 +0800
Subject: [PATCH] Modularize settings smart start paths

---
 Apptag/SmartStartNoticeOverlay.swift |   99 ++++++++++++++++++++++++
 TODO.md                              |   10 +-
 Scripts/window_logic_qa.sh           |    5 
 Apptag/ContentView.swift             |  102 +------------------------
 Apptag/PreferencesView.swift         |   10 -
 Apptag/AppLibraryController.swift    |   14 +++
 6 files changed, 131 insertions(+), 109 deletions(-)

diff --git a/Apptag/AppLibraryController.swift b/Apptag/AppLibraryController.swift
index 7737960..090a31b 100644
--- a/Apptag/AppLibraryController.swift
+++ b/Apptag/AppLibraryController.swift
@@ -17,6 +17,11 @@
     let summary: SmartStartSummary?
 }
 
+struct AppLibrarySystemSchemeApplyResult {
+    let snapshot: AppLibrarySnapshot
+    let summary: SmartStartSummary?
+}
+
 enum AppLibraryController {
     static func refresh() -> AppLibraryRefreshResult {
         let scannedApps = AppIndexer.scan()
@@ -42,6 +47,15 @@
         )
     }
 
+    static func applySystemInitialScheme() -> AppLibrarySystemSchemeApplyResult {
+        let scannedApps = AppIndexer.scan(useCache: false)
+        let result = SmartStartService.applySystemInitialScheme(apps: scannedApps)
+        return AppLibrarySystemSchemeApplyResult(
+            snapshot: makeSnapshot(scannedApps: scannedApps, store: result.store),
+            summary: result.summary
+        )
+    }
+
     private static func makeSnapshot(
         scannedApps: [AppInfo],
         store: TagDatabase.Store
diff --git a/Apptag/ContentView.swift b/Apptag/ContentView.swift
index 033efcc..e5389cd 100644
--- a/Apptag/ContentView.swift
+++ b/Apptag/ContentView.swift
@@ -340,20 +340,6 @@
     let tagName: String
 }
 
-private enum SmartStartNoticeMode {
-    case autoApplied
-    case suggestionOnly
-    case manuallyApplied
-}
-
-private struct SmartStartNotice: Identifiable {
-    let id = UUID()
-    let mode: SmartStartNoticeMode
-    let title: String
-    let message: String
-    let summary: SmartStartSummary
-}
-
 struct ContentView: View {
     let hideOverlay: () -> Void
     private let initialQuickSearchSource: String?
@@ -1100,88 +1086,12 @@
     }
 
     private var smartStartNoticeOverlay: some View {
-        GeometryReader { proxy in
-            if let notice = smartStartNotice {
-                let panelWidth = min(560, max(320, proxy.size.width - 64))
-
-                ZStack {
-                    Color.black.opacity(0.10)
-                        .ignoresSafeArea()
-
-                    VStack(spacing: 18) {
-                        Image(systemName: notice.mode == .suggestionOnly ? "sparkles" : "checkmark.seal.fill")
-                            .font(.system(size: 30, weight: .semibold))
-                            .foregroundStyle(Color.accentColor)
-                            .frame(width: 58, height: 58)
-                            .background(
-                                Circle()
-                                    .fill(Color.accentColor.opacity(0.12))
-                            )
-
-                        VStack(spacing: 8) {
-                            Text(notice.title)
-                                .font(.system(size: 21, weight: .semibold))
-                                .foregroundStyle(.primary)
-                                .multilineTextAlignment(.center)
-
-                            Text(notice.message)
-                                .font(.system(size: 15, weight: .regular))
-                                .foregroundStyle(.secondary)
-                                .multilineTextAlignment(.center)
-                                .lineLimit(4)
-                                .fixedSize(horizontal: false, vertical: true)
-                        }
-
-                        HStack(spacing: 12) {
-                            switch notice.mode {
-                            case .suggestionOnly:
-                                Button(tr("smartstart.later")) {
-                                    dismissSmartStartNotice()
-                                }
-                                .buttonStyle(.bordered)
-                                .controlSize(.large)
-
-                                Button(tr("smartstart.apply")) {
-                                    applySmartStartSuggestion()
-                                }
-                                .buttonStyle(.borderedProminent)
-                                .controlSize(.large)
-                            case .autoApplied, .manuallyApplied:
-                                if notice.summary.backupPath != nil {
-                                    Button(tr("smartstart.undo")) {
-                                        undoSmartStart()
-                                    }
-                                    .buttonStyle(.bordered)
-                                    .controlSize(.large)
-                                }
-                                Button(tr("smartstart.ok")) {
-                                    dismissSmartStartNotice()
-                                }
-                                .buttonStyle(.borderedProminent)
-                                .controlSize(.large)
-                            }
-                        }
-                    }
-                    .padding(.horizontal, 30)
-                    .padding(.vertical, 26)
-                    .frame(width: panelWidth)
-                    .background(
-                        RoundedRectangle(cornerRadius: 16)
-                            .fill(.ultraThickMaterial)
-                            .shadow(color: .black.opacity(0.26), radius: 28, y: 16)
-                    )
-                    .overlay(
-                        RoundedRectangle(cornerRadius: 16)
-                            .stroke(.white.opacity(0.16), lineWidth: 1)
-                    )
-                }
-                .frame(width: proxy.size.width, height: proxy.size.height)
-                .transition(.scale(scale: 0.96).combined(with: .opacity))
-                .zIndex(650)
-            }
-        }
-        .ignoresSafeArea()
-        .allowsHitTesting(smartStartNotice != nil)
+        SmartStartNoticeOverlay(
+            notice: smartStartNotice,
+            onDismiss: dismissSmartStartNotice,
+            onApply: applySmartStartSuggestion,
+            onUndo: undoSmartStart
+        )
     }
     // MARK: - Edit Tags View
 
diff --git a/Apptag/PreferencesView.swift b/Apptag/PreferencesView.swift
index 37b2276..3549010 100644
--- a/Apptag/PreferencesView.swift
+++ b/Apptag/PreferencesView.swift
@@ -271,16 +271,12 @@
         isApplyingSystemScheme = true
 
         DispatchQueue.global(qos: .userInitiated).async {
-            let scannedApps = AppIndexer.scan(useCache: false)
-            let result = SmartStartService.applySystemInitialScheme(apps: scannedApps)
-            let store = result.store
-            let apps = TagEditor.annotate(apps: scannedApps, store: store)
-            let colors = store.tags.mapValues { $0.color }
+            let result = AppLibraryController.applySystemInitialScheme()
 
             DispatchQueue.main.async {
                 isApplyingSystemScheme = false
-                allApps = apps
-                tagColors = colors
+                allApps = result.snapshot.apps
+                tagColors = result.snapshot.tagColors
                 refreshDataState()
                 notifyDataChanged()
 
diff --git a/Apptag/SmartStartNoticeOverlay.swift b/Apptag/SmartStartNoticeOverlay.swift
new file mode 100644
index 0000000..c034271
--- /dev/null
+++ b/Apptag/SmartStartNoticeOverlay.swift
@@ -0,0 +1,99 @@
+import SwiftUI
+
+enum SmartStartNoticeMode {
+    case autoApplied
+    case suggestionOnly
+    case manuallyApplied
+}
+
+struct SmartStartNotice: Identifiable {
+    let id = UUID()
+    let mode: SmartStartNoticeMode
+    let title: String
+    let message: String
+    let summary: SmartStartSummary
+}
+
+struct SmartStartNoticeOverlay: View {
+    let notice: SmartStartNotice?
+    let onDismiss: () -> Void
+    let onApply: () -> Void
+    let onUndo: () -> Void
+
+    var body: some View {
+        GeometryReader { proxy in
+            if let notice {
+                let panelWidth = min(560, max(320, proxy.size.width - 64))
+
+                ZStack {
+                    Color.black.opacity(0.10)
+                        .ignoresSafeArea()
+
+                    VStack(spacing: 18) {
+                        Image(systemName: notice.mode == .suggestionOnly ? "sparkles" : "checkmark.seal.fill")
+                            .font(.system(size: 30, weight: .semibold))
+                            .foregroundStyle(Color.accentColor)
+                            .frame(width: 58, height: 58)
+                            .background(
+                                Circle()
+                                    .fill(Color.accentColor.opacity(0.12))
+                            )
+
+                        VStack(spacing: 8) {
+                            Text(notice.title)
+                                .font(.system(size: 21, weight: .semibold))
+                                .foregroundStyle(.primary)
+                                .multilineTextAlignment(.center)
+
+                            Text(notice.message)
+                                .font(.system(size: 15, weight: .regular))
+                                .foregroundStyle(.secondary)
+                                .multilineTextAlignment(.center)
+                                .lineLimit(4)
+                                .fixedSize(horizontal: false, vertical: true)
+                        }
+
+                        HStack(spacing: 12) {
+                            switch notice.mode {
+                            case .suggestionOnly:
+                                Button(tr("smartstart.later"), action: onDismiss)
+                                    .buttonStyle(.bordered)
+                                    .controlSize(.large)
+
+                                Button(tr("smartstart.apply"), action: onApply)
+                                    .buttonStyle(.borderedProminent)
+                                    .controlSize(.large)
+                            case .autoApplied, .manuallyApplied:
+                                if notice.summary.backupPath != nil {
+                                    Button(tr("smartstart.undo"), action: onUndo)
+                                        .buttonStyle(.bordered)
+                                        .controlSize(.large)
+                                }
+                                Button(tr("smartstart.ok"), action: onDismiss)
+                                    .buttonStyle(.borderedProminent)
+                                    .controlSize(.large)
+                            }
+                        }
+                    }
+                    .padding(.horizontal, 30)
+                    .padding(.vertical, 26)
+                    .frame(width: panelWidth)
+                    .background(
+                        RoundedRectangle(cornerRadius: 16)
+                            .fill(.ultraThickMaterial)
+                            .shadow(color: .black.opacity(0.26), radius: 28, y: 16)
+                    )
+                    .overlay(
+                        RoundedRectangle(cornerRadius: 16)
+                            .stroke(.white.opacity(0.16), lineWidth: 1)
+                    )
+                }
+                .frame(width: proxy.size.width, height: proxy.size.height)
+                .transition(.scale(scale: 0.96).combined(with: .opacity))
+                .zIndex(650)
+            }
+        }
+        .ignoresSafeArea()
+        .allowsHitTesting(notice != nil)
+    }
+}
diff --git a/Scripts/window_logic_qa.sh b/Scripts/window_logic_qa.sh
index 05fd697..f7dc843 100755
--- a/Scripts/window_logic_qa.sh
+++ b/Scripts/window_logic_qa.sh
@@ -1143,8 +1143,9 @@
 SWIFT
   done < <(swift "$screens_swift")
 else
-  rg -Fq 'screenContainingCurrentPointer() ??' "$ROOT_DIR/Apptag/ApptagApp.swift"
-  rg -Fq 'NSMouseInRect(mousePoint, $0.frame, false)' "$ROOT_DIR/Apptag/ApptagApp.swift"
+  rg -Fq 'statusMenuScreenForNextOverlay = overlayController.screenContainingCurrentPointer()' "$ROOT_DIR/Apptag/ApptagApp.swift"
+  rg -Fq 'screenContainingCurrentPointer() ??' "$ROOT_DIR/Apptag/OverlayWindowController.swift"
+  rg -Fq 'NSMouseInRect(mousePoint, $0.frame, false)' "$ROOT_DIR/Apptag/OverlayWindowController.swift"
   log "PASS screen-following static path: single physical display here; code selects NSScreen under current pointer"
 fi
 
diff --git a/TODO.md b/TODO.md
index 757d5d5..59f85d5 100644
--- a/TODO.md
+++ b/TODO.md
@@ -13,12 +13,14 @@
 
 ## Todo
 
-- [2026-05-28] 设置页和 Smart Start 模块化。
-  - 目标: 低频路径后置整理。
-  - 验收: 语言、通用、快捷键、标签、数据、关于页都保持现有行为。
-
 ## Done
 
+- [2026-05-29] 设置页和 Smart Start 模块化。
+  - 提交: 本次提交
+  - 结果: `SmartStartNoticeOverlay` 已从 `ContentView` 拆出;设置页“应用系统智能化初始分类”的扫描、Smart Start 应用和 snapshot 组装改由 `AppLibraryController` 统一处理;窗口 QA 脚本同步适配 `OverlayWindowController` 的屏幕选择静态检查。
+  - 范围: 不改设置页 UI 布局、不改 Smart Start 分类规则、不改导入/导出/恢复行为。
+  - 验证: `bash build.sh`、`codesign --verify --deep --strict`、设置页语言/通用/快捷键/标签/数据/关于六个 tab 屏幕复核、`Scripts/window_logic_qa.sh` 全部通过。
+
 - [2026-05-29] AppLibraryController / 数据刷新管线整理。
   - 提交: 本次提交
   - 结果: 新增 `AppLibraryController` 和 `AppLibrarySnapshot`,把扫描、reconcile、Smart Start、annotate、Quick Search documents、标签颜色和标签顺序组装从 `ContentView` 的刷新路径中抽离。

--
Gitblit v1.9.3