Ariver
2026-05-18 9abd2554308521c72bd0c3b60f6e7f5e7a129a8b
Fix built-in app grouping and bubble hover settings
36 files modified
254 ■■■■ changed files
Apptag/AppGridItem.swift 30 ●●●● patch | view | raw | blame | history
Apptag/ContentView.swift 6 ●●●●● patch | view | raw | blame | history
Apptag/DataLayer.swift 12 ●●●● patch | view | raw | blame | history
Apptag/Info.plist 4 ●●●● patch | view | raw | blame | history
Apptag/Localization/ar-Najdi.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ar.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/cs.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/da.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/de.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/en.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/es.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/fr.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/id.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/it.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ja.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ko.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ms.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/nb.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/nl.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/nn.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/no.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/pl.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/pt-BR.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ro.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/ru.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/sr-Cyrl.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/sv.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/th.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/tr.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/uk.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/vi.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/zh-Hans.json 5 ●●●● patch | view | raw | blame | history
Apptag/Localization/zh-Hant.json 5 ●●●● patch | view | raw | blame | history
Apptag/PreferencesView.swift 46 ●●●● patch | view | raw | blame | history
Apptag/TagGroupView.swift 3 ●●●●● patch | view | raw | blame | history
CHANGELOG.md 8 ●●●●● patch | view | raw | blame | history
Apptag/AppGridItem.swift
@@ -12,9 +12,10 @@
    var onDragModeChange: ((Bool) -> Void)? = nil
    var onBubbleHover: ((AppInfo, CGRect, Bool) -> Void)? = nil
    var onEditNote: ((AppInfo, CGRect) -> Void)? = nil
    var itemID: String
    @Binding var hoveredAppItemID: String?
    let onSelect: () -> Void
    @State private var isHovered = false
    @State private var wiggle = false
    @State private var interactionFrame: CGRect = .zero
    @AppStorage("showUncommonAppBubbles") private var showUncommonAppBubbles = AppDefaults.showUncommonAppBubbles
@@ -35,6 +36,7 @@
    private var iconSlotSize: CGFloat { iconSize * Self.hoverScale }
    private var labelWidth: CGFloat { iconSize + 20 }
    private var labelHeight: CGFloat { Self.labelHeight }
    private var isHovered: Bool { hoveredAppItemID == itemID }
    var body: some View {
        VStack(spacing: 6) {
@@ -62,7 +64,7 @@
                .lineLimit(1)
                .truncationMode(.tail)
                .frame(width: labelWidth, height: labelHeight)
                .opacity(showName ? 1 : (isHovered ? 0.85 : 0))
                .opacity(showName ? 1 : (shouldShowHoverName ? 0.85 : 0))
        }
        .padding(.vertical, 8)
        .padding(.horizontal, 4)
@@ -70,12 +72,15 @@
        .background(
            AppGridItemHoverTracker { hovering, frame in
                interactionFrame = frame
                setHoverState(hovering)
                if hovering {
                    setHoverState(true)
                    guard shouldShowAppBubble else { return }
                    onBubbleHover?(app, frame, hovering)
                } else {
                } else if isHovered {
                    setHoverState(false)
                    onBubbleHover?(app, frame, hovering)
                } else {
                    setHoverState(false)
                }
            }
        )
@@ -95,16 +100,29 @@
        .onChange(of: dragModeActive) { _, active in
            wiggle = active
        }
        .onDisappear {
            if hoveredAppItemID == itemID {
                hoveredAppItemID = nil
                onBubbleHover?(app, interactionFrame, false)
            }
        }
    }
    private var shouldShowAppBubble: Bool {
        !showUncommonAppBubbles || app.isUncommon
    }
    private var shouldShowHoverName: Bool {
        !showName && !shouldShowAppBubble && isHovered
    }
    private func setHoverState(_ hovering: Bool) {
        guard isHovered != hovering else { return }
        withAnimation(hovering ? Self.hoverInAnimation : Self.hoverOutAnimation) {
            isHovered = hovering
            if hovering {
                hoveredAppItemID = itemID
            } else if hoveredAppItemID == itemID {
                hoveredAppItemID = nil
            }
        }
    }
}
Apptag/ContentView.swift
@@ -370,6 +370,7 @@
    @State private var refreshInProgress = false
    @State private var refreshAgainAfterCurrent = false
    @State private var refreshAgainForceLayout = false
    @State private var hoveredAppItemID: String? = nil
    @State private var hoveredBubble: AppBubbleContext? = nil
    @State private var editingBubble: AppBubbleContext? = nil
    @State private var bubbleDraftNote = ""
@@ -825,6 +826,7 @@
                            onDragModeChange: { setAppDragMode($0) },
                            onBubbleHover: handleBubbleHover,
                            onEditNote: beginEditingBubbleNote,
                            hoveredAppItemID: $hoveredAppItemID,
                            onDropApp: { path, source, copy in
                                dropApp(path: path, sourceTag: source, targetTag: group.name, copy: copy)
                            }
@@ -927,6 +929,8 @@
                        onDragModeChange: { setAppDragMode($0) },
                        onBubbleHover: handleBubbleHover,
                        onEditNote: beginEditingBubbleNote,
                        itemID: "\(group.name)|\(app.path.path)",
                        hoveredAppItemID: $hoveredAppItemID,
                        onSelect: { openApp(app) }
                    )
                }
@@ -1163,6 +1167,8 @@
                                onDragModeChange: { setAppDragMode($0) },
                                onBubbleHover: handleBubbleHover,
                                onEditNote: beginEditingBubbleNote,
                                itemID: "\(group.name)|\(app.path.path)",
                                hoveredAppItemID: $hoveredAppItemID,
                                onSelect: { openApp(app) }
                            )
                                .frame(width: cellWidth)
Apptag/DataLayer.swift
@@ -220,12 +220,18 @@
        var seenAppIDsByGroup: [String: Set<URL>] = [:]
        for app in apps {
            if app.isAppleApp {
                appendGroupedApp(app, to: macCategory, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
            }
            if app.tags.isEmpty {
                let bucket = app.isAppleApp ? macCategory : defaultGroupName
                appendGroupedApp(app, to: bucket, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
                if !app.isAppleApp {
                    appendGroupedApp(app, to: defaultGroupName, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
                }
            } else {
                for tag in uniqueOrdered(app.tags) {
                    let displayName = nameOverrides[tag] ?? tag
                    guard displayName != macCategory else { continue }
                    appendGroupedApp(app, to: displayName, groups: &dict, seenAppIDsByGroup: &seenAppIDsByGroup)
                }
            }
@@ -1253,7 +1259,7 @@
        }
        var current = store.appTags[path] ?? []
        if !copy, !sourceTag.isEmpty {
        if !copy, !sourceTag.isEmpty, sourceTag != "Mac自带", sourceTag != tr("group.appleBuiltIn") {
            current.removeAll { $0 == sourceTag }
        }
        if !current.contains(targetTag) {
Apptag/Info.plist
@@ -19,9 +19,9 @@
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>7.2.0</string>
    <string>7.3.0</string>
    <key>CFBundleVersion</key>
    <string>738</string>
    <string>739</string>
    <key>LSMinimumSystemVersion</key>
    <string>15.0</string>
    <key>NSHighResolutionCapable</key>
Apptag/Localization/ar-Najdi.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "تم تطبيق %tagCount% تعيينات وسوم على %appCount% تطبيقات.",
  "settings.systemSchemeApplyFailed": "ما فيه نتائج تصنيف ذكي قابلة للتطبيق.",
  "scheme.local": "المخطط المحلي",
  "scheme.systemSmartStart": "مخطط البداية الذكي للنظام"
  "scheme.systemSmartStart": "مخطط البداية الذكي للنظام",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/ar.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "تم تطبيق %tagCount% تعيينات وسوم على %appCount% تطبيقات.",
  "settings.systemSchemeApplyFailed": "لا توجد نتائج تصنيف ذكي يمكن تطبيقها.",
  "scheme.local": "المخطط المحلي",
  "scheme.systemSmartStart": "مخطط البداية الذكي للنظام"
  "scheme.systemSmartStart": "مخطط البداية الذكي للنظام",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/cs.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Použito %tagCount% přiřazení štítků pro %appCount% aplikací.",
  "settings.systemSchemeApplyFailed": "Nejsou k dispozici žádné výsledky chytré klasifikace.",
  "scheme.local": "Místní schéma",
  "scheme.systemSmartStart": "Chytré počáteční schéma systému"
  "scheme.systemSmartStart": "Chytré počáteční schéma systému",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/da.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% tagtildelinger anvendt på %appCount% apps.",
  "settings.systemSchemeApplyFailed": "Ingen smarte klassificeringsresultater kan anvendes.",
  "scheme.local": "Lokalt skema",
  "scheme.systemSmartStart": "Systemets smarte startskema"
  "scheme.systemSmartStart": "Systemets smarte startskema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/de.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% Tag-Zuordnungen auf %appCount% Apps angewendet.",
  "settings.systemSchemeApplyFailed": "Es sind keine intelligenten Klassifizierungsergebnisse verfügbar.",
  "scheme.local": "Lokales Schema",
  "scheme.systemSmartStart": "Intelligentes System-Startschema"
  "scheme.systemSmartStart": "Intelligentes System-Startschema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/en.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Applied %tagCount% tag assignments across %appCount% apps.",
  "settings.systemSchemeApplyFailed": "No smart classification results were available to apply.",
  "scheme.local": "Local scheme",
  "scheme.systemSmartStart": "System smart initial scheme"
  "scheme.systemSmartStart": "System smart initial scheme",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/es.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Se aplicaron %tagCount% asignaciones de etiquetas a %appCount% apps.",
  "settings.systemSchemeApplyFailed": "No hay resultados de clasificación inteligente para aplicar.",
  "scheme.local": "Esquema local",
  "scheme.systemSmartStart": "Esquema inicial inteligente del sistema"
  "scheme.systemSmartStart": "Esquema inicial inteligente del sistema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/fr.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% attributions de tags appliquées à %appCount% apps.",
  "settings.systemSchemeApplyFailed": "Aucun résultat de classification intelligente à appliquer.",
  "scheme.local": "Schéma local",
  "scheme.systemSmartStart": "Schéma initial intelligent du système"
  "scheme.systemSmartStart": "Schéma initial intelligent du système",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/id.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Menerapkan %tagCount% penetapan tag pada %appCount% app.",
  "settings.systemSchemeApplyFailed": "Tidak ada hasil klasifikasi pintar yang dapat diterapkan.",
  "scheme.local": "Skema lokal",
  "scheme.systemSmartStart": "Skema awal pintar sistem"
  "scheme.systemSmartStart": "Skema awal pintar sistem",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/it.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Applicate %tagCount% assegnazioni tag a %appCount% app.",
  "settings.systemSchemeApplyFailed": "Nessun risultato di classificazione intelligente disponibile.",
  "scheme.local": "Schema locale",
  "scheme.systemSmartStart": "Schema iniziale intelligente di sistema"
  "scheme.systemSmartStart": "Schema iniziale intelligente di sistema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/ja.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%appCount% 個のアプリに %tagCount% 件のタグ割り当てを適用しました。",
  "settings.systemSchemeApplyFailed": "適用できるスマート分類結果がありませんでした。",
  "scheme.local": "ローカル分類スキーム",
  "scheme.systemSmartStart": "システム初期スマート分類"
  "scheme.systemSmartStart": "システム初期スマート分類",
  "settings.bubbleDisplayScope": "吹き出しの表示対象:",
  "settings.bubbleAllApps": "すべてのアプリ",
  "settings.bubbleUncommonOnly": "あまり使わないアプリのみ"
}
Apptag/Localization/ko.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%appCount%개의 앱에 %tagCount%개의 태그 할당을 적용했습니다.",
  "settings.systemSchemeApplyFailed": "적용할 수 있는 스마트 분류 결과가 없습니다.",
  "scheme.local": "로컬 분류 방식",
  "scheme.systemSmartStart": "시스템 스마트 초기 분류"
  "scheme.systemSmartStart": "시스템 스마트 초기 분류",
  "settings.bubbleDisplayScope": "말풍선 표시 대상:",
  "settings.bubbleAllApps": "모든 앱",
  "settings.bubbleUncommonOnly": "자주 쓰지 않는 앱만"
}
Apptag/Localization/ms.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% tugasan tag digunakan pada %appCount% app.",
  "settings.systemSchemeApplyFailed": "Tiada hasil pengelasan pintar untuk digunakan.",
  "scheme.local": "Skema setempat",
  "scheme.systemSmartStart": "Skema awal pintar sistem"
  "scheme.systemSmartStart": "Skema awal pintar sistem",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/nb.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Brukte %tagCount% taggtildelinger på %appCount% apper.",
  "settings.systemSchemeApplyFailed": "Ingen smarte klassifiseringsresultater kan brukes.",
  "scheme.local": "Lokalt skjema",
  "scheme.systemSmartStart": "Systemets smarte startskjema"
  "scheme.systemSmartStart": "Systemets smarte startskjema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/nl.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% tagtoewijzingen toegepast op %appCount% apps.",
  "settings.systemSchemeApplyFailed": "Geen slimme classificatieresultaten beschikbaar om toe te passen.",
  "scheme.local": "Lokaal schema",
  "scheme.systemSmartStart": "Slim beginschema van systeem"
  "scheme.systemSmartStart": "Slim beginschema van systeem",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/nn.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Brukte %tagCount% taggtildelinger på %appCount% apper.",
  "settings.systemSchemeApplyFailed": "Ingen smarte klassifiseringsresultater kan brukes.",
  "scheme.local": "Lokalt skjema",
  "scheme.systemSmartStart": "Systemets smarte startskjema"
  "scheme.systemSmartStart": "Systemets smarte startskjema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/no.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Brukte %tagCount% taggtildelinger på %appCount% apper.",
  "settings.systemSchemeApplyFailed": "Ingen smarte klassifiseringsresultater kan brukes.",
  "scheme.local": "Lokalt skjema",
  "scheme.systemSmartStart": "Systemets smarte startskjema"
  "scheme.systemSmartStart": "Systemets smarte startskjema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/pl.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Zastosowano %tagCount% przypisań tagów dla %appCount% aplikacji.",
  "settings.systemSchemeApplyFailed": "Brak wyników inteligentnej klasyfikacji do zastosowania.",
  "scheme.local": "Schemat lokalny",
  "scheme.systemSmartStart": "Inteligentny schemat początkowy systemu"
  "scheme.systemSmartStart": "Inteligentny schemat początkowy systemu",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/pt-BR.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Foram aplicadas %tagCount% atribuições de tags em %appCount% apps.",
  "settings.systemSchemeApplyFailed": "Não há resultados de classificação inteligente para aplicar.",
  "scheme.local": "Esquema local",
  "scheme.systemSmartStart": "Esquema inicial inteligente do sistema"
  "scheme.systemSmartStart": "Esquema inicial inteligente do sistema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/ro.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Au fost aplicate %tagCount% atribuiri de etichete pentru %appCount% aplicații.",
  "settings.systemSchemeApplyFailed": "Nu există rezultate de clasificare inteligentă de aplicat.",
  "scheme.local": "Schema locală",
  "scheme.systemSmartStart": "Schema inteligentă inițială a sistemului"
  "scheme.systemSmartStart": "Schema inteligentă inițială a sistemului",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/ru.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Применено %tagCount% назначений тегов для %appCount% приложений.",
  "settings.systemSchemeApplyFailed": "Нет результатов интеллектуальной классификации для применения.",
  "scheme.local": "Локальная схема",
  "scheme.systemSmartStart": "Интеллектуальная начальная схема системы"
  "scheme.systemSmartStart": "Интеллектуальная начальная схема системы",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/sr-Cyrl.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Примењено је %tagCount% додела ознака за %appCount% апликација.",
  "settings.systemSchemeApplyFailed": "Нема резултата паметне класификације за примену.",
  "scheme.local": "Локална шема",
  "scheme.systemSmartStart": "Паметна почетна шема система"
  "scheme.systemSmartStart": "Паметна почетна шема система",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/sv.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%tagCount% taggtilldelningar tillämpades på %appCount% appar.",
  "settings.systemSchemeApplyFailed": "Inga smarta klassificeringsresultat kunde tillämpas.",
  "scheme.local": "Lokalt schema",
  "scheme.systemSmartStart": "Systemets smarta startschema"
  "scheme.systemSmartStart": "Systemets smarta startschema",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/th.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "นำการกำหนดแท็ก %tagCount% รายการไปใช้กับแอป %appCount% รายการแล้ว",
  "settings.systemSchemeApplyFailed": "ไม่มีผลการจัดหมวดหมู่อัจฉริยะให้ใช้",
  "scheme.local": "ชุดภายในเครื่อง",
  "scheme.systemSmartStart": "ชุดเริ่มต้นอัจฉริยะของระบบ"
  "scheme.systemSmartStart": "ชุดเริ่มต้นอัจฉริยะของระบบ",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/tr.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "%appCount% uygulamada %tagCount% etiket ataması uygulandı.",
  "settings.systemSchemeApplyFailed": "Uygulanacak akıllı sınıflandırma sonucu yok.",
  "scheme.local": "Yerel şema",
  "scheme.systemSmartStart": "Sistem akıllı başlangıç şeması"
  "scheme.systemSmartStart": "Sistem akıllı başlangıç şeması",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/uk.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Застосовано %tagCount% призначень тегів для %appCount% програм.",
  "settings.systemSchemeApplyFailed": "Немає результатів розумної класифікації для застосування.",
  "scheme.local": "Локальна схема",
  "scheme.systemSmartStart": "Розумна початкова схема системи"
  "scheme.systemSmartStart": "Розумна початкова схема системи",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/vi.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "Đã áp dụng %tagCount% gán thẻ cho %appCount% ứng dụng.",
  "settings.systemSchemeApplyFailed": "Không có kết quả phân loại thông minh để áp dụng.",
  "scheme.local": "Sơ đồ cục bộ",
  "scheme.systemSmartStart": "Sơ đồ khởi tạo thông minh của hệ thống"
  "scheme.systemSmartStart": "Sơ đồ khởi tạo thông minh của hệ thống",
  "settings.bubbleDisplayScope": "Bubble tips show for:",
  "settings.bubbleAllApps": "All apps",
  "settings.bubbleUncommonOnly": "Uncommon apps only"
}
Apptag/Localization/zh-Hans.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "已为 %appCount% 个应用应用 %tagCount% 个标签分配。",
  "settings.systemSchemeApplyFailed": "没有找到可应用的智能分类结果。",
  "scheme.local": "本地分类方案",
  "scheme.systemSmartStart": "系统智能化初始分类"
  "scheme.systemSmartStart": "系统智能化初始分类",
  "settings.bubbleDisplayScope": "气泡提示框显示位置:",
  "settings.bubbleAllApps": "全部应用",
  "settings.bubbleUncommonOnly": "仅不常用应用"
}
Apptag/Localization/zh-Hant.json
@@ -139,5 +139,8 @@
  "settings.systemSchemeAppliedMessage": "已為 %appCount% 個 App 套用 %tagCount% 個標籤分配。",
  "settings.systemSchemeApplyFailed": "沒有找到可套用的智慧分類結果。",
  "scheme.local": "本機分類方案",
  "scheme.systemSmartStart": "系統智慧化初始分類"
  "scheme.systemSmartStart": "系統智慧化初始分類",
  "settings.bubbleDisplayScope": "氣泡提示框顯示位置:",
  "settings.bubbleAllApps": "全部 App",
  "settings.bubbleUncommonOnly": "僅不常用 App"
}
Apptag/PreferencesView.swift
@@ -219,6 +219,25 @@
        return FileManager.default.fileExists(atPath: path)
    }
    private func bubbleScopeOption(
        _ title: String,
        isSelected: Bool,
        action: @escaping () -> Void
    ) -> some View {
        Button(action: action) {
            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))
            }
            .foregroundStyle(isSelected ? Color.accentColor : Color.primary)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
    }
    private func syncSelectedLanguage() {
        let code = L10n.currentCode
        guard selectedLanguage != code else { return }
@@ -580,15 +599,28 @@
                Spacer(minLength: 22)
                VStack(alignment: .leading, spacing: 5) {
                    Toggle(tr("settings.uncommonBubble"), isOn: $showUncommonAppBubbles)
                HStack(alignment: .center, spacing: 14) {
                    Text(tr("settings.bubbleDisplayScope"))
                        .font(.system(size: 13, weight: .medium))
                    Text(tr("settings.uncommonBubbleDesc"))
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .fixedSize(horizontal: false, vertical: true)
                        .frame(width: 190, alignment: .trailing)
                    HStack(spacing: 20) {
                        bubbleScopeOption(
                            tr("settings.bubbleAllApps"),
                            isSelected: !showUncommonAppBubbles
                        ) {
                            showUncommonAppBubbles = false
                }
                .frame(width: 420, alignment: .leading)
                        bubbleScopeOption(
                            tr("settings.bubbleUncommonOnly"),
                            isSelected: showUncommonAppBubbles
                        ) {
                            showUncommonAppBubbles = true
                        }
                    }
                    .frame(width: 300, alignment: .leading)
                }
                .frame(width: 520, alignment: .center)
                Spacer(minLength: 18)
            }
Apptag/TagGroupView.swift
@@ -13,6 +13,7 @@
    var onDragModeChange: ((Bool) -> Void)? = nil
    var onBubbleHover: ((AppInfo, CGRect, Bool) -> Void)? = nil
    var onEditNote: ((AppInfo, CGRect) -> Void)? = nil
    @Binding var hoveredAppItemID: String?
    var onDropApp: ((String, String, Bool) -> Void)? = nil
    /// Adaptive columns — auto-fit based on icon size and available width.
@@ -57,6 +58,8 @@
                        onDragModeChange: onDragModeChange,
                        onBubbleHover: onBubbleHover,
                        onEditNote: onEditNote,
                        itemID: "\(group.name)|\(app.path.path)",
                        hoveredAppItemID: $hoveredAppItemID,
                        onSelect: { onSelectApp(app) }
                    )
                }
CHANGELOG.md
@@ -1,5 +1,13 @@
# Apptag Changelog
## [7.3.0] — 2026-05-18
- 修复 Apple 系统自带 App 被拖入其他标签后从「Mac自带」分组消失的问题,「Mac自带」现在作为客观属性始终保留
- 气泡提示设置改为「全部应用 / 仅不常用应用」二选一,并补齐多语言文案
- 统一 App 图标 hover 焦点状态,降低快速滑过、视图复用或同一 App 出现在多个分组时出现小名字残影的概率
- 当气泡提示范围选择「全部应用」时,hover 不再额外显示 App 图标下方的小名字,避免和大气泡重复
- 版本号更新为 `7.3.0`,Build 更新为 `739`
## [7.2.0] — 2026-05-18
- 统筹优化 App hover 交互:容器、图标和气泡不再同时做大幅动画,避免快速滑过时出现慢动作和卡顿