Sfoglia il codice sorgente

Apply dark theme to CV Maker template gallery.

Wire the templates page, filter chips, and cards to AppDashboardTheme so they match the dashboard when appearance changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 2 mesi fa
parent
commit
96683d5143

+ 54 - 0
App for Indeed/Services/AppDashboardTheme.swift

@@ -233,4 +233,58 @@ enum AppDashboardTheme {
             ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
             : NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
     }
+
+    // MARK: CV Maker gallery
+
+    static var cvMakerPageGradientTop: NSColor {
+        isDark ? pageBackground : NSColor(srgbRed: 250 / 255, green: 252 / 255, blue: 1, alpha: 1)
+    }
+
+    static var cvMakerPageGradientBottom: NSColor {
+        isDark ? pageBackground : NSColor(srgbRed: 236 / 255, green: 244 / 255, blue: 1, alpha: 1)
+    }
+
+    static var cvMakerChipRestFill: NSColor { cardBackground }
+
+    static var cvMakerChipRestBorder: NSColor { border }
+
+    static var cvMakerChipHoverFill: NSColor { neutralHoverFill }
+
+    static var cvMakerChipBadgeBackground: NSColor {
+        isDark
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
+            : NSColor(srgbRed: 233 / 255, green: 236 / 255, blue: 241 / 255, alpha: 1)
+    }
+
+    static var cvMakerChipBadgeText: NSColor { secondaryText }
+
+    static var cvMakerCardFooter: NSColor {
+        isDark
+            ? NSColor(srgbRed: 40 / 255, green: 40 / 255, blue: 42 / 255, alpha: 1)
+            : NSColor(srgbRed: 250 / 255, green: 251 / 255, blue: 253 / 255, alpha: 1)
+    }
+
+    static var cvMakerPreviewSurface: NSColor {
+        isDark
+            ? NSColor(srgbRed: 52 / 255, green: 52 / 255, blue: 54 / 255, alpha: 1)
+            : NSColor(srgbRed: 252 / 255, green: 252 / 255, blue: 252 / 255, alpha: 1)
+    }
+
+    static var cvMakerPreviewSidebarTint: NSColor {
+        isDark
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
+            : NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
+    }
+
+    static var cvMakerCardBorderHover: NSColor { searchBarBorderHover }
+
+    static var cvMakerSelectionGlow: NSColor {
+        NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: isDark ? 0.65 : 0.55)
+    }
+
+    static var cvMakerFilterChromeBorder: NSColor {
+        isDark
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
+            : NSColor.white.withAlphaComponent(0.65)
+    }
 }

+ 101 - 65
App for Indeed/Views/CVMakerPageView.swift

@@ -2,9 +2,9 @@
 //  CVMakerPageView.swift
 //  App for Indeed
 //
-//  Template gallery for the CV Maker sidebar destination. Light-theme rendering
-//  inspired by a dark reference UI: page header, category toggle, style chips,
-//  4-column thumbnail grid and a sticky bottom CTA.
+//  Template gallery for the CV Maker sidebar destination: page header, category
+//  toggle, style chips, thumbnail grid, and a sticky bottom CTA. Follows the
+//  active dashboard light / dark appearance via `AppDashboardTheme`.
 //
 
 import Cocoa
@@ -554,41 +554,38 @@ enum CVTemplateCatalog {
 /// bottom CTA. Hosts inside the same `nonHomeHost` slot as Saved Jobs/Settings.
 final class CVMakerPageView: NSView {
 
-    /// Light-theme palette aligned with the rest of the dashboard (brand blue + neutral grays on white).
     private enum Palette {
-        static let pageBackground = NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
-        static let mutedSurface = NSColor(srgbRed: 247 / 255, green: 247 / 255, blue: 247 / 255, alpha: 1)
-        static let chipRestFill = NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
-        static let chipBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
-        static let chipHoverFill = NSColor(srgbRed: 236 / 255, green: 240 / 255, blue: 246 / 255, alpha: 1)
-        static let chipBadgeBackground = NSColor(srgbRed: 233 / 255, green: 236 / 255, blue: 241 / 255, alpha: 1)
-        static let chipBadgeText = NSColor(srgbRed: 90 / 255, green: 102 / 255, blue: 121 / 255, alpha: 1)
-        static let activeChipBackground = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
-        static let activeChipHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
-        static let activeChipText = NSColor.white
-        static let activeChipBadgeBackground = NSColor.white.withAlphaComponent(0.22)
-        static let activeChipBadgeText = NSColor.white
-        static let primaryText = NSColor(srgbRed: 31 / 255, green: 41 / 255, blue: 55 / 255, alpha: 1)
-        static let secondaryText = NSColor(srgbRed: 100 / 255, green: 116 / 255, blue: 139 / 255, alpha: 1)
-        static let cardBorder = NSColor(srgbRed: 216 / 255, green: 223 / 255, blue: 233 / 255, alpha: 1)
-        static let cardBorderHover = NSColor(srgbRed: 178 / 255, green: 196 / 255, blue: 225 / 255, alpha: 1)
-        static let cardBorderSelected = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
-        static let cardFooter = NSColor(srgbRed: 250 / 255, green: 251 / 255, blue: 253 / 255, alpha: 1)
-        static let previewSurface = NSColor(srgbRed: 252 / 255, green: 252 / 255, blue: 252 / 255, alpha: 1)
-        static let previewPaper = NSColor.white
-        static let previewSidebarTint = NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
-        static let previewInk = NSColor(srgbRed: 38 / 255, green: 50 / 255, blue: 71 / 255, alpha: 1)
-        static let previewMuted = NSColor(srgbRed: 165 / 255, green: 175 / 255, blue: 192 / 255, alpha: 1)
-        static let previewAccentRed = NSColor(srgbRed: 207 / 255, green: 67 / 255, blue: 50 / 255, alpha: 1)
-        static let previewAccentBlue = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
-        static let ctaBackground = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
-        static let ctaHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
-        static let ctaText = NSColor.white
-        static let selectionGlow = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 0.55)
-        static let gradientTop = NSColor(srgbRed: 250 / 255, green: 252 / 255, blue: 1, alpha: 1)
-        static let gradientBottom = NSColor(srgbRed: 236 / 255, green: 244 / 255, blue: 1, alpha: 1)
+        static var primaryText: NSColor { AppDashboardTheme.primaryText }
+        static var secondaryText: NSColor { AppDashboardTheme.secondaryText }
+        static var cardBackground: NSColor { AppDashboardTheme.cardBackground }
+        static var cardBorder: NSColor { AppDashboardTheme.border }
+        static var cardBorderHover: NSColor { AppDashboardTheme.cvMakerCardBorderHover }
+        static var cardBorderSelected: NSColor { AppDashboardTheme.brandBlue }
+        static var cardFooter: NSColor { AppDashboardTheme.cvMakerCardFooter }
+        static var previewSurface: NSColor { AppDashboardTheme.cvMakerPreviewSurface }
+        static var previewPaper: NSColor { NSColor.white }
+        static var previewSidebarTint: NSColor { AppDashboardTheme.cvMakerPreviewSidebarTint }
+        static var previewInk: NSColor {
+            NSColor(srgbRed: 38 / 255, green: 50 / 255, blue: 71 / 255, alpha: 1)
+        }
+        static var previewMuted: NSColor {
+            NSColor(srgbRed: 165 / 255, green: 175 / 255, blue: 192 / 255, alpha: 1)
+        }
+        static var previewAccentRed: NSColor {
+            NSColor(srgbRed: 207 / 255, green: 67 / 255, blue: 50 / 255, alpha: 1)
+        }
+        static var previewAccentBlue: NSColor { AppDashboardTheme.brandBlue }
+        static var ctaBackground: NSColor { AppDashboardTheme.brandBlue }
+        static var ctaHover: NSColor { AppDashboardTheme.brandBlueHover }
+        static var ctaText: NSColor { AppDashboardTheme.proCTAText }
+        static var selectionGlow: NSColor { AppDashboardTheme.cvMakerSelectionGlow }
+        static var gradientTop: NSColor { AppDashboardTheme.cvMakerPageGradientTop }
+        static var gradientBottom: NSColor { AppDashboardTheme.cvMakerPageGradientBottom }
+        static var filterChromeBorder: NSColor { AppDashboardTheme.cvMakerFilterChromeBorder }
     }
 
+    private var appearanceObserver: NSObjectProtocol?
+
     private let pageGradientLayer = CAGradientLayer()
     private let filterChrome = NSVisualEffectView()
     private let filterStack = NSStackView()
@@ -650,6 +647,20 @@ final class CVMakerPageView: NSView {
         reloadTemplateGrid()
         updateSelectedChipStates()
         beginLoadingAICatalogIfPossible()
+        appearanceObserver = NotificationCenter.default.addObserver(
+            forName: AppAppearanceManager.didChangeNotification,
+            object: nil,
+            queue: .main
+        ) { [weak self] _ in
+            self?.applyCurrentAppearance()
+        }
+        applyCurrentAppearance()
+    }
+
+    deinit {
+        if let appearanceObserver {
+            NotificationCenter.default.removeObserver(appearanceObserver)
+        }
     }
 
     @available(*, unavailable)
@@ -657,32 +668,47 @@ final class CVMakerPageView: NSView {
         fatalError("init(coder:) has not been implemented")
     }
 
+    override func viewDidChangeEffectiveAppearance() {
+        super.viewDidChangeEffectiveAppearance()
+        applyCurrentAppearance()
+    }
+
     override func layout() {
         super.layout()
         pageGradientLayer.frame = bounds
         layoutGridCardsIfNeeded()
     }
 
+    func applyCurrentAppearance() {
+        pageGradientLayer.colors = [Palette.gradientBottom.cgColor, Palette.gradientTop.cgColor]
+        filterChrome.layer?.borderColor = Palette.filterChromeBorder.cgColor
+        titleLabel.textColor = Palette.primaryText
+        subtitleLabel.textColor = Palette.secondaryText
+        styleCTAButton(ctaButton)
+        for chip in groupTabButtons.values { chip.applyCurrentAppearance() }
+        for chip in familyChipButtons.values { chip.applyCurrentAppearance() }
+        reloadTemplateGrid()
+        updateSelectedChipStates()
+    }
+
     // MARK: Setup
 
     private func configureLayout() {
         wantsLayer = true
         layer?.backgroundColor = NSColor.clear.cgColor
 
-        pageGradientLayer.colors = [Palette.gradientBottom.cgColor, Palette.gradientTop.cgColor]
         pageGradientLayer.locations = [0, 1] as [NSNumber]
         pageGradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
         pageGradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
         layer?.insertSublayer(pageGradientLayer, at: 0)
 
         filterChrome.translatesAutoresizingMaskIntoConstraints = false
-        filterChrome.material = .sidebar
+        filterChrome.material = .contentBackground
         filterChrome.blendingMode = .withinWindow
         filterChrome.state = .active
         filterChrome.wantsLayer = true
         filterChrome.layer?.cornerRadius = 18
         filterChrome.layer?.borderWidth = 1
-        filterChrome.layer?.borderColor = NSColor.white.withAlphaComponent(0.65).cgColor
 
         filterStack.orientation = .vertical
         filterStack.spacing = 12
@@ -975,6 +1001,7 @@ final class CVMakerPageView: NSView {
             borderHover: Palette.cardBorderHover,
             borderSelected: Palette.cardBorderSelected,
             selectionGlow: Palette.selectionGlow,
+            cardShellBackground: Palette.cardBackground,
             footerBackground: Palette.cardFooter,
             previewSurface: Palette.previewSurface,
             previewPaper: Palette.previewPaper,
@@ -1121,18 +1148,22 @@ private final class CVChipButton: NSView {
     private var trackingArea: NSTrackingArea?
 
     private enum Palette {
-        static let restFill = NSColor.white
-        static let restBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
-        static let hoverFill = NSColor(srgbRed: 248 / 255, green: 250 / 255, blue: 252 / 255, alpha: 1)
-        static let activeFill = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
-        static let activeFillHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
-        static let activeBorder = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
-        static let restText = NSColor(srgbRed: 71 / 255, green: 85 / 255, blue: 105 / 255, alpha: 1)
-        static let activeText = NSColor.white
-        static let restBadge = NSColor(srgbRed: 230 / 255, green: 234 / 255, blue: 240 / 255, alpha: 1)
-        static let restBadgeText = NSColor(srgbRed: 100 / 255, green: 116 / 255, blue: 139 / 255, alpha: 1)
-        static let activeBadge = NSColor.white.withAlphaComponent(0.22)
-        static let activeBadgeText = NSColor.white
+        static var restFill: NSColor { AppDashboardTheme.cvMakerChipRestFill }
+        static var restBorder: NSColor { AppDashboardTheme.cvMakerChipRestBorder }
+        static var hoverFill: NSColor { AppDashboardTheme.cvMakerChipHoverFill }
+        static var activeFill: NSColor { AppDashboardTheme.brandBlue }
+        static var activeFillHover: NSColor { AppDashboardTheme.brandBlueHover }
+        static var activeBorder: NSColor { AppDashboardTheme.brandBlueHover }
+        static var restText: NSColor { AppDashboardTheme.primaryText }
+        static var activeText: NSColor { AppDashboardTheme.proCTAText }
+        static var restBadge: NSColor { AppDashboardTheme.cvMakerChipBadgeBackground }
+        static var restBadgeText: NSColor { AppDashboardTheme.cvMakerChipBadgeText }
+        static var activeBadge: NSColor { NSColor.white.withAlphaComponent(0.22) }
+        static var activeBadgeText: NSColor { AppDashboardTheme.proCTAText }
+    }
+
+    func applyCurrentAppearance() {
+        applyState()
     }
 
     private static let symbolSide: CGFloat = 18
@@ -1331,6 +1362,7 @@ private final class CVTemplateCard: NSView {
     private let template: CVTemplate
     private let palette: CVTemplateCardPalette
     private let previewSurface = NSView()
+    private let footerView = NSView()
     private let preview: CVTemplatePreviewView
     private let nameLabel = NSTextField(labelWithString: "")
     private let categoryLabel = NSTextField(labelWithString: "")
@@ -1346,7 +1378,7 @@ private final class CVTemplateCard: NSView {
         wantsLayer = true
         layer?.masksToBounds = false
         layer?.cornerRadius = 24
-        layer?.backgroundColor = NSColor.white.cgColor
+        layer?.backgroundColor = palette.cardShellBackground.cgColor
         translatesAutoresizingMaskIntoConstraints = false
         heightAnchor.constraint(equalToConstant: Self.layoutHeight).isActive = true
 
@@ -1379,20 +1411,19 @@ private final class CVTemplateCard: NSView {
         footerStack.alignment = .leading
         footerStack.translatesAutoresizingMaskIntoConstraints = false
 
-        let footer = NSView()
-        footer.translatesAutoresizingMaskIntoConstraints = false
-        footer.wantsLayer = true
-        footer.layer?.backgroundColor = palette.footerBackground.cgColor
-        footer.addSubview(footerStack)
+        footerView.translatesAutoresizingMaskIntoConstraints = false
+        footerView.wantsLayer = true
+        footerView.layer?.backgroundColor = palette.footerBackground.cgColor
+        footerView.addSubview(footerStack)
         NSLayoutConstraint.activate([
-            footerStack.leadingAnchor.constraint(equalTo: footer.leadingAnchor, constant: 16),
-            footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footer.trailingAnchor, constant: -16),
-            footerStack.topAnchor.constraint(equalTo: footer.topAnchor, constant: 13),
-            footerStack.bottomAnchor.constraint(equalTo: footer.bottomAnchor, constant: -13)
+            footerStack.leadingAnchor.constraint(equalTo: footerView.leadingAnchor, constant: 16),
+            footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footerView.trailingAnchor, constant: -16),
+            footerStack.topAnchor.constraint(equalTo: footerView.topAnchor, constant: 13),
+            footerStack.bottomAnchor.constraint(equalTo: footerView.bottomAnchor, constant: -13)
         ])
 
         addSubview(previewSurface)
-        addSubview(footer)
+        addSubview(footerView)
 
         NSLayoutConstraint.activate([
             previewSurface.topAnchor.constraint(equalTo: topAnchor),
@@ -1405,10 +1436,10 @@ private final class CVTemplateCard: NSView {
             preview.trailingAnchor.constraint(equalTo: previewSurface.trailingAnchor, constant: -16),
             preview.bottomAnchor.constraint(equalTo: previewSurface.bottomAnchor, constant: -14),
 
-            footer.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
-            footer.leadingAnchor.constraint(equalTo: leadingAnchor),
-            footer.trailingAnchor.constraint(equalTo: trailingAnchor),
-            footer.bottomAnchor.constraint(equalTo: bottomAnchor)
+            footerView.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
+            footerView.leadingAnchor.constraint(equalTo: leadingAnchor),
+            footerView.trailingAnchor.constraint(equalTo: trailingAnchor),
+            footerView.bottomAnchor.constraint(equalTo: bottomAnchor)
         ])
         applyChrome()
     }
@@ -1499,6 +1530,11 @@ private final class CVTemplateCard: NSView {
         } else {
             borderColor = palette.border
         }
+        layer?.backgroundColor = palette.cardShellBackground.cgColor
+        previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
+        footerView.layer?.backgroundColor = palette.footerBackground.cgColor
+        nameLabel.textColor = palette.primaryText
+        categoryLabel.textColor = palette.secondaryText
         layer?.borderColor = borderColor.cgColor
         layer?.borderWidth = uniformBorder
         layer?.shadowColor = NSColor.black.cgColor

+ 1 - 0
App for Indeed/Views/CVTemplateMiniPreview.swift

@@ -15,6 +15,7 @@ struct CVTemplateCardPalette {
     let borderHover: NSColor
     let borderSelected: NSColor
     let selectionGlow: NSColor
+    let cardShellBackground: NSColor
     let footerBackground: NSColor
     let previewSurface: NSColor
     let previewPaper: NSColor

+ 1 - 0
App for Indeed/Views/DashboardView.swift

@@ -278,6 +278,7 @@ final class DashboardView: NSView, NSTextFieldDelegate, NSSharingServicePickerDe
         sendLabel.textColor = Theme.proCTAText
 
         appearanceModeSegment?.selectedSegment = AppAppearanceManager.shared.mode.segmentIndex
+        cvMakerPageView.applyCurrentAppearance()
         refreshSettingsPageAppearance(in: settingsPageContainer)
         rebuildFeatureShortcutCards()
         configureSidebar()