Confirm single-tag removal on empty grid drop
| | |
| | | "showDockIcon": showDockIcon, |
| | | "launchAtLogin": launchAtLogin, |
| | | "showUncommonAppBubbles": showUncommonAppBubbles, |
| | | "skipTagRemovalDropConfirm": false, |
| | | LauncherHotkeyRegistrationStore.mainStateKey: LauncherHotkeyRegistrationState.active.rawValue, |
| | | LauncherHotkeyRegistrationStore.quickSearchStateKey: LauncherHotkeyRegistrationState.active.rawValue |
| | | ]) |
| | |
| | | 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? |
| | |
| | | |
| | | 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?) { |
| | |
| | | .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() { |
| | |
| | | |
| | | 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? { |
| | |
| | | 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 } |
| | |
| | | } |
| | | } |
| | | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | onBubbleHover: onBubbleHover, |
| | | onEditNote: onEditNote, |
| | | onDropApp: onDropApp, |
| | | onDropOutsideGroup: onDropOutsideGroup, |
| | | onGroupActivate: onGroupActivate, |
| | | onScrollActivity: onScrollActivity, |
| | | onDragModeChange: onDragModeChange |
| | |
| | | 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 } |
| | |
| | | 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 |
| | |
| | | self.onBubbleHover = onBubbleHover |
| | | self.onEditNote = onEditNote |
| | | self.onDropApp = onDropApp |
| | | self.onDropOutsideGroup = onDropOutsideGroup |
| | | self.onGroupActivate = onGroupActivate |
| | | self.onScrollActivity = onScrollActivity |
| | | self.onDragModeChange = onDragModeChange |
| | |
| | | } |
| | | } |
| | | |
| | | 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() |
| | |
| | | NotificationCenter.default.removeObserver(scrollObserver) |
| | | } |
| | | scrollUnfreezeWorkItem?.cancel() |
| | | AppDragCoordinator.shared.unregisterEmptyDropTarget(id: emptyDropTargetID) |
| | | } |
| | | |
| | | func configure(coordinator: AppGridCollectionView.Coordinator) { |
| | |
| | | } |
| | | } |
| | | |
| | | 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 |
| | |
| | | let removableTags: [String] |
| | | } |
| | | |
| | | private struct PendingTagRemovalDrop: Identifiable { |
| | | let id = UUID() |
| | | let app: AppInfo |
| | | let tagName: String |
| | | } |
| | | |
| | | private enum SmartStartNoticeMode { |
| | | case autoApplied |
| | | case suggestionOnly |
| | |
| | | @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 |
| | |
| | | @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 |
| | | |
| | |
| | | smartStartNoticeOverlay |
| | | editActionFeedbackOverlay |
| | | uncategorizedDropConfirmOverlay |
| | | tagRemovalDropConfirmOverlay |
| | | } |
| | | |
| | | quickSearchOverlay |
| | |
| | | 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) |
| | |
| | | } |
| | | .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 { |
| | |
| | | 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) |
| | |
| | | ) |
| | | } |
| | | |
| | | 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)) { |
| | |
| | | } |
| | | 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) |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private func resetTransientDragState(keepingPendingUncategorizedDrop: Bool = false) { |
| | | private func resetTransientDragState( |
| | | keepingPendingUncategorizedDrop: Bool = false, |
| | | keepingPendingTagRemovalDrop: Bool = false |
| | | ) { |
| | | let hadAppDragState = appDragModeActive |
| | | AppDragCoordinator.shared.cancelDrag() |
| | | if appDragModeActive { |
| | |
| | | if !keepingPendingUncategorizedDrop, pendingUncategorizedDrop != nil { |
| | | pendingUncategorizedDrop = nil |
| | | } |
| | | if !keepingPendingTagRemovalDrop, pendingTagRemovalDrop != nil { |
| | | pendingTagRemovalDrop = nil |
| | | } |
| | | } |
| | | |
| | | func refreshApps(forceLayoutRefresh: Bool = false) { |
| | |
| | | .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) |
| | | } |
| | | } |
| | |
| | | "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": "كتابة", |
| | |
| | | "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": "كتابة", |
| | |
| | | "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í", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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": "ライティング", |
| | |
| | | "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": "글쓰기", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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", |
| | |
| | | "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": "Текст", |
| | |
| | | "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": "Писање", |
| | |
| | | "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", |
| | |
| | | "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": "เขียน", |
| | |
| | | "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", |
| | |
| | | "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": "Письмо", |
| | |
| | | "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", |
| | |
| | | "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": "写作", |
| | |
| | | "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": "寫作", |