From 57abfdc2ba880be1fae4a1a184531e3228d62dae Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 19 May 2026 01:20:21 +0800
Subject: [PATCH] Prepare App Store release 7.3.5 build 744

---
 Apptag/TagGroupView.swift                                 |    2 
 /dev/null                                                 |   55 ------
 Apptag/ApptagApp.swift                                    |   25 ++
 AppStore_Submission.md                                    |   39 ++--
 Apptag/AppGridItem.swift                                  |   13 -
 Apptag/ContentView.swift                                  |  125 +++++++++----
 AppStore_TODO_7.3.5_744.md                                |  100 +++++++++++
 Apptag/PreferencesView.swift                              |  140 ++++++++++++++-
 Apptag/SmartCategorization/SmartStartService.swift        |   24 +
 Apptag/SmartCategorization/SmartCategorizationDraft.swift |    5 
 CHANGELOG.md                                              |    5 
 11 files changed, 393 insertions(+), 140 deletions(-)

diff --git a/AppStore_Submission.md b/AppStore_Submission.md
index 5d9a815..6676582 100644
--- a/AppStore_Submission.md
+++ b/AppStore_Submission.md
@@ -1,6 +1,6 @@
 # Apptag — Mac App Store 提审资料
 
-> 版本: 6.0.5 | Bundle ID: com.apptag.launcher | 更新日期: 2026-05-17
+> 版本: 7.3.5 | Build: 744 | Bundle ID: com.apptag.launcher | 更新日期: 2026-05-18
 
 ---
 
@@ -11,8 +11,8 @@
 | **App 名称** | Apptag |
 | **副标题** | Tag-Based App Launcher |
 | **Bundle ID** | com.apptag.launcher |
-| **版本号** | 6.0.5 |
-| **Build 号** | 610 |
+| **版本号** | 7.3.5 |
+| **Build 号** | 744 |
 | **SKU** | apptag-mac-001 |
 | **主要类别** | Utilities (工具) |
 | **次要类别** | Productivity (效率) |
@@ -175,7 +175,9 @@
 
 ## 7. App 图标
 
-已有自定义图标文件: `~/Projects/Apptag/icon-icns.icns`
+已有自定义图标文件:
+- `~/Projects/Apptag/icon-icns.icns`
+- `~/Projects/Apptag/Apptag/Assets.xcassets/AppIcon.appiconset/icon_1024_preview.png`
 
 App Store 额外要求:
 - **1024×1024 PNG** (用于 App Store 列表展示)
@@ -187,23 +189,21 @@
 ## 8. What's New (本次版本更新说明)
 
 ```
-Apptag 6.0.5
+Apptag 7.3.5
 
-- Added an Uncommon app marker for apps you are still learning, with optional hover notes
-- New apps can be marked as Uncommon automatically and are cleared after enough launches from TagLauncher
-- Added local note editing for Uncommon apps; notes stay in the exported JSON even if the marker is removed later
-- Added an Uncommon bubble toggle in the Data preferences tab, enabled by default
-- Reworked batch category editing into clear Add Tags and Remove Tags modes
-- Fixed batch editing so selected apps no longer inherit unrelated tags from each other
-- Added clear confirmation feedback showing the real number of tag links added or removed
-- Improved Remove Tags mode with red delete indicators and safer label truncation
-- Fixed edit-sidebar layout so long localized hints no longer stretch the page
-- Removed the edit-page center title and gave more room to the action hint and confirmation button
-- Improved shortcut hints in Language, Data, and About settings pages using symbol-only key graphics
-- Fixed a crash caused by toolbar hint layout constraints in the edit screen
+- Added Smart Start intelligent initial categorization with a built-in macOS app catalog, localized system tags, and default app notes
+- Added category scheme backup, restore, export, import, and automatic snapshots for safer tag management
+- Improved overlay performance with app scan caching, reduced full-view rebuilds, and smoother hover behavior
+- Improved drag-to-uncategorized handling with an in-overlay confirmation panel and better visual cleanup during drag operations
 - Fixed Safari and other modern macOS system apps being missed when they live behind Cryptex system paths
-- Kept duplicate app icons out by resolving symlinks for scanning deduplication
-- Version updated to 6.0.5, Build updated to 610
+- Improved settings and edit controls, including more stable settings access from the overlay and better multilingual layout behavior
+- Improved menu bar item stability with a persistent status item identity so macOS and menu bar managers can recognize it consistently
+- Fixed localized default notes for Apple apps during first launch in non-Chinese languages
+- Fixed Uncategorized drag-and-drop confirmation so cancel/confirm no longer leaves icon drag state stuck
+- Improved tag-list reordering feedback by replacing persistent wiggle animation with clearer opacity-based drag state
+- Fixed the Smart Start apply confirmation in Settings so the warning stays above the Settings window with clearer readable controls
+- Fixed right-side tag navigation getting stuck in a dimmed drag visual state after a tag click
+- Version updated to 7.3.5, Build updated to 744
 ```
 
 ---
@@ -263,6 +263,7 @@
 - [ ] 生成 1024×1024 App Store 图标 PNG
 - [ ] 截取 6 张 Mac 截图 (3456×2234 PNG)
 - [ ] 准备隐私政策 URL(GitHub Pages / 独立页面 / Notion)
+- [ ] 确认 `Apptag/Info.plist` 与构建产物均为 `7.3.5` / Build `744`
 - [ ] 确认代码签名证书 (Mac App Distribution)
 - [ ] 通过 TestFlight 测试 sandbox 下快捷键功能
 - [ ] 使用 `APP_STORE=1 CODESIGN_IDENTITY="..." bash build.sh` 生成正式签名包
diff --git a/AppStore_TODO_5.7.8_583.md b/AppStore_TODO_5.7.8_583.md
deleted file mode 100644
index 1177bf2..0000000
--- a/AppStore_TODO_5.7.8_583.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# TagLauncher 5.7.8 (Build 583) App Store 提交 To-do List
-
-
-
-> 这个本机 .app/.dmg 是本地构建产物。去 App Store 提交时,最好在那台登录开发者账号的电脑上,用 source/ 里的源码重新用正式证书构建、签名、上传。
-
-## A. 换电脑前确认
-
-- 确认要提交的版本号是 `5.7.8`,Build 是 `583`。
-- 带走本文件夹里的 `AppStore_Submission.md`、`CHANGELOG.md`、`TagLauncher.dmg`、`TagLauncher.app`。
-- 如果要在另一台电脑重新签名构建,也带走源码快照文件夹或整个项目目录。
-- 测试时先退出旧版 TagLauncher,再启动新版,避免菜单栏状态项仍来自旧进程。
-- 如果菜单栏图标看不到,先检查 Thaw / Bartender / Ice / Hidden Bar / Dozer 等菜单栏管理软件是否隐藏了 TagLauncher。
-
-## B. 开发者电脑准备
-
-- 登录正确的 Apple Developer 账号。
-- 确认 Xcode 已安装,并安装 Command Line Tools。
-- 在 Xcode 账号里确认有 Mac App Distribution / Apple Distribution 证书。
-- 确认 App Store Connect 里 Bundle ID 是 `com.apptag.launcher`。
-- 确认 App Store Connect 已创建或选择 Apptag 的 `5.7.8` 版本。
-
-## C. 正式构建与签名
-
-- 在开发者电脑上用开发者证书重新构建,不要直接提交本机 ad-hoc 签名包。
-- 使用 App Store sandbox entitlements:`Apptag/TagLauncher.entitlements`。
-- 构建完成后确认 About 页显示 `版本 5.7.8(构建 583)`。
-- 确认 `LSMinimumSystemVersion` 是 `15.0`。
-- 本地打开新构建的 App,快速检查:
-  - `⌥⇧空格` 能呼出应用列表。
-  - 设置窗口能在当前 TagLauncher 浮层上方居中显示。
-  - 设置页语言切换正常。
-  - 通用页排版在英文、法语、意大利语、中文下都不拥挤。
-  - 数据页导入/导出分类与布局正常。
-
-## D. App Store Connect 填写
-
-- 复制 `AppStore_Submission.md` 里的 App 描述、关键词、隐私说明和审核备注。
-- 隐私标签选择“不收集任何数据”。
-- 最低系统版本填写 macOS `15.0`。
-- What's New 使用 `AppStore_Submission.md` 第 8 节。
-- 如果上传截图,优先展示:
-  - 主应用列表视图。
-  - 设置页语言切换。
-  - 通用页布局设置。
-  - 标签编辑/拖动排序。
-  - 导入/导出分类与布局。
-
-## E. 上传与提交审核
-
-- 用 Xcode Organizer / Transporter 上传正式签名构建。
-- 上传后在 App Store Connect 选择 Build `583`。
-- 填完审核信息后提交审核。
-- 提交后保留本提交包,方便审核反馈时对照版本。
-
diff --git a/AppStore_TODO_7.3.5_744.md b/AppStore_TODO_7.3.5_744.md
new file mode 100644
index 0000000..b8ecf55
--- /dev/null
+++ b/AppStore_TODO_7.3.5_744.md
@@ -0,0 +1,100 @@
+# Apptag 7.3.5 (Build 744) App Store 提交 To-do List
+
+> 当前发布版本以源码目录中的 `Apptag/Info.plist` 为准;在发布包里对应 `Source/Apptag/Info.plist`:`CFBundleShortVersionString = 7.3.5`,`CFBundleVersion = 744`,`LSMinimumSystemVersion = 15.0`。
+> 本地已有构建产物 `build/TagLauncher.app` 与 `build/TagLauncher.dmg`,但正式提交仍建议在登录 Apple Developer 账号的开发者电脑上重新签名、归档、上传。
+
+## 0. 当前状态
+
+- 本地发布包已整理到 `/Users/ar/Projects/Apptag744Release`。
+- 本地 QA 已通过,详见 `QA/QA_Report_7.3.5_744.md`。
+- 发布资料版本漂移已清理:当前提审资料、清单、发布包均指向 `7.3.5` / Build `744`。
+- 本地构建产物为 ad-hoc 签名,仅用于 QA;正式提审仍需使用 Apple Distribution 证书重新签名并上传。
+- 下一步需要 Apple Developer 账号环境执行:正式签名、上传、TestFlight/App Store 签名包验证、提交审核。
+
+## A. 发布资料确认
+
+- 确认 App Store Connect 中本次版本为 `7.3.5`,Build 选择 `744`。
+- 使用 `AppStore_Submission.md` 填写描述、关键词、隐私说明、审核备注和 What's New。
+- 使用 `CHANGELOG.md` 顶部 `7.3.5` 记录作为版本变更来源。
+- 发布包目录为 `/Users/ar/Projects/Apptag744Release`,其中 `00todolist.md` 是正式操作清单。
+- 确认发布包中包含:
+  - `AppStore_Submission.md`
+  - `CHANGELOG.md`
+  - `00todolist.md`
+  - `Artifacts/TagLauncher.app`
+  - `Artifacts/TagLauncher.dmg`
+  - `Artifacts/AppStore_1024.png`
+  - `Artifacts/AppIcon.icns`
+  - `Source/` 源码与构建依赖
+  - `QA/QA_Report_7.3.5_744.md`
+
+## B. 正式构建与签名
+
+- 登录正确的 Apple Developer 账号。
+- 确认 Xcode、Command Line Tools、Transporter 或 Xcode Organizer 可用。
+- 确认证书包含 Mac App Distribution / Apple Distribution。
+- 确认 Bundle ID 是 `com.apptag.launcher`。
+- 使用 App Store sandbox entitlements:`Apptag/TagLauncher.entitlements`。
+- 在开发者电脑上进入发布包源码目录后运行:
+
+```bash
+cd /Users/ar/Projects/Apptag744Release/Source
+APP_STORE=1 CODESIGN_IDENTITY="Apple Distribution: Your Name (TEAMID)" bash build.sh
+```
+
+- 构建完成后确认 About 页显示 `版本 7.3.5(构建 744)`。
+- 确认 `build/TagLauncher.app/Contents/Info.plist` 中:
+  - `CFBundleShortVersionString = 7.3.5`
+  - `CFBundleVersion = 744`
+  - `LSMinimumSystemVersion = 15.0`
+
+## C. 本地冒烟测试
+
+- 退出旧版 TagLauncher,再启动新版。
+- 如果菜单栏图标不可见,检查 Thaw / Bartender / Ice / Hidden Bar / Dozer 等菜单栏管理工具是否隐藏了 TagLauncher。
+- 确认 `⌥⇧空格` 能呼出应用列表。
+- 确认主列表 Flat / Colored Grid / Container 模式均能显示。
+- 确认设置按钮和编辑按钮都能从浮层右上角打开。
+- 确认设置页语言切换正常。
+- 确认标签拖拽排序、右侧标签点击切换、App 拖入标签、拖到未分类确认面板正常。
+- 确认数据页导出 / 导入分类与布局正常。
+- 确认 Smart Start 初始分类方案可应用、确认提示不被设置窗口遮挡、恢复和导出。
+- 确认 App Store 沙盒构建中 Launch at Login 控件隐藏。
+- 在 TestFlight 中重点验证 sandbox 下全局快捷键 `Shift+Option+Space`。
+- 已完成本地关键 QA:全新用户、已有用户、Smart Start、语言切换、备注保护、拖到未分类、标签拖拽、右侧标签点击切换、设置页确认层级、菜单栏稳定性。
+
+## D. App Store Connect 填写
+
+- 创建或选择 Apptag `7.3.5` 版本。
+- App 名称:`Apptag`。
+- Bundle ID:`com.apptag.launcher`。
+- SKU:`apptag-mac-001`。
+- 类别:Utilities,次要类别 Productivity。
+- 最低系统版本:macOS `15.0`。
+- 复制 `AppStore_Submission.md` 第 2 节 App 描述。
+- 复制第 3 节关键词。
+- 隐私标签选择“不收集任何数据”。
+- 年龄分级按第 5 节填写,最终应为 `4+`。
+- What's New 使用第 8 节 `Apptag 7.3.5` 内容。
+- 审核备注使用第 9 节,说明 Carbon 全局快捷键不需要辅助功能权限,且无网络请求。
+
+## E. 截图与图标
+
+- 上传 1024×1024 App Store 图标 PNG,优先使用 `Apptag/Assets.xcassets/AppIcon.appiconset/icon_1024_preview.png`。
+- 准备至少 1 张、建议 6 张 Mac 截图。
+- 推荐截图场景:
+  - 全屏浮层 Flat 或 Colored Grid 模式
+  - Container 模式
+  - 标签编辑与拖拽排序
+  - 设置页通用选项
+  - 数据页分类方案管理
+  - 菜单栏入口与应用浮层工作流
+
+## F. 上传与提交审核
+
+- 使用 Xcode Organizer 或 Transporter 上传正式签名构建。
+- 上传后在 App Store Connect 选择 Build `744`。
+- 等待处理完成后,确认版本页显示 `7.3.5 (744)`。
+- 检查描述、隐私、截图、价格、年龄分级、审核备注均已填写。
+- 提交审核。
+- 提交后保留 `/Users/ar/Projects/Apptag744Release`,方便审核反馈时快速对照。
diff --git a/Apptag/AppGridItem.swift b/Apptag/AppGridItem.swift
index 22c204c..5a9a61b 100644
--- a/Apptag/AppGridItem.swift
+++ b/Apptag/AppGridItem.swift
@@ -14,10 +14,10 @@
     var onEditNote: ((AppInfo, CGRect) -> Void)? = nil
     var bubbleDisabled: Bool = false
     var itemID: String
+    var dragResetToken: Int = 0
     @Binding var hoveredAppItemID: String?
     let onSelect: () -> Void
 
-    @State private var wiggle = false
     @State private var interactionFrame: CGRect = .zero
     @AppStorage("showUncommonAppBubbles") private var showUncommonAppBubbles = AppDefaults.showUncommonAppBubbles
 
@@ -96,15 +96,10 @@
                 onEditNote?(app, interactionFrame)
             }
         }
-        .rotationEffect(.degrees(dragModeActive ? (wiggle ? 2.0 : -2.0) : 0))
-        .animation(
-            dragModeActive
-                ? .easeInOut(duration: 0.12).repeatForever(autoreverses: true)
-                : .default,
-            value: wiggle
-        )
+        .opacity(dragModeActive ? 0.92 : 1)
+        .animation(.easeOut(duration: 0.08), value: dragModeActive)
+        .id(dragResetToken)
         .onChange(of: dragModeActive) { _, active in
-            wiggle = active
             if active {
                 setHoverState(false)
                 onBubbleHover?(app, interactionFrame, false)
diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index 3737c9b..72a1665 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -586,6 +586,7 @@
         }
         overlayWindow?.orderOut(nil)
         removeOverlayKeyMonitor()
+        NotificationCenter.default.post(name: .tagLauncherOverlayDidHide, object: nil)
         if force {
             overlayWindow = nil
         }
@@ -836,15 +837,25 @@
 
 final class DismissibleHostingView<Content: View>: NSHostingView<Content> {
     private let onBackdropTap: () -> Void
+    private var suppressBackdropDismiss = false
+    private var modalInteractionObserver: NSObjectProtocol?
 
     @MainActor required init(rootView: Content) {
         self.onBackdropTap = {}
         super.init(rootView: rootView)
+        observeModalInteractionChanges()
     }
 
     init(rootView: Content, onBackdropTap: @escaping () -> Void) {
         self.onBackdropTap = onBackdropTap
         super.init(rootView: rootView)
+        observeModalInteractionChanges()
+    }
+
+    deinit {
+        if let modalInteractionObserver {
+            NotificationCenter.default.removeObserver(modalInteractionObserver)
+        }
     }
 
     @available(*, unavailable)
@@ -857,6 +868,10 @@
             return
         }
         if hit == self {
+            if suppressBackdropDismiss {
+                super.mouseDown(with: event)
+                return
+            }
             onBackdropTap()
             return
         }
@@ -879,6 +894,16 @@
         super.mouseDown(with: event)
     }
 
+    private func observeModalInteractionChanges() {
+        modalInteractionObserver = NotificationCenter.default.addObserver(
+            forName: .tagLauncherModalInteractionChanged,
+            object: nil,
+            queue: .main
+        ) { [weak self] notification in
+            self?.suppressBackdropDismiss = (notification.userInfo?["active"] as? Bool) ?? false
+        }
+    }
+
     private func findTextFieldContainer(in view: NSView) -> TextFieldContainer? {
         if let container = view as? TextFieldContainer { return container }
         for sub in view.subviews {
diff --git a/Apptag/ContentView.swift b/Apptag/ContentView.swift
index 16f587e..0b877a7 100644
--- a/Apptag/ContentView.swift
+++ b/Apptag/ContentView.swift
@@ -10,6 +10,8 @@
     static let tagLauncherDataDidChange = Notification.Name("TagLauncherDataDidChange")
     static let tagLauncherOpenPreferencesRequested = Notification.Name("TagLauncherOpenPreferencesRequested")
     static let tagLauncherOverlayDidShow = Notification.Name("TagLauncherOverlayDidShow")
+    static let tagLauncherOverlayDidHide = Notification.Name("TagLauncherOverlayDidHide")
+    static let tagLauncherModalInteractionChanged = Notification.Name("TagLauncherModalInteractionChanged")
 }
 
 // MARK: - Edit Phase
@@ -199,8 +201,8 @@
     let colorIndex: Int
     var dragModeActive: Bool = false
     var isDragging: Bool = false
+    var dragResetToken: Int = 0
     let action: () -> Void
-    @State private var wiggle = false
 
     private var bgColor: Color {
         Color(nsColor: TagColor.nsColor(for: colorIndex))
@@ -222,20 +224,14 @@
             .background(RoundedRectangle(cornerRadius: 7).fill(bgColor))
             .shadow(color: .black.opacity(isDragging ? 0.34 : 0.2), radius: isDragging ? 8 : 3, y: isDragging ? 4 : 1)
             .scaleEffect(isDragging ? 1.05 : 1.0)
-            .rotationEffect(.degrees(dragModeActive ? (wiggle ? 1.8 : -1.8) : 0))
-            .animation(
-                dragModeActive
-                    ? .easeInOut(duration: 0.12).repeatForever(autoreverses: true)
-                    : .default,
-                value: wiggle
-            )
-            .onChange(of: dragModeActive) { _, active in
-                wiggle = active
-            }
+            .opacity(dragModeActive ? (isDragging ? 1.0 : 0.62) : 1.0)
+            .animation(.easeOut(duration: 0.08), value: isDragging)
+            .animation(.easeOut(duration: 0.08), value: dragModeActive)
+            .id(dragResetToken)
             .contentShape(RoundedRectangle(cornerRadius: 7))
             .onTapGesture {
-                if !dragModeActive { action() }
-        }
+                action()
+            }
     }
 }
 
@@ -246,8 +242,8 @@
     let colorIndex: Int
     var dragModeActive: Bool = false
     var isDragging: Bool = false
+    var dragResetToken: Int = 0
     let action: () -> Void
-    @State private var wiggle = false
 
     private var bgColor: Color {
         Color(nsColor: TagColor.nsColor(for: colorIndex))
@@ -265,20 +261,14 @@
             .background(RoundedRectangle(cornerRadius: 6).fill(bgColor))
             .shadow(color: .black.opacity(isDragging ? 0.34 : 0.2), radius: isDragging ? 8 : 3, y: isDragging ? 4 : 1)
             .scaleEffect(isDragging ? 1.03 : 1.0)
-            .rotationEffect(.degrees(dragModeActive ? (wiggle ? 1.6 : -1.6) : 0))
-            .animation(
-                dragModeActive
-                    ? .easeInOut(duration: 0.12).repeatForever(autoreverses: true)
-                    : .default,
-                value: wiggle
-            )
-            .onChange(of: dragModeActive) { _, active in
-                wiggle = active
-            }
+            .opacity(dragModeActive ? (isDragging ? 1.0 : 0.62) : 1.0)
+            .animation(.easeOut(duration: 0.08), value: isDragging)
+            .animation(.easeOut(duration: 0.08), value: dragModeActive)
+            .id(dragResetToken)
             .contentShape(RoundedRectangle(cornerRadius: 6))
             .onTapGesture {
-                if !dragModeActive { action() }
-        }
+                action()
+            }
     }
 }
 
@@ -364,7 +354,9 @@
     @State private var tagReorderFrames: [String: CGRect] = [:]
     @State private var tagNavDragModeActive = false
     @State private var tagNavDragItem: String? = nil
+    @State private var tagNavDragResetToken = 0
     @State private var tagNavReorderFrames: [String: CGRect] = [:]
+    @State private var tagNavReorderDidMove = false
     @State private var hoveredContainer: String? = nil  // colored container lift
     // Fixed interaction for "Colorless Container": hover fills persistently; click clears.
     @State private var filledColorlessContainer: String? = nil
@@ -381,6 +373,7 @@
     @State private var hoveredBubble: AppBubbleContext? = nil
     @State private var editingBubble: AppBubbleContext? = nil
     @State private var pendingUncategorizedDrop: PendingUncategorizedDrop? = nil
+    @State private var appDragResetToken = 0
     @State private var bubbleDraftNote = ""
     @FocusState private var bubbleNoteFocused: Bool
 
@@ -488,8 +481,12 @@
             refreshApps()
         }
         .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidShow)) { _ in
+            resetTransientDragState()
             refreshNotchHeight()
             refreshApps()
+        }
+        .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidHide)) { _ in
+            resetTransientDragState()
         }
         .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
             guard allApps.isEmpty, !refreshInProgress else { return }
@@ -520,6 +517,20 @@
             if editingBubble != nil && !focused {
                 commitBubbleNote()
             }
+        }
+        .onChange(of: pendingUncategorizedDrop != nil) { _, active in
+            NotificationCenter.default.post(
+                name: .tagLauncherModalInteractionChanged,
+                object: nil,
+                userInfo: ["active": active]
+            )
+        }
+        .onDisappear {
+            NotificationCenter.default.post(
+                name: .tagLauncherModalInteractionChanged,
+                object: nil,
+                userInfo: ["active": false]
+            )
         }
     }
 
@@ -613,11 +624,9 @@
                     TagPill(name: tag.name, colorIndex: tag.colorIndex,
                             dragModeActive: tagNavDragModeActive && canReorderTag(tag.name),
                             isDragging: tagNavDragItem == tag.name,
+                            dragResetToken: tagNavDragResetToken,
                             action: {
-                        if isColorlessContainerMode {
-                            toggleColorlessFill(tag.id)
-                        }
-                        scrollTo(tag.id)
+                        activateTagNavigation(tag.id)
                     })
                     .background(tagNavFrameReader(for: tag.name))
                     .zIndex(tagNavDragItem == tag.name ? 1 : 0)
@@ -657,11 +666,9 @@
                     SideTagPill(name: tag.name, colorIndex: tag.colorIndex,
                                 dragModeActive: tagNavDragModeActive && canReorderTag(tag.name),
                                 isDragging: tagNavDragItem == tag.name,
+                                dragResetToken: tagNavDragResetToken,
                                 action: {
-                        if isColorlessContainerMode {
-                            toggleColorlessFill(tag.id)
-                        }
-                        scrollTo(tag.id)
+                        activateTagNavigation(tag.id)
                     })
                     .background(tagNavFrameReader(for: tag.name))
                     .zIndex(tagNavDragItem == tag.name ? 1 : 0)
@@ -840,6 +847,7 @@
                             onBubbleHover: handleBubbleHover,
                             onEditNote: beginEditingBubbleNote,
                             bubbleDisabled: appBubbleDisabled,
+                            dragResetToken: appDragResetToken,
                             hoveredAppItemID: $hoveredAppItemID,
                             onDropApp: { path, source, copy in
                                 dropApp(path: path, sourceTag: source, targetTag: group.name, copy: copy)
@@ -945,6 +953,7 @@
                         onEditNote: beginEditingBubbleNote,
                         bubbleDisabled: appBubbleDisabled,
                         itemID: "\(group.name)|\(app.path.path)",
+                        dragResetToken: appDragResetToken,
                         hoveredAppItemID: $hoveredAppItemID,
                         onSelect: { openApp(app) }
                     )
@@ -1184,6 +1193,7 @@
                                 onEditNote: beginEditingBubbleNote,
                                 bubbleDisabled: appBubbleDisabled,
                                 itemID: "\(group.name)|\(app.path.path)",
+                                dragResetToken: appDragResetToken,
                                 hoveredAppItemID: $hoveredAppItemID,
                                 onSelect: { openApp(app) }
                             )
@@ -1985,6 +1995,14 @@
         withAnimation(.easeInOut(duration: 0.25)) { scrollProxy?.scrollTo(id, anchor: .top) }
     }
 
+    private func activateTagNavigation(_ id: String) {
+        cancelTagNavReorderVisualState()
+        if isColorlessContainerMode {
+            toggleColorlessFill(id)
+        }
+        scrollTo(id)
+    }
+
     private func fillColorlessContainer(_ id: String) {
         guard isColorlessContainerMode else { return }
         guard filledColorlessContainer != id else { return }
@@ -2012,8 +2030,8 @@
     }
 
     private func tagNavReorderGesture(for tagName: String) -> some Gesture {
-        LongPressGesture(minimumDuration: 0.5)
-            .sequenced(before: DragGesture(minimumDistance: 3, coordinateSpace: .named("tagNavReorder")))
+        LongPressGesture(minimumDuration: 0.35)
+            .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .named("tagNavReorder")))
             .onChanged { value in
                 guard canReorderTag(tagName) else { return }
                 switch value {
@@ -2039,16 +2057,27 @@
         guard canReorderTag(tagName) else { return }
         if tagNavDragItem == nil {
             tagNavDragItem = tagName
+            tagNavReorderDidMove = false
         }
         tagNavDragModeActive = true
     }
 
     private func endTagNavReorder() {
-        if tagNavDragModeActive {
+        if tagNavDragModeActive && tagNavReorderDidMove {
             TagEditor.reorderTags(draggedTagNames)
         }
         tagNavDragModeActive = false
         tagNavDragItem = nil
+        tagNavReorderDidMove = false
+        tagNavDragResetToken &+= 1
+    }
+
+    private func cancelTagNavReorderVisualState() {
+        guard tagNavDragModeActive || tagNavDragItem != nil else { return }
+        tagNavDragModeActive = false
+        tagNavDragItem = nil
+        tagNavReorderDidMove = false
+        tagNavDragResetToken &+= 1
     }
 
     private func reorderTagNavItem(at location: CGPoint) {
@@ -2059,6 +2088,7 @@
               let toIndex = draggedTagNames.firstIndex(of: targetName)
         else { return }
 
+        tagNavReorderDidMove = true
         withAnimation(.spring(response: 0.22, dampingFraction: 0.82)) {
             let destination = toIndex > fromIndex ? toIndex + 1 : toIndex
             draggedTagNames.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: destination)
@@ -2093,7 +2123,7 @@
     }
 
     private func dropApp(path: String, sourceTag: String, targetTag: String, copy: Bool) {
-        appDragModeActive = false
+        resetTransientDragState(keepingPendingUncategorizedDrop: true)
 
         if isUncategorizedDropTarget(targetTag) {
             confirmAndMoveAppToUncategorized(path: path)
@@ -2152,6 +2182,7 @@
     }
 
     private func dismissUncategorizedDropConfirm() {
+        resetTransientDragState(keepingPendingUncategorizedDrop: true)
         withAnimation(.easeOut(duration: 0.18)) {
             pendingUncategorizedDrop = nil
         }
@@ -2161,6 +2192,7 @@
         guard let pendingDrop = pendingUncategorizedDrop else { return }
         let path = pendingDrop.app.path.path
         let tags = pendingDrop.app.tags
+        resetTransientDragState(keepingPendingUncategorizedDrop: true)
         withAnimation(.easeOut(duration: 0.16)) {
             pendingUncategorizedDrop = nil
         }
@@ -2230,6 +2262,23 @@
         }
     }
 
+    private func resetTransientDragState(keepingPendingUncategorizedDrop: Bool = false) {
+        AppDragCoordinator.shared.cancelDrag()
+        appDragModeActive = false
+        appDragResetToken &+= 1
+        tagNavDragModeActive = false
+        tagNavDragItem = nil
+        tagNavReorderDidMove = false
+        tagNavDragResetToken &+= 1
+        dragItem = nil
+        hoveredAppItemID = nil
+        hoveredContainer = nil
+        clearAppBubbleState()
+        if !keepingPendingUncategorizedDrop {
+            pendingUncategorizedDrop = nil
+        }
+    }
+
     func refreshApps(forceLayoutRefresh: Bool = false) {
         guard !refreshInProgress else {
             if forceLayoutRefresh {
diff --git a/Apptag/PreferencesView.swift b/Apptag/PreferencesView.swift
index 88c00a4..d0a1843 100644
--- a/Apptag/PreferencesView.swift
+++ b/Apptag/PreferencesView.swift
@@ -30,6 +30,7 @@
     @State private var tagColors: [String: Int] = [:]
     @State private var categoryScheme = TagDatabase.CategorySchemeState()
     @State private var isApplyingSystemScheme = false
+    @State private var showApplySystemSchemeConfirmation = false
 
     private func scanApps() {
         DispatchQueue.global(qos: .userInitiated).async {
@@ -135,7 +136,15 @@
     }
 
     private func applySystemInitialScheme() {
-        guard confirmApplySystemInitialScheme() else { return }
+        guard !isApplyingSystemScheme else { return }
+        withAnimation(.easeOut(duration: 0.16)) {
+            showApplySystemSchemeConfirmation = true
+        }
+    }
+
+    private func performApplySystemInitialScheme() {
+        guard !isApplyingSystemScheme else { return }
+        showApplySystemSchemeConfirmation = false
         TagDatabase.flushPendingCategorySchemeBackupBatch()
         isApplyingSystemScheme = true
 
@@ -166,16 +175,6 @@
                 }
             }
         }
-    }
-
-    private func confirmApplySystemInitialScheme() -> Bool {
-        let alert = NSAlert()
-        alert.messageText = tr("settings.applySystemSchemeWarningTitle")
-        alert.informativeText = tr("settings.applySystemSchemeWarningMessage")
-        alert.alertStyle = .warning
-        alert.addButton(withTitle: tr("settings.confirmApplyScheme"))
-        alert.addButton(withTitle: tr("settings.cancel"))
-        return alert.runModal() == .alertFirstButtonReturn
     }
 
     private func formattedSystemSchemeAppliedMessage(_ summary: SmartStartSummary) -> String {
@@ -697,6 +696,23 @@
                 }
                 .transition(.opacity)
             }
+
+            if showApplySystemSchemeConfirmation {
+                ApplySystemSchemeConfirmationView(
+                    title: tr("settings.applySystemSchemeWarningTitle"),
+                    message: tr("settings.applySystemSchemeWarningMessage"),
+                    confirmTitle: tr("settings.confirmApplyScheme"),
+                    cancelTitle: tr("settings.cancel"),
+                    onConfirm: performApplySystemInitialScheme,
+                    onCancel: {
+                        withAnimation(.easeOut(duration: 0.14)) {
+                            showApplySystemSchemeConfirmation = false
+                        }
+                    }
+                )
+                .zIndex(3)
+                .transition(.opacity)
+            }
         }
         .frame(width: settingsWindowWidth, height: 460)
         .onAppear {
@@ -714,6 +730,108 @@
     }
 }
 
+private struct ApplySystemSchemeConfirmationView: View {
+    let title: String
+    let message: String
+    let confirmTitle: String
+    let cancelTitle: String
+    let onConfirm: () -> Void
+    let onCancel: () -> Void
+
+    var body: some View {
+        ZStack {
+            Color.black.opacity(0.20)
+                .contentShape(Rectangle())
+                .onTapGesture { }
+
+            VStack(alignment: .leading, spacing: 20) {
+                HStack(alignment: .top, spacing: 12) {
+                    ZStack {
+                        Circle()
+                            .fill(Color.orange.opacity(0.16))
+                            .frame(width: 38, height: 38)
+                        Image(systemName: "exclamationmark.triangle.fill")
+                            .font(.system(size: 19, weight: .semibold))
+                            .foregroundStyle(Color.orange)
+                    }
+                    .frame(width: 40, height: 40)
+
+                    VStack(alignment: .leading, spacing: 8) {
+                        Text(title)
+                            .font(.system(size: 16, weight: .semibold))
+                            .foregroundStyle(.primary)
+                            .fixedSize(horizontal: false, vertical: true)
+                        Text(message)
+                            .font(.system(size: 13))
+                            .foregroundStyle(Color.primary.opacity(0.78))
+                            .lineSpacing(2)
+                            .fixedSize(horizontal: false, vertical: true)
+                    }
+                }
+
+                HStack(spacing: 10) {
+                    Spacer()
+                    ConfirmationActionButton(
+                        title: cancelTitle,
+                        prominent: false,
+                        action: onCancel
+                    )
+                        .keyboardShortcut(.cancelAction)
+                    ConfirmationActionButton(
+                        title: confirmTitle,
+                        prominent: true,
+                        action: onConfirm
+                    )
+                        .keyboardShortcut(.defaultAction)
+                }
+            }
+            .padding(22)
+            .frame(width: 440)
+            .background(
+                RoundedRectangle(cornerRadius: 8, style: .continuous)
+                    .fill(Color(nsColor: .windowBackgroundColor))
+            )
+            .overlay(
+                RoundedRectangle(cornerRadius: 8, style: .continuous)
+                    .stroke(Color.primary.opacity(0.16), lineWidth: 1)
+            )
+            .shadow(color: .black.opacity(0.24), radius: 20, x: 0, y: 10)
+        }
+        .frame(maxWidth: .infinity, maxHeight: .infinity)
+    }
+}
+
+private struct ConfirmationActionButton: View {
+    let title: String
+    let prominent: Bool
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            Text(title)
+                .font(.system(size: 13, weight: .semibold))
+                .foregroundStyle(prominent ? Color.white : Color.primary)
+                .lineLimit(1)
+                .minimumScaleFactor(0.86)
+                .frame(minWidth: 82, minHeight: 32)
+                .padding(.horizontal, 10)
+                .background(
+                    RoundedRectangle(cornerRadius: 7, style: .continuous)
+                        .fill(prominent ? Color.accentColor : Color(nsColor: .controlBackgroundColor))
+                )
+                .overlay(
+                    RoundedRectangle(cornerRadius: 7, style: .continuous)
+                        .stroke(
+                            prominent ? Color.accentColor.opacity(0.40) : Color.primary.opacity(0.16),
+                            lineWidth: 1
+                        )
+                )
+        }
+        .buttonStyle(.plain)
+        .contentShape(RoundedRectangle(cornerRadius: 7, style: .continuous))
+    }
+}
+
 private struct DisplayModeOptionButton: View {
     let mode: String
     let title: String
diff --git a/Apptag/SmartCategorization/SmartCategorizationDraft.swift b/Apptag/SmartCategorization/SmartCategorizationDraft.swift
index a3b9715..552466e 100644
--- a/Apptag/SmartCategorization/SmartCategorizationDraft.swift
+++ b/Apptag/SmartCategorization/SmartCategorizationDraft.swift
@@ -54,6 +54,7 @@
     let reason: String
     let provenance: [String]
     let defaultNote: String?
+    let defaultNoteCandidates: [String]
 
     var id: String {
         if let bundleIdentifier, !bundleIdentifier.isEmpty {
@@ -78,7 +79,8 @@
         source: SmartCategorizationAssignmentSource,
         reason: String,
         provenance: [String] = [],
-        defaultNote: String? = nil
+        defaultNote: String? = nil,
+        defaultNoteCandidates: [String] = []
     ) {
         self.appName = appName
         self.bundleIdentifier = bundleIdentifier
@@ -89,6 +91,7 @@
         self.reason = reason
         self.provenance = provenance
         self.defaultNote = defaultNote
+        self.defaultNoteCandidates = defaultNoteCandidates
     }
 
     private static func uniqueOrdered(_ ids: [SmartCategoryID]) -> [SmartCategoryID] {
diff --git a/Apptag/SmartCategorization/SmartStartService.swift b/Apptag/SmartCategorization/SmartStartService.swift
index 97016f4..220373a 100644
--- a/Apptag/SmartCategorization/SmartStartService.swift
+++ b/Apptag/SmartCategorization/SmartStartService.swift
@@ -261,7 +261,8 @@
                 source: .localCatalog,
                 reason: "Matched Smart Start catalog entry: \(matched.name)",
                 provenance: matched.sourceEvidence,
-                defaultNote: matched.localizedNote
+                defaultNote: matched.localizedNote,
+                defaultNoteCandidates: matched.notes?.values.map { $0 } ?? []
             ))
         }
 
@@ -348,12 +349,21 @@
             }
 
             if let defaultNote = assignment.defaultNote?.trimmingCharacters(in: .whitespacesAndNewlines),
-               !defaultNote.isEmpty,
-               store.appNotes[path]?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty != false {
-                store.appNotes[path] = String(defaultNote.prefix(TagDatabase.maxAppNoteLength))
-                uncommonPaths.insert(path)
-                if store.uncommonSources[path] == nil {
-                    store.uncommonSources[path] = .auto
+               !defaultNote.isEmpty {
+                let currentNote = store.appNotes[path]?.trimmingCharacters(in: .whitespacesAndNewlines)
+                let knownDefaultNotes = Set(assignment.defaultNoteCandidates.compactMap(normalizedNote))
+                let normalizedCurrentNote = normalizedNote(currentNote)
+                let shouldSeedNote = currentNote?.isEmpty != false
+                    || normalizedCurrentNote.map { knownDefaultNotes.contains($0) } == true
+
+                if shouldSeedNote {
+                    store.appNotes[path] = String(defaultNote.prefix(TagDatabase.maxAppNoteLength))
+                    if currentNote?.isEmpty != false {
+                        uncommonPaths.insert(path)
+                        if store.uncommonSources[path] == nil {
+                            store.uncommonSources[path] = .auto
+                        }
+                    }
                 }
             }
         }
diff --git a/Apptag/TagGroupView.swift b/Apptag/TagGroupView.swift
index bea1637..02075e9 100644
--- a/Apptag/TagGroupView.swift
+++ b/Apptag/TagGroupView.swift
@@ -14,6 +14,7 @@
     var onBubbleHover: ((AppInfo, CGRect, Bool) -> Void)? = nil
     var onEditNote: ((AppInfo, CGRect) -> Void)? = nil
     var bubbleDisabled: Bool = false
+    var dragResetToken: Int = 0
     @Binding var hoveredAppItemID: String?
     var onDropApp: ((String, String, Bool) -> Void)? = nil
 
@@ -61,6 +62,7 @@
                         onEditNote: onEditNote,
                         bubbleDisabled: bubbleDisabled,
                         itemID: "\(group.name)|\(app.path.path)",
+                        dragResetToken: dragResetToken,
                         hoveredAppItemID: $hoveredAppItemID,
                         onSelect: { onSelectApp(app) }
                     )
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db40728..40e1e5d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@
 - 为菜单栏图标设置稳定 `NSStatusItem.autosaveName`,让 macOS 与 Thaw 更容易识别为同一个菜单栏项
 - 菜单栏刷新时复用已有 `NSStatusItem`,避免语言刷新等场景反复删除重建导致位置记忆失效
 - 为菜单栏按钮补充稳定 identifier 与 accessibility 标识,提升第三方菜单栏管理工具识别稳定性
+- 修复全新用户使用英文等非中文界面时,部分 Apple App 默认用途备注仍被旧路径种成中文的问题
+- 修复拖动 App 到“未分类”确认/取消后,浮层可能关闭且图标持续抖动、拖拽状态残留的问题
+- 优化标签列表拖拽排序反馈:移除标签无限抖动,改为拖拽时其他标签降透明度,并缩短长按响应时间
+- 修复设置页“应用系统智能化初始分类”确认提示可能跑到设置窗口背后、被遮挡的问题,并优化为更清晰的实底确认框
+- 修复右侧标签列表点击后可能停留在拖拽视觉状态,导致后续标签无法正常点击切换的问题
 - 版本号更新为 `7.3.5`,Build 更新为 `744`
 
 ## [7.3.4] — 2026-05-18

--
Gitblit v1.9.3