Ariver
2026-05-28 71bfdd4087d0b4d94bd5345cb45bb335617d737c
Confirm single-tag removal on empty grid drop
34 files modified
415 ■■■■■ changed files
Apptag/AppDefaults.swift 1 ●●●● patch | view | raw | blame | history
Apptag/AppDragCoordinator.swift 48 ●●●●● patch | view | raw | blame | history
Apptag/AppGridCollectionView.swift 25 ●●●●● patch | view | raw | blame | history
Apptag/ContentView.swift 112 ●●●●● patch | view | raw | blame | history
Apptag/EditModeViews.swift 84 ●●●●● 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/AppDefaults.swift
@@ -23,6 +23,7 @@
            "showDockIcon": showDockIcon,
            "launchAtLogin": launchAtLogin,
            "showUncommonAppBubbles": showUncommonAppBubbles,
            "skipTagRemovalDropConfirm": false,
            LauncherHotkeyRegistrationStore.mainStateKey: LauncherHotkeyRegistrationState.active.rawValue,
            LauncherHotkeyRegistrationStore.quickSearchStateKey: LauncherHotkeyRegistrationState.active.rawValue
        ])
Apptag/AppDragCoordinator.swift
@@ -10,7 +10,12 @@
        var tag: String
    }
    struct EmptyDropTarget {
        weak var view: AppEmptyDropReceivingView?
    }
    private var targets: [UUID: DropTarget] = [:]
    private var emptyTargets: [UUID: EmptyDropTarget] = [:]
    private weak var dragHostWindow: NSWindow?
    private weak var dragLayerHostView: NSView?
    private var dragLayer: CALayer?
@@ -39,6 +44,17 @@
    func unregister(id: UUID) {
        targets.removeValue(forKey: id)
    }
    func registerEmptyDropTarget(id: UUID, view: AppEmptyDropReceivingView) {
        if emptyTargets.count > 64 {
            pruneDeadTargets()
        }
        emptyTargets[id] = EmptyDropTarget(view: view)
    }
    func unregisterEmptyDropTarget(id: UUID) {
        emptyTargets.removeValue(forKey: id)
    }
    func beginDrag(image: NSImage, payload: String, at screenPoint: NSPoint, copy: Bool, in hostWindow: NSWindow?) {
@@ -142,7 +158,23 @@
            .sorted { $0.1 < $1.1 }
            .first?.0
        hitTarget?.performDrop(path: path, source: source, copy: copy)
        if let hitTarget {
            hitTarget.performDrop(path: path, source: source, copy: copy)
            return
        }
        let emptyTarget = emptyTargets.values
            .compactMap { target -> (AppEmptyDropReceivingView, CGFloat)? in
                guard let view = target.view,
                      let frame = view.screenFrame(),
                      frame.contains(screenPoint)
                else { return nil }
                return (view, frame.width * frame.height)
            }
            .sorted { $0.1 < $1.1 }
            .first?.0
        emptyTarget?.performEmptyDrop(path: path, source: source, screenPoint: screenPoint, copy: copy)
    }
    func cancelDrag() {
@@ -174,6 +206,7 @@
    private func pruneDeadTargets() {
        targets = targets.filter { $0.value.view != nil }
        emptyTargets = emptyTargets.filter { $0.value.view != nil }
    }
    private static func cgImage(from image: NSImage) -> CGImage? {
@@ -231,6 +264,11 @@
    func performDrop(path: String, source: String, copy: Bool)
}
protocol AppEmptyDropReceivingView: AnyObject {
    func screenFrame() -> NSRect?
    func performEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool)
}
extension AppDropTargetReceivingView where Self: NSView {
    func screenFrame() -> NSRect? {
        guard let window else { return nil }
@@ -239,6 +277,14 @@
    }
}
extension AppEmptyDropReceivingView where Self: NSView {
    func screenFrame() -> NSRect? {
        guard let window else { return nil }
        let rectInWindow = convert(bounds, to: nil)
        return window.convertToScreen(rectInWindow)
    }
}
struct AppDropTargetView: NSViewRepresentable {
    let targetTag: String
    let onDropApp: (String, String, Bool) -> Void
Apptag/AppGridCollectionView.swift
@@ -45,6 +45,7 @@
    let onBubbleHover: (AppInfo, CGRect, AppBubbleHoverEvent) -> Void
    let onEditNote: (AppInfo, CGRect) -> Void
    let onDropApp: (String, String, String, Bool) -> Void
    let onDropOutsideGroup: (String, String, Bool) -> Void
    let onGroupActivate: (String) -> Void
    let onScrollActivity: () -> Void
    let onDragModeChange: (Bool) -> Void
@@ -76,6 +77,7 @@
            onBubbleHover: onBubbleHover,
            onEditNote: onEditNote,
            onDropApp: onDropApp,
            onDropOutsideGroup: onDropOutsideGroup,
            onGroupActivate: onGroupActivate,
            onScrollActivity: onScrollActivity,
            onDragModeChange: onDragModeChange
@@ -104,6 +106,7 @@
        var onBubbleHover: (AppInfo, CGRect, AppBubbleHoverEvent) -> Void = { _, _, _ in }
        var onEditNote: (AppInfo, CGRect) -> Void = { _, _ in }
        var onDropApp: (String, String, String, Bool) -> Void = { _, _, _, _ in }
        var onDropOutsideGroup: (String, String, Bool) -> Void = { _, _, _ in }
        var onGroupActivate: (String) -> Void = { _ in }
        var onScrollActivity: () -> Void = {}
        var onDragModeChange: (Bool) -> Void = { _ in }
@@ -124,6 +127,7 @@
            onBubbleHover: @escaping (AppInfo, CGRect, AppBubbleHoverEvent) -> Void,
            onEditNote: @escaping (AppInfo, CGRect) -> Void,
            onDropApp: @escaping (String, String, String, Bool) -> Void,
            onDropOutsideGroup: @escaping (String, String, Bool) -> Void,
            onGroupActivate: @escaping (String) -> Void,
            onScrollActivity: @escaping () -> Void,
            onDragModeChange: @escaping (Bool) -> Void
@@ -143,6 +147,7 @@
            self.onBubbleHover = onBubbleHover
            self.onEditNote = onEditNote
            self.onDropApp = onDropApp
            self.onDropOutsideGroup = onDropOutsideGroup
            self.onGroupActivate = onGroupActivate
            self.onScrollActivity = onScrollActivity
            self.onDragModeChange = onDragModeChange
@@ -250,7 +255,8 @@
    }
}
final class AppGridCollectionHostView: NSView {
final class AppGridCollectionHostView: NSView, AppEmptyDropReceivingView {
    private let emptyDropTargetID = UUID()
    private let scrollView = NSScrollView()
    private let collectionView = NSCollectionView()
    private let gridLayout = AppGridContainerCollectionLayout()
@@ -277,6 +283,7 @@
            NotificationCenter.default.removeObserver(scrollObserver)
        }
        scrollUnfreezeWorkItem?.cancel()
        AppDragCoordinator.shared.unregisterEmptyDropTarget(id: emptyDropTargetID)
    }
    func configure(coordinator: AppGridCollectionView.Coordinator) {
@@ -319,6 +326,22 @@
        }
    }
    override func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        if window == nil {
            AppDragCoordinator.shared.unregisterEmptyDropTarget(id: emptyDropTargetID)
        } else {
            AppDragCoordinator.shared.registerEmptyDropTarget(id: emptyDropTargetID, view: self)
        }
    }
    func performEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool) {
        guard let coordinator,
              coordinator.displayStyle != .flat
        else { return }
        coordinator.onDropOutsideGroup(path, source, copy)
    }
    private func setup() {
        wantsLayer = true
        layer?.backgroundColor = NSColor.clear.cgColor
Apptag/ContentView.swift
@@ -317,6 +317,12 @@
    let removableTags: [String]
}
private struct PendingTagRemovalDrop: Identifiable {
    let id = UUID()
    let app: AppInfo
    let tagName: String
}
private enum SmartStartNoticeMode {
    case autoApplied
    case suggestionOnly
@@ -371,6 +377,8 @@
    @State private var hoveredBubble: AppBubbleContext? = nil
    @State private var editingBubble: AppBubbleContext? = nil
    @State private var pendingUncategorizedDrop: PendingUncategorizedDrop? = nil
    @State private var pendingTagRemovalDrop: PendingTagRemovalDrop? = nil
    @State private var tagRemovalDropSuppressFuturePrompt = false
    @State private var appDragResetToken = 0
    @State private var bubbleDraftNote = ""
    @FocusState private var bubbleNoteFocused: Bool
@@ -406,13 +414,14 @@
    @AppStorage("displayMode") private var displayMode = AppDefaults.displayMode
    @AppStorage("hideAppNames") private var hideAppNames = AppDefaults.hideAppNames
    @AppStorage("showUncommonAppBubbles") private var showUncommonAppBubbles = AppDefaults.showUncommonAppBubbles
    @AppStorage("skipTagRemovalDropConfirm") private var skipTagRemovalDropConfirm = false
    private let editSidebarWidth: CGFloat = 188
    private let editSidebarHorizontalInset: CGFloat = 12
    private let floatingControlsTrailingInset: CGFloat = 20
    private let floatingControlsReservedWidth: CGFloat = 120
    private var appBubbleDisabled: Bool {
        appDragModeActive || pendingUncategorizedDrop != nil
        appDragModeActive || pendingUncategorizedDrop != nil || pendingTagRemovalDrop != nil
    }
    private let rightSidebarFloatingClearance: CGFloat = 44
@@ -470,6 +479,7 @@
                smartStartNoticeOverlay
                editActionFeedbackOverlay
                uncategorizedDropConfirmOverlay
                tagRemovalDropConfirmOverlay
            }
            quickSearchOverlay
@@ -959,6 +969,9 @@
                    onDropApp: { path, source, target, copy in
                        dropApp(path: path, sourceTag: source, targetTag: target, copy: copy)
                    },
                    onDropOutsideGroup: { path, source, copy in
                        dropAppOutsideGroup(path: path, sourceTag: source, copy: copy)
                    },
                    onGroupActivate: { groupName in
                        if isColorlessContainerMode {
                            toggleColorlessFill(groupName)
@@ -1434,6 +1447,34 @@
        }
        .ignoresSafeArea()
        .allowsHitTesting(pendingUncategorizedDrop != nil)
    }
    private var tagRemovalDropConfirmOverlay: some View {
        GeometryReader { proxy in
            if let pendingDrop = pendingTagRemovalDrop {
                ZStack {
                    Color.black.opacity(0.14)
                        .ignoresSafeArea()
                    TagRemovalDropConfirmBubble(
                        title: tr("drop.removeTagConfirmTitle"),
                        message: tagRemovalConfirmMessage(for: pendingDrop),
                        doNotRemindTitle: tr("drop.removeTagDoNotAskAgain"),
                        doNotRemind: $tagRemovalDropSuppressFuturePrompt,
                        cancelTitle: tr("drop.removeTagConfirmNo"),
                        confirmTitle: tr("drop.removeTagConfirmYes"),
                        onCancel: dismissTagRemovalDropConfirm,
                        onConfirm: confirmPendingTagRemovalDrop
                    )
                    .frame(width: min(560, max(350, proxy.size.width - 120)))
                    .position(x: proxy.size.width / 2, y: proxy.size.height / 2)
                    .transition(.scale(scale: 0.94).combined(with: .opacity))
                }
                .zIndex(711)
            }
        }
        .ignoresSafeArea()
        .allowsHitTesting(pendingTagRemovalDrop != nil)
    }
    private func buildEditActionFeedback(for selectedApps: [AppInfo], tags: [String]) -> EditActionFeedback {
@@ -1970,6 +2011,25 @@
        refreshApps(forceLayoutRefresh: true)
    }
    private func dropAppOutsideGroup(path: String, sourceTag: String, copy: Bool) {
        resetTransientDragState(keepingPendingTagRemovalDrop: true)
        guard isRemovableRegularTag(sourceTag) else { return }
        guard let app = allApps.first(where: { $0.path.path == path }),
              appHasTag(app, tagName: sourceTag)
        else { return }
        if skipTagRemovalDropConfirm {
            removeTagFromDroppedApp(app: app, tagName: sourceTag)
            return
        }
        clearAppBubbleState()
        tagRemovalDropSuppressFuturePrompt = false
        withAnimation(.spring(response: 0.24, dampingFraction: 0.84)) {
            pendingTagRemovalDrop = PendingTagRemovalDrop(app: app, tagName: sourceTag)
        }
    }
    private func confirmAndMoveAppToUncategorized(path: String) {
        guard let app = allApps.first(where: { $0.path.path == path }) else { return }
        let removableTags = removableRegularTags(for: app)
@@ -2019,6 +2079,16 @@
        )
    }
    private func tagRemovalConfirmMessage(for pendingDrop: PendingTagRemovalDrop) -> String {
        formattedFeedbackMessage(
            forKey: "drop.removeTagConfirmMessage",
            replacements: [
                "%appName%": pendingDrop.app.displayName,
                "%tagName%": displayTagName(pendingDrop.tagName)
            ]
        )
    }
    private func dismissUncategorizedDropConfirm() {
        resetTransientDragState(keepingPendingUncategorizedDrop: true)
        withAnimation(.easeOut(duration: 0.18)) {
@@ -2036,6 +2106,38 @@
        }
        guard !tags.isEmpty else { return }
        TagEditor.removeTags(tags, from: [path])
        showDropRefresh()
        refreshApps(forceLayoutRefresh: true)
    }
    private func dismissTagRemovalDropConfirm() {
        resetTransientDragState(keepingPendingTagRemovalDrop: true)
        tagRemovalDropSuppressFuturePrompt = false
        withAnimation(.easeOut(duration: 0.18)) {
            pendingTagRemovalDrop = nil
        }
    }
    private func confirmPendingTagRemovalDrop() {
        guard let pendingDrop = pendingTagRemovalDrop else { return }
        let app = pendingDrop.app
        let tagName = pendingDrop.tagName
        if tagRemovalDropSuppressFuturePrompt {
            skipTagRemovalDropConfirm = true
        }
        resetTransientDragState(keepingPendingTagRemovalDrop: true)
        tagRemovalDropSuppressFuturePrompt = false
        withAnimation(.easeOut(duration: 0.16)) {
            pendingTagRemovalDrop = nil
        }
        removeTagFromDroppedApp(app: app, tagName: tagName)
    }
    private func removeTagFromDroppedApp(app: AppInfo, tagName: String) {
        guard isRemovableRegularTag(tagName),
              appHasTag(app, tagName: tagName)
        else { return }
        TagEditor.removeTags([tagName], from: [app.path.path])
        showDropRefresh()
        refreshApps(forceLayoutRefresh: true)
    }
@@ -2114,7 +2216,10 @@
        }
    }
    private func resetTransientDragState(keepingPendingUncategorizedDrop: Bool = false) {
    private func resetTransientDragState(
        keepingPendingUncategorizedDrop: Bool = false,
        keepingPendingTagRemovalDrop: Bool = false
    ) {
        let hadAppDragState = appDragModeActive
        AppDragCoordinator.shared.cancelDrag()
        if appDragModeActive {
@@ -2139,6 +2244,9 @@
        if !keepingPendingUncategorizedDrop, pendingUncategorizedDrop != nil {
            pendingUncategorizedDrop = nil
        }
        if !keepingPendingTagRemovalDrop, pendingTagRemovalDrop != nil {
            pendingTagRemovalDrop = nil
        }
    }
    func refreshApps(forceLayoutRefresh: Bool = false) {
Apptag/EditModeViews.swift
@@ -387,3 +387,87 @@
        .onExitCommand(perform: onCancel)
    }
}
struct TagRemovalDropConfirmBubble: View {
    let title: String
    let message: String
    let doNotRemindTitle: String
    @Binding var doNotRemind: Bool
    let cancelTitle: String
    let confirmTitle: String
    let onCancel: () -> Void
    let onConfirm: () -> Void
    var body: some View {
        VStack(alignment: .leading, spacing: 18) {
            HStack(alignment: .top, spacing: 12) {
                Text(title)
                    .font(.system(size: 22, weight: .bold))
                    .foregroundStyle(.white)
                    .frame(maxWidth: .infinity, alignment: .leading)
                Button(action: onCancel) {
                    Image(systemName: "xmark")
                        .font(.system(size: 12, weight: .bold))
                        .foregroundStyle(.white.opacity(0.82))
                        .frame(width: 26, height: 26)
                        .background(
                            Circle()
                                .fill(Color.white.opacity(0.12))
                        )
                }
                .buttonStyle(.plain)
            }
            Text(message)
                .font(.system(size: 15, weight: .medium))
                .lineSpacing(4)
                .foregroundStyle(.white.opacity(0.84))
                .multilineTextAlignment(.leading)
                .fixedSize(horizontal: false, vertical: true)
                .frame(maxWidth: .infinity, alignment: .leading)
            Toggle(doNotRemindTitle, isOn: $doNotRemind)
                .toggleStyle(.checkbox)
                .font(.system(size: 13, weight: .medium))
                .foregroundStyle(.white.opacity(0.80))
            HStack(spacing: 12) {
                Spacer(minLength: 0)
                Button(action: onCancel) {
                    Text(cancelTitle)
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.white.opacity(0.86))
                        .frame(width: 92, height: 32)
                        .background(
                            RoundedRectangle(cornerRadius: 8, style: .continuous)
                                .fill(Color.white.opacity(0.13))
                        )
                }
                .buttonStyle(.plain)
                Button(action: onConfirm) {
                    Text(confirmTitle)
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.white)
                        .frame(width: 92, height: 32)
                        .background(
                            RoundedRectangle(cornerRadius: 8, style: .continuous)
                                .fill(Color.accentColor)
                        )
                }
                .buttonStyle(.plain)
            }
        }
        .padding(.horizontal, 24)
        .padding(.vertical, 20)
        .background(
            RoundedRectangle(cornerRadius: 18, style: .continuous)
                .fill(Color.black.opacity(0.92))
                .shadow(color: .black.opacity(0.34), radius: 24, y: 16)
                .shadow(color: .black.opacity(0.18), radius: 8, y: 3)
        )
        .onExitCommand(perform: onCancel)
    }
}
Apptag/Localization/ar-Najdi.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "لا تسحب التطبيقات لتصنيفات النظام الافتراضية",
  "drop.uncategorizedConfirmTitle": "نقل إلى غير مصنف؟",
  "drop.uncategorizedConfirmMessage": "نقل %appName% إلى غير مصنف بيزيل ارتباطاته الحالية بعدد %tagCount% من الوسوم: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "تصميم",
  "tag.development": "تطوير",
  "tag.writing": "كتابة",
Apptag/Localization/ar.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "لا تسحب التطبيقات إلى التصنيفات الافتراضية للنظام",
  "drop.uncategorizedConfirmTitle": "نقل إلى غير مصنف؟",
  "drop.uncategorizedConfirmMessage": "سيؤدي نقل %appName% إلى غير مصنف إلى إزالة ارتباطاته الحالية بعدد %tagCount% من الوسوم: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "تصميم",
  "tag.development": "تطوير",
  "tag.writing": "كتابة",
Apptag/Localization/cs.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Nepřetahujte aplikace do výchozích systémových kategorií",
  "drop.uncategorizedConfirmTitle": "Přesunout do Nezařazeno?",
  "drop.uncategorizedConfirmMessage": "Přesunutí %appName% do Nezařazeno odstraní aktuální přiřazení štítků (%tagCount%): %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Vývoj",
  "tag.writing": "Psaní",
Apptag/Localization/da.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Træk ikke apps til systemets standardkategorier",
  "drop.uncategorizedConfirmTitle": "Flyt til Ikke kategoriseret?",
  "drop.uncategorizedConfirmMessage": "Hvis %appName% flyttes til Ikke kategoriseret, fjernes de nuværende %tagCount% tagtilknytninger: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Udvikling",
  "tag.writing": "Skrivning",
Apptag/Localization/de.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Ziehe keine Apps in die Standard-Systemkategorien",
  "drop.uncategorizedConfirmTitle": "Nach Nicht kategorisiert verschieben?",
  "drop.uncategorizedConfirmMessage": "Wenn %appName% nach Nicht kategorisiert verschoben wird, werden die aktuellen %tagCount% Tag-Zuordnungen entfernt: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Entwicklung",
  "tag.writing": "Schreiben",
Apptag/Localization/en.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Do not drag apps into system default categories",
  "drop.uncategorizedConfirmTitle": "Move to Uncategorized?",
  "drop.uncategorizedConfirmMessage": "Moving %appName% to Uncategorized will remove its current %tagCount% tag associations: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Development",
  "tag.writing": "Writing",
Apptag/Localization/es.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "No arrastres a las categorías predeterminadas del sistema",
  "drop.uncategorizedConfirmTitle": "¿Mover a Sin clasificar?",
  "drop.uncategorizedConfirmMessage": "Mover %appName% a Sin clasificar eliminará sus %tagCount% asociaciones de etiquetas actuales: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Diseño",
  "tag.development": "Desarrollo",
  "tag.writing": "Escritura",
Apptag/Localization/fr.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Ne déposez pas dans les catégories système par défaut",
  "drop.uncategorizedConfirmTitle": "Déplacer vers Non classé ?",
  "drop.uncategorizedConfirmMessage": "Déplacer %appName% vers Non classé supprimera ses %tagCount% associations de tags actuelles : %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Développement",
  "tag.writing": "Écriture",
Apptag/Localization/id.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Jangan seret app ke kategori bawaan sistem",
  "drop.uncategorizedConfirmTitle": "Pindahkan ke Tanpa kategori?",
  "drop.uncategorizedConfirmMessage": "Memindahkan %appName% ke Tanpa kategori akan menghapus %tagCount% hubungan tag saat ini: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Desain",
  "tag.development": "Pengembangan",
  "tag.writing": "Menulis",
Apptag/Localization/it.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Non trascinare nelle categorie di sistema predefinite",
  "drop.uncategorizedConfirmTitle": "Spostare in Non classificato?",
  "drop.uncategorizedConfirmMessage": "Spostare %appName% in Non classificato rimuoverà le %tagCount% associazioni tag attuali: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Sviluppo",
  "tag.writing": "Scrittura",
Apptag/Localization/ja.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "システム既定カテゴリにはドラッグできません",
  "drop.uncategorizedConfirmTitle": "未分類に移動しますか?",
  "drop.uncategorizedConfirmMessage": "%appName% を未分類に移動すると、現在関連付けられている %tagCount% 個のタグが解除されます:%tagNames%。",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "デザイン",
  "tag.development": "プログラミング",
  "tag.writing": "ライティング",
Apptag/Localization/ko.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "시스템 기본 분류로 드래그하지 마세요",
  "drop.uncategorizedConfirmTitle": "분류 없음으로 이동할까요?",
  "drop.uncategorizedConfirmMessage": "%appName%을(를) 분류 없음으로 이동하면 현재 연결된 태그 %tagCount%개가 해제됩니다: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "디자인",
  "tag.development": "프로그래밍",
  "tag.writing": "글쓰기",
Apptag/Localization/ms.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Jangan seret app ke kategori bawaan sistem",
  "drop.uncategorizedConfirmTitle": "Alih ke Tiada kategori?",
  "drop.uncategorizedConfirmMessage": "Mengalih %appName% ke Tiada kategori akan membuang %tagCount% kaitan tag semasa: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Desain",
  "tag.development": "Pengembangan",
  "tag.writing": "Menulis",
Apptag/Localization/nb.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Ikke dra apper til systemets standardkategorier",
  "drop.uncategorizedConfirmTitle": "Flytt til Ukategorisert?",
  "drop.uncategorizedConfirmMessage": "Hvis %appName% flyttes til Ukategorisert, fjernes de nåværende %tagCount% taggtilknytningene: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Utvikling",
  "tag.writing": "Skriving",
Apptag/Localization/nl.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Sleep apps niet naar standaard systeemcategorieën",
  "drop.uncategorizedConfirmTitle": "Naar Niet gecategoriseerd verplaatsen?",
  "drop.uncategorizedConfirmMessage": "Als je %appName% naar Niet gecategoriseerd verplaatst, worden de huidige %tagCount% tagkoppelingen verwijderd: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Ontwerp",
  "tag.development": "Ontwikkeling",
  "tag.writing": "Schrijven",
Apptag/Localization/nn.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Ikke dra apper til systemets standardkategorier",
  "drop.uncategorizedConfirmTitle": "Flytt til Ukategorisert?",
  "drop.uncategorizedConfirmMessage": "Viss %appName% blir flytta til Ukategorisert, blir dei noverande %tagCount% taggkoplingane fjerna: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Utvikling",
  "tag.writing": "Skriving",
Apptag/Localization/no.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Ikke dra apper til systemets standardkategorier",
  "drop.uncategorizedConfirmTitle": "Flytt til Ukategorisert?",
  "drop.uncategorizedConfirmMessage": "Hvis %appName% flyttes til Ukategorisert, fjernes de nåværende %tagCount% taggtilknytningene: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Utvikling",
  "tag.writing": "Skriving",
Apptag/Localization/pl.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Nie przeciągaj aplikacji do domyślnych kategorii systemu",
  "drop.uncategorizedConfirmTitle": "Przenieść do Bez kategorii?",
  "drop.uncategorizedConfirmMessage": "Przeniesienie %appName% do Bez kategorii usunie obecne powiązania tagów (%tagCount%): %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Projektowanie",
  "tag.development": "Programowanie",
  "tag.writing": "Pisanie",
Apptag/Localization/pt-BR.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Não arraste apps para categorias padrão do sistema",
  "drop.uncategorizedConfirmTitle": "Mover para Sem categoria?",
  "drop.uncategorizedConfirmMessage": "Mover %appName% para Sem categoria removerá suas %tagCount% associações de tags atuais: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Desenvolvimento",
  "tag.writing": "Escrita",
Apptag/Localization/ro.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Nu trage aplicații în categoriile implicite ale sistemului",
  "drop.uncategorizedConfirmTitle": "Muți la Necategorizat?",
  "drop.uncategorizedConfirmMessage": "Mutarea %appName% la Necategorizat va elimina cele %tagCount% asocieri curente de etichete: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Dezvoltare",
  "tag.writing": "Scriere",
Apptag/Localization/ru.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Не перетаскивайте в системные категории по умолчанию",
  "drop.uncategorizedConfirmTitle": "Переместить в Без категории?",
  "drop.uncategorizedConfirmMessage": "Перемещение %appName% в Без категории удалит текущие связи с тегами (%tagCount%): %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Дизайн",
  "tag.development": "Разработка",
  "tag.writing": "Текст",
Apptag/Localization/sr-Cyrl.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Не превлачите апликације у подразумеване системске категорије",
  "drop.uncategorizedConfirmTitle": "Преместити у Некатегорисано?",
  "drop.uncategorizedConfirmMessage": "Премештање %appName% у Некатегорисано уклониће тренутне везе са ознакама (%tagCount%): %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Дизајн",
  "tag.development": "Развој",
  "tag.writing": "Писање",
Apptag/Localization/sv.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Dra inte appar till systemets standardkategorier",
  "drop.uncategorizedConfirmTitle": "Flytta till Okategoriserat?",
  "drop.uncategorizedConfirmMessage": "Om %appName% flyttas till Okategoriserat tas de nuvarande %tagCount% taggkopplingarna bort: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Design",
  "tag.development": "Utveckling",
  "tag.writing": "Skrivande",
Apptag/Localization/th.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "อย่าลากแอปไปยังหมวดหมู่เริ่มต้นของระบบ",
  "drop.uncategorizedConfirmTitle": "ย้ายไปยังไม่จัดหมวดหมู่หรือไม่?",
  "drop.uncategorizedConfirmMessage": "การย้าย %appName% ไปยังไม่จัดหมวดหมู่จะลบการเชื่อมโยงแท็กปัจจุบัน %tagCount% รายการ: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "ออกแบบ",
  "tag.development": "พัฒนา",
  "tag.writing": "เขียน",
Apptag/Localization/tr.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Uygulamaları sistem varsayılan kategorilerine sürüklemeyin",
  "drop.uncategorizedConfirmTitle": "Sınıflandırılmamışa taşınsın mı?",
  "drop.uncategorizedConfirmMessage": "%appName% Sınıflandırılmamışa taşınırsa mevcut %tagCount% etiket ilişkisi kaldırılır: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Tasarım",
  "tag.development": "Geliştirme",
  "tag.writing": "Yazma",
Apptag/Localization/uk.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Не перетягуйте застосунки в системні категорії за замовчуванням",
  "drop.uncategorizedConfirmTitle": "Перемістити до Без категорії?",
  "drop.uncategorizedConfirmMessage": "Переміщення %appName% до Без категорії видалить поточні зв’язки з тегами (%tagCount%): %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Дизайн",
  "tag.development": "Розробка",
  "tag.writing": "Письмо",
Apptag/Localization/vi.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "Không kéo ứng dụng vào danh mục mặc định của hệ thống",
  "drop.uncategorizedConfirmTitle": "Chuyển sang Chưa phân loại?",
  "drop.uncategorizedConfirmMessage": "Chuyển %appName% sang Chưa phân loại sẽ gỡ %tagCount% liên kết thẻ hiện tại: %tagNames%.",
  "drop.removeTagConfirmTitle": "Remove tag?",
  "drop.removeTagConfirmMessage": "Remove %appName% from the tag %tagName%?",
  "drop.removeTagDoNotAskAgain": "Got it, don't remind me again",
  "drop.removeTagConfirmYes": "Yes",
  "drop.removeTagConfirmNo": "No",
  "tag.design": "Thiết kế",
  "tag.development": "Phát triển",
  "tag.writing": "Viết",
Apptag/Localization/zh-Hans.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "请勿拖动至系统默认分类",
  "drop.uncategorizedConfirmTitle": "归类为未分类?",
  "drop.uncategorizedConfirmMessage": "将 %appName% 归类为未分类,会解除它当前关联的 %tagCount% 个标签:%tagNames%。",
  "drop.removeTagConfirmTitle": "要移除这个标签吗?",
  "drop.removeTagConfirmMessage": "要将 %appName% 移出“%tagName%”标签吗?",
  "drop.removeTagDoNotAskAgain": "知道了,以后不用再提醒我",
  "drop.removeTagConfirmYes": "是",
  "drop.removeTagConfirmNo": "否",
  "tag.design": "设计",
  "tag.development": "编程",
  "tag.writing": "写作",
Apptag/Localization/zh-Hant.json
@@ -63,6 +63,11 @@
  "drop.systemDefaultWarning": "請勿拖動至系統預設分類",
  "drop.uncategorizedConfirmTitle": "歸類為未分類?",
  "drop.uncategorizedConfirmMessage": "將 %appName% 歸類為未分類,會解除它目前關聯的 %tagCount% 個標籤:%tagNames%。",
  "drop.removeTagConfirmTitle": "要移除這個標籤嗎?",
  "drop.removeTagConfirmMessage": "要將 %appName% 移出「%tagName%」標籤嗎?",
  "drop.removeTagDoNotAskAgain": "知道了,以後不用再提醒我",
  "drop.removeTagConfirmYes": "是",
  "drop.removeTagConfirmNo": "否",
  "tag.design": "設計",
  "tag.development": "程式設計",
  "tag.writing": "寫作",