Ariver
2026-05-28 efbfc27e0c813c31b570d72fded40eb91d07f2db
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
@@ -153,7 +158,6 @@
                iconSize: iconSize,
                showNames: showNames,
                showUncommonAppBubbles: showUncommonAppBubbles,
                highlightedGroupName: highlightedGroupName,
                contentRevision: contentRevision
            )
            if nextSignature != contentSignature {
@@ -230,7 +234,6 @@
            iconSize: CGFloat,
            showNames: Bool,
            showUncommonAppBubbles: Bool,
            highlightedGroupName: String?,
            contentRevision: Int
        ) -> String {
            let colorPart = tagColors
@@ -242,7 +245,6 @@
                "\(Int(iconSize.rounded()))",
                showNames ? "names" : "nonames",
                showUncommonAppBubbles ? "uncommon" : "allbubbles",
                highlightedGroupName ?? "",
                colorPart,
                "rev=\(contentRevision)"
            ].joined(separator: "|")
@@ -250,7 +252,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 +280,7 @@
            NotificationCenter.default.removeObserver(scrollObserver)
        }
        scrollUnfreezeWorkItem?.cancel()
        AppDragCoordinator.shared.unregisterEmptyDropTarget(id: emptyDropTargetID)
    }
    func configure(coordinator: AppGridCollectionView.Coordinator) {
@@ -316,6 +320,24 @@
        if lastLayoutSize != bounds.size {
            lastLayoutSize = bounds.size
            gridLayout.invalidateLayout()
        }
    }
    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 }
        DispatchQueue.main.async {
            coordinator.onDropOutsideGroup(path, source, copy)
        }
    }
@@ -926,8 +948,9 @@
        guard let coordinator, let group else { return }
        let displayStyle = coordinator.displayStyle
        let tagColor = TagColor.nsColor(for: coordinator.tagColors[group.name] ?? 0)
        let isNavigationHighlighted = coordinator.highlightedGroupName == group.name
        let isColorlessActive = coordinator.isColorlessContainerMode
            && (isHovered || coordinator.highlightedGroupName == group.name)
            && (isHovered || isNavigationHighlighted)
        if displayStyle.usesCardSurface {
            let rect = bounds.insetBy(dx: 0.5, dy: 0.5)
@@ -1008,10 +1031,11 @@
    private func updateCardShadow() {
        guard let coordinator else { return }
        let isNavigationHighlighted = coordinator.highlightedGroupName == group?.name
        let isColorlessActive = coordinator.isColorlessContainerMode
            && (isHovered || coordinator.highlightedGroupName == group?.name)
            && (isHovered || isNavigationHighlighted)
        let shouldShadow = coordinator.displayStyle.usesCardSurface
            && ((coordinator.isColoredContainerMode && isHovered) || isColorlessActive)
            && ((coordinator.isColoredContainerMode && (isHovered || isNavigationHighlighted)) || isColorlessActive)
        layer?.shadowColor = NSColor.black.cgColor
        layer?.shadowOpacity = shouldShadow ? 0.22 : 0
        layer?.shadowRadius = shouldShadow ? 8 : 0