浏览代码

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 月之前
父节点
当前提交
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()