Переглянути джерело

Fix CV Maker template gallery so only one card appears selected.

Clear stale hover borders when the user picks a different template or scrolls the gallery, since hover and selection both used a similar blue outline.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 місяць тому
батько
коміт
49b820eb4b
1 змінених файлів з 41 додано та 6 видалено
  1. 41 6
      App for Indeed/Views/CVMakerPageView.swift

+ 41 - 6
App for Indeed/Views/CVMakerPageView.swift

@@ -592,6 +592,7 @@ final class CVMakerPageView: NSView {
 
     private var appearanceObserver: NSObjectProtocol?
     private var languageObserver: NSObjectProtocol?
+    private var scrollBoundsObserver: NSObjectProtocol?
     /// AI catalog keyed by `AppLanguage.localeIdentifier` so switching language can restore without a network round-trip.
     private var aiCatalogByLocale: [String: [CVTemplate]] = [:]
     /// Ignores stale OpenAI responses after a newer fetch or language change started.
@@ -686,6 +687,9 @@ final class CVMakerPageView: NSView {
         if let languageObserver {
             NotificationCenter.default.removeObserver(languageObserver)
         }
+        if let scrollBoundsObserver {
+            NotificationCenter.default.removeObserver(scrollBoundsObserver)
+        }
     }
 
     @available(*, unavailable)
@@ -828,6 +832,14 @@ final class CVMakerPageView: NSView {
         scrollView.drawsBackground = false
         scrollView.borderType = .noBorder
         scrollView.documentView = gridDocument
+        scrollView.contentView.postsBoundsChangedNotifications = true
+        scrollBoundsObserver = NotificationCenter.default.addObserver(
+            forName: NSView.boundsDidChangeNotification,
+            object: scrollView.contentView,
+            queue: .main
+        ) { [weak self] _ in
+            self?.resetTemplateCardHoverStates()
+        }
         NSLayoutConstraint.activate([
             gridDocument.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor),
             gridDocument.leadingAnchor.constraint(equalTo: scrollView.contentView.leadingAnchor),
@@ -980,9 +992,10 @@ final class CVMakerPageView: NSView {
                     let template = templates[position]
                     let card = CVTemplateCard(template: template, palette: palette())
                     card.translatesAutoresizingMaskIntoConstraints = false
+                    let selectionToken = card.selectionToken
+                    let templateID = card.templateID
                     card.onSelect = { [weak self] in
-                        guard let self else { return }
-                        self.didSelectCard(card)
+                        self?.didSelectCard(selectionToken: selectionToken, templateID: templateID)
                     }
                     row.addArrangedSubview(card)
                     templateCardsInGrid.append(card)
@@ -1036,7 +1049,18 @@ final class CVMakerPageView: NSView {
     private func applySelectionToCards() {
         let token = selectedTemplateCardToken
         for card in templateCardsInGrid {
-            card.isSelected = (card.selectionToken == token)
+            let shouldSelect = (card.selectionToken == token)
+            card.isSelected = shouldSelect
+            if !shouldSelect {
+                card.clearHoverVisualState()
+            }
+        }
+    }
+
+    /// Scroll moves cards under a stationary cursor without `mouseExited`; clear stale hovers so only the selected card keeps a brand border.
+    private func resetTemplateCardHoverStates() {
+        for card in templateCardsInGrid where !card.isSelected {
+            card.clearHoverVisualState()
         }
     }
 
@@ -1078,9 +1102,9 @@ final class CVMakerPageView: NSView {
         updateSelectedChipStates()
     }
 
-    private func didSelectCard(_ card: CVTemplateCard) {
-        selectedTemplateCardToken = card.selectionToken
-        selectedTemplateID = card.templateID
+    private func didSelectCard(selectionToken: UUID, templateID: String) {
+        selectedTemplateCardToken = selectionToken
+        selectedTemplateID = templateID
         applySelectionToCards()
     }
 
@@ -1602,6 +1626,17 @@ private final class CVTemplateCard: NSView {
         }
     }
 
+    /// Clears hover chrome without waiting for `mouseExited` (e.g. after scroll or when another card is selected).
+    func clearHoverVisualState() {
+        if didPushCursor {
+            NSCursor.pop()
+            didPushCursor = false
+        }
+        guard isHovering else { return }
+        isHovering = false
+        applyChrome()
+    }
+
     private func applyChrome() {
         // Same border + shadow metrics in every state — only color changes on select
         // so thumbnails keep identical insets (stroke is drawn inside the layer).