| Apptag/AppLibraryController.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/ContentView.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/PreferencesView.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/SmartStartNoticeOverlay.swift | ●●●●● patch | view | raw | blame | history | |
| Scripts/window_logic_qa.sh | ●●●●● patch | view | raw | blame | history | |
| TODO.md | ●●●●● patch | view | raw | blame | history |
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 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)) SmartStartNoticeOverlay( notice: smartStartNotice, onDismiss: dismissSmartStartNotice, onApply: applySmartStartSuggestion, onUndo: undoSmartStart ) 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) } // MARK: - Edit Tags View 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() Apptag/SmartStartNoticeOverlay.swift
New file @@ -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) } } 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 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` 的刷新路径中抽离。