Pārlūkot izejas kodu

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 nedēļas atpakaļ
vecāks
revīzija
96683d5143

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

@@ -233,4 +233,58 @@ enum AppDashboardTheme {
233 233
             ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
234 234
             : NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
235 235
     }
236
+
237
+    // MARK: CV Maker gallery
238
+
239
+    static var cvMakerPageGradientTop: NSColor {
240
+        isDark ? pageBackground : NSColor(srgbRed: 250 / 255, green: 252 / 255, blue: 1, alpha: 1)
241
+    }
242
+
243
+    static var cvMakerPageGradientBottom: NSColor {
244
+        isDark ? pageBackground : NSColor(srgbRed: 236 / 255, green: 244 / 255, blue: 1, alpha: 1)
245
+    }
246
+
247
+    static var cvMakerChipRestFill: NSColor { cardBackground }
248
+
249
+    static var cvMakerChipRestBorder: NSColor { border }
250
+
251
+    static var cvMakerChipHoverFill: NSColor { neutralHoverFill }
252
+
253
+    static var cvMakerChipBadgeBackground: NSColor {
254
+        isDark
255
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
256
+            : NSColor(srgbRed: 233 / 255, green: 236 / 255, blue: 241 / 255, alpha: 1)
257
+    }
258
+
259
+    static var cvMakerChipBadgeText: NSColor { secondaryText }
260
+
261
+    static var cvMakerCardFooter: NSColor {
262
+        isDark
263
+            ? NSColor(srgbRed: 40 / 255, green: 40 / 255, blue: 42 / 255, alpha: 1)
264
+            : NSColor(srgbRed: 250 / 255, green: 251 / 255, blue: 253 / 255, alpha: 1)
265
+    }
266
+
267
+    static var cvMakerPreviewSurface: NSColor {
268
+        isDark
269
+            ? NSColor(srgbRed: 52 / 255, green: 52 / 255, blue: 54 / 255, alpha: 1)
270
+            : NSColor(srgbRed: 252 / 255, green: 252 / 255, blue: 252 / 255, alpha: 1)
271
+    }
272
+
273
+    static var cvMakerPreviewSidebarTint: NSColor {
274
+        isDark
275
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
276
+            : NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
277
+    }
278
+
279
+    static var cvMakerCardBorderHover: NSColor { searchBarBorderHover }
280
+
281
+    static var cvMakerSelectionGlow: NSColor {
282
+        NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: isDark ? 0.65 : 0.55)
283
+    }
284
+
285
+    static var cvMakerFilterChromeBorder: NSColor {
286
+        isDark
287
+            ? NSColor(srgbRed: 58 / 255, green: 58 / 255, blue: 60 / 255, alpha: 1)
288
+            : NSColor.white.withAlphaComponent(0.65)
289
+    }
236 290
 }

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

@@ -2,9 +2,9 @@
2 2
 //  CVMakerPageView.swift
3 3
 //  App for Indeed
4 4
 //
5
-//  Template gallery for the CV Maker sidebar destination. Light-theme rendering
6
-//  inspired by a dark reference UI: page header, category toggle, style chips,
7
-//  4-column thumbnail grid and a sticky bottom CTA.
5
+//  Template gallery for the CV Maker sidebar destination: page header, category
6
+//  toggle, style chips, thumbnail grid, and a sticky bottom CTA. Follows the
7
+//  active dashboard light / dark appearance via `AppDashboardTheme`.
8 8
 //
9 9
 
10 10
 import Cocoa
@@ -554,41 +554,38 @@ enum CVTemplateCatalog {
554 554
 /// bottom CTA. Hosts inside the same `nonHomeHost` slot as Saved Jobs/Settings.
555 555
 final class CVMakerPageView: NSView {
556 556
 
557
-    /// Light-theme palette aligned with the rest of the dashboard (brand blue + neutral grays on white).
558 557
     private enum Palette {
559
-        static let pageBackground = NSColor(srgbRed: 1, green: 1, blue: 1, alpha: 1)
560
-        static let mutedSurface = NSColor(srgbRed: 247 / 255, green: 247 / 255, blue: 247 / 255, alpha: 1)
561
-        static let chipRestFill = NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
562
-        static let chipBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
563
-        static let chipHoverFill = NSColor(srgbRed: 236 / 255, green: 240 / 255, blue: 246 / 255, alpha: 1)
564
-        static let chipBadgeBackground = NSColor(srgbRed: 233 / 255, green: 236 / 255, blue: 241 / 255, alpha: 1)
565
-        static let chipBadgeText = NSColor(srgbRed: 90 / 255, green: 102 / 255, blue: 121 / 255, alpha: 1)
566
-        static let activeChipBackground = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
567
-        static let activeChipHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
568
-        static let activeChipText = NSColor.white
569
-        static let activeChipBadgeBackground = NSColor.white.withAlphaComponent(0.22)
570
-        static let activeChipBadgeText = NSColor.white
571
-        static let primaryText = NSColor(srgbRed: 31 / 255, green: 41 / 255, blue: 55 / 255, alpha: 1)
572
-        static let secondaryText = NSColor(srgbRed: 100 / 255, green: 116 / 255, blue: 139 / 255, alpha: 1)
573
-        static let cardBorder = NSColor(srgbRed: 216 / 255, green: 223 / 255, blue: 233 / 255, alpha: 1)
574
-        static let cardBorderHover = NSColor(srgbRed: 178 / 255, green: 196 / 255, blue: 225 / 255, alpha: 1)
575
-        static let cardBorderSelected = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
576
-        static let cardFooter = NSColor(srgbRed: 250 / 255, green: 251 / 255, blue: 253 / 255, alpha: 1)
577
-        static let previewSurface = NSColor(srgbRed: 252 / 255, green: 252 / 255, blue: 252 / 255, alpha: 1)
578
-        static let previewPaper = NSColor.white
579
-        static let previewSidebarTint = NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1)
580
-        static let previewInk = NSColor(srgbRed: 38 / 255, green: 50 / 255, blue: 71 / 255, alpha: 1)
581
-        static let previewMuted = NSColor(srgbRed: 165 / 255, green: 175 / 255, blue: 192 / 255, alpha: 1)
582
-        static let previewAccentRed = NSColor(srgbRed: 207 / 255, green: 67 / 255, blue: 50 / 255, alpha: 1)
583
-        static let previewAccentBlue = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
584
-        static let ctaBackground = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
585
-        static let ctaHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
586
-        static let ctaText = NSColor.white
587
-        static let selectionGlow = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 0.55)
588
-        static let gradientTop = NSColor(srgbRed: 250 / 255, green: 252 / 255, blue: 1, alpha: 1)
589
-        static let gradientBottom = NSColor(srgbRed: 236 / 255, green: 244 / 255, blue: 1, alpha: 1)
558
+        static var primaryText: NSColor { AppDashboardTheme.primaryText }
559
+        static var secondaryText: NSColor { AppDashboardTheme.secondaryText }
560
+        static var cardBackground: NSColor { AppDashboardTheme.cardBackground }
561
+        static var cardBorder: NSColor { AppDashboardTheme.border }
562
+        static var cardBorderHover: NSColor { AppDashboardTheme.cvMakerCardBorderHover }
563
+        static var cardBorderSelected: NSColor { AppDashboardTheme.brandBlue }
564
+        static var cardFooter: NSColor { AppDashboardTheme.cvMakerCardFooter }
565
+        static var previewSurface: NSColor { AppDashboardTheme.cvMakerPreviewSurface }
566
+        static var previewPaper: NSColor { NSColor.white }
567
+        static var previewSidebarTint: NSColor { AppDashboardTheme.cvMakerPreviewSidebarTint }
568
+        static var previewInk: NSColor {
569
+            NSColor(srgbRed: 38 / 255, green: 50 / 255, blue: 71 / 255, alpha: 1)
570
+        }
571
+        static var previewMuted: NSColor {
572
+            NSColor(srgbRed: 165 / 255, green: 175 / 255, blue: 192 / 255, alpha: 1)
573
+        }
574
+        static var previewAccentRed: NSColor {
575
+            NSColor(srgbRed: 207 / 255, green: 67 / 255, blue: 50 / 255, alpha: 1)
576
+        }
577
+        static var previewAccentBlue: NSColor { AppDashboardTheme.brandBlue }
578
+        static var ctaBackground: NSColor { AppDashboardTheme.brandBlue }
579
+        static var ctaHover: NSColor { AppDashboardTheme.brandBlueHover }
580
+        static var ctaText: NSColor { AppDashboardTheme.proCTAText }
581
+        static var selectionGlow: NSColor { AppDashboardTheme.cvMakerSelectionGlow }
582
+        static var gradientTop: NSColor { AppDashboardTheme.cvMakerPageGradientTop }
583
+        static var gradientBottom: NSColor { AppDashboardTheme.cvMakerPageGradientBottom }
584
+        static var filterChromeBorder: NSColor { AppDashboardTheme.cvMakerFilterChromeBorder }
590 585
     }
591 586
 
587
+    private var appearanceObserver: NSObjectProtocol?
588
+
592 589
     private let pageGradientLayer = CAGradientLayer()
593 590
     private let filterChrome = NSVisualEffectView()
594 591
     private let filterStack = NSStackView()
@@ -650,6 +647,20 @@ final class CVMakerPageView: NSView {
650 647
         reloadTemplateGrid()
651 648
         updateSelectedChipStates()
652 649
         beginLoadingAICatalogIfPossible()
650
+        appearanceObserver = NotificationCenter.default.addObserver(
651
+            forName: AppAppearanceManager.didChangeNotification,
652
+            object: nil,
653
+            queue: .main
654
+        ) { [weak self] _ in
655
+            self?.applyCurrentAppearance()
656
+        }
657
+        applyCurrentAppearance()
658
+    }
659
+
660
+    deinit {
661
+        if let appearanceObserver {
662
+            NotificationCenter.default.removeObserver(appearanceObserver)
663
+        }
653 664
     }
654 665
 
655 666
     @available(*, unavailable)
@@ -657,32 +668,47 @@ final class CVMakerPageView: NSView {
657 668
         fatalError("init(coder:) has not been implemented")
658 669
     }
659 670
 
671
+    override func viewDidChangeEffectiveAppearance() {
672
+        super.viewDidChangeEffectiveAppearance()
673
+        applyCurrentAppearance()
674
+    }
675
+
660 676
     override func layout() {
661 677
         super.layout()
662 678
         pageGradientLayer.frame = bounds
663 679
         layoutGridCardsIfNeeded()
664 680
     }
665 681
 
682
+    func applyCurrentAppearance() {
683
+        pageGradientLayer.colors = [Palette.gradientBottom.cgColor, Palette.gradientTop.cgColor]
684
+        filterChrome.layer?.borderColor = Palette.filterChromeBorder.cgColor
685
+        titleLabel.textColor = Palette.primaryText
686
+        subtitleLabel.textColor = Palette.secondaryText
687
+        styleCTAButton(ctaButton)
688
+        for chip in groupTabButtons.values { chip.applyCurrentAppearance() }
689
+        for chip in familyChipButtons.values { chip.applyCurrentAppearance() }
690
+        reloadTemplateGrid()
691
+        updateSelectedChipStates()
692
+    }
693
+
666 694
     // MARK: Setup
667 695
 
668 696
     private func configureLayout() {
669 697
         wantsLayer = true
670 698
         layer?.backgroundColor = NSColor.clear.cgColor
671 699
 
672
-        pageGradientLayer.colors = [Palette.gradientBottom.cgColor, Palette.gradientTop.cgColor]
673 700
         pageGradientLayer.locations = [0, 1] as [NSNumber]
674 701
         pageGradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
675 702
         pageGradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
676 703
         layer?.insertSublayer(pageGradientLayer, at: 0)
677 704
 
678 705
         filterChrome.translatesAutoresizingMaskIntoConstraints = false
679
-        filterChrome.material = .sidebar
706
+        filterChrome.material = .contentBackground
680 707
         filterChrome.blendingMode = .withinWindow
681 708
         filterChrome.state = .active
682 709
         filterChrome.wantsLayer = true
683 710
         filterChrome.layer?.cornerRadius = 18
684 711
         filterChrome.layer?.borderWidth = 1
685
-        filterChrome.layer?.borderColor = NSColor.white.withAlphaComponent(0.65).cgColor
686 712
 
687 713
         filterStack.orientation = .vertical
688 714
         filterStack.spacing = 12
@@ -975,6 +1001,7 @@ final class CVMakerPageView: NSView {
975 1001
             borderHover: Palette.cardBorderHover,
976 1002
             borderSelected: Palette.cardBorderSelected,
977 1003
             selectionGlow: Palette.selectionGlow,
1004
+            cardShellBackground: Palette.cardBackground,
978 1005
             footerBackground: Palette.cardFooter,
979 1006
             previewSurface: Palette.previewSurface,
980 1007
             previewPaper: Palette.previewPaper,
@@ -1121,18 +1148,22 @@ private final class CVChipButton: NSView {
1121 1148
     private var trackingArea: NSTrackingArea?
1122 1149
 
1123 1150
     private enum Palette {
1124
-        static let restFill = NSColor.white
1125
-        static let restBorder = NSColor(srgbRed: 222 / 255, green: 226 / 255, blue: 233 / 255, alpha: 1)
1126
-        static let hoverFill = NSColor(srgbRed: 248 / 255, green: 250 / 255, blue: 252 / 255, alpha: 1)
1127
-        static let activeFill = NSColor(srgbRed: 37 / 255, green: 87 / 255, blue: 167 / 255, alpha: 1)
1128
-        static let activeFillHover = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
1129
-        static let activeBorder = NSColor(srgbRed: 28 / 255, green: 70 / 255, blue: 140 / 255, alpha: 1)
1130
-        static let restText = NSColor(srgbRed: 71 / 255, green: 85 / 255, blue: 105 / 255, alpha: 1)
1131
-        static let activeText = NSColor.white
1132
-        static let restBadge = NSColor(srgbRed: 230 / 255, green: 234 / 255, blue: 240 / 255, alpha: 1)
1133
-        static let restBadgeText = NSColor(srgbRed: 100 / 255, green: 116 / 255, blue: 139 / 255, alpha: 1)
1134
-        static let activeBadge = NSColor.white.withAlphaComponent(0.22)
1135
-        static let activeBadgeText = NSColor.white
1151
+        static var restFill: NSColor { AppDashboardTheme.cvMakerChipRestFill }
1152
+        static var restBorder: NSColor { AppDashboardTheme.cvMakerChipRestBorder }
1153
+        static var hoverFill: NSColor { AppDashboardTheme.cvMakerChipHoverFill }
1154
+        static var activeFill: NSColor { AppDashboardTheme.brandBlue }
1155
+        static var activeFillHover: NSColor { AppDashboardTheme.brandBlueHover }
1156
+        static var activeBorder: NSColor { AppDashboardTheme.brandBlueHover }
1157
+        static var restText: NSColor { AppDashboardTheme.primaryText }
1158
+        static var activeText: NSColor { AppDashboardTheme.proCTAText }
1159
+        static var restBadge: NSColor { AppDashboardTheme.cvMakerChipBadgeBackground }
1160
+        static var restBadgeText: NSColor { AppDashboardTheme.cvMakerChipBadgeText }
1161
+        static var activeBadge: NSColor { NSColor.white.withAlphaComponent(0.22) }
1162
+        static var activeBadgeText: NSColor { AppDashboardTheme.proCTAText }
1163
+    }
1164
+
1165
+    func applyCurrentAppearance() {
1166
+        applyState()
1136 1167
     }
1137 1168
 
1138 1169
     private static let symbolSide: CGFloat = 18
@@ -1331,6 +1362,7 @@ private final class CVTemplateCard: NSView {
1331 1362
     private let template: CVTemplate
1332 1363
     private let palette: CVTemplateCardPalette
1333 1364
     private let previewSurface = NSView()
1365
+    private let footerView = NSView()
1334 1366
     private let preview: CVTemplatePreviewView
1335 1367
     private let nameLabel = NSTextField(labelWithString: "")
1336 1368
     private let categoryLabel = NSTextField(labelWithString: "")
@@ -1346,7 +1378,7 @@ private final class CVTemplateCard: NSView {
1346 1378
         wantsLayer = true
1347 1379
         layer?.masksToBounds = false
1348 1380
         layer?.cornerRadius = 24
1349
-        layer?.backgroundColor = NSColor.white.cgColor
1381
+        layer?.backgroundColor = palette.cardShellBackground.cgColor
1350 1382
         translatesAutoresizingMaskIntoConstraints = false
1351 1383
         heightAnchor.constraint(equalToConstant: Self.layoutHeight).isActive = true
1352 1384
 
@@ -1379,20 +1411,19 @@ private final class CVTemplateCard: NSView {
1379 1411
         footerStack.alignment = .leading
1380 1412
         footerStack.translatesAutoresizingMaskIntoConstraints = false
1381 1413
 
1382
-        let footer = NSView()
1383
-        footer.translatesAutoresizingMaskIntoConstraints = false
1384
-        footer.wantsLayer = true
1385
-        footer.layer?.backgroundColor = palette.footerBackground.cgColor
1386
-        footer.addSubview(footerStack)
1414
+        footerView.translatesAutoresizingMaskIntoConstraints = false
1415
+        footerView.wantsLayer = true
1416
+        footerView.layer?.backgroundColor = palette.footerBackground.cgColor
1417
+        footerView.addSubview(footerStack)
1387 1418
         NSLayoutConstraint.activate([
1388
-            footerStack.leadingAnchor.constraint(equalTo: footer.leadingAnchor, constant: 16),
1389
-            footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footer.trailingAnchor, constant: -16),
1390
-            footerStack.topAnchor.constraint(equalTo: footer.topAnchor, constant: 13),
1391
-            footerStack.bottomAnchor.constraint(equalTo: footer.bottomAnchor, constant: -13)
1419
+            footerStack.leadingAnchor.constraint(equalTo: footerView.leadingAnchor, constant: 16),
1420
+            footerStack.trailingAnchor.constraint(lessThanOrEqualTo: footerView.trailingAnchor, constant: -16),
1421
+            footerStack.topAnchor.constraint(equalTo: footerView.topAnchor, constant: 13),
1422
+            footerStack.bottomAnchor.constraint(equalTo: footerView.bottomAnchor, constant: -13)
1392 1423
         ])
1393 1424
 
1394 1425
         addSubview(previewSurface)
1395
-        addSubview(footer)
1426
+        addSubview(footerView)
1396 1427
 
1397 1428
         NSLayoutConstraint.activate([
1398 1429
             previewSurface.topAnchor.constraint(equalTo: topAnchor),
@@ -1405,10 +1436,10 @@ private final class CVTemplateCard: NSView {
1405 1436
             preview.trailingAnchor.constraint(equalTo: previewSurface.trailingAnchor, constant: -16),
1406 1437
             preview.bottomAnchor.constraint(equalTo: previewSurface.bottomAnchor, constant: -14),
1407 1438
 
1408
-            footer.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
1409
-            footer.leadingAnchor.constraint(equalTo: leadingAnchor),
1410
-            footer.trailingAnchor.constraint(equalTo: trailingAnchor),
1411
-            footer.bottomAnchor.constraint(equalTo: bottomAnchor)
1439
+            footerView.topAnchor.constraint(equalTo: previewSurface.bottomAnchor),
1440
+            footerView.leadingAnchor.constraint(equalTo: leadingAnchor),
1441
+            footerView.trailingAnchor.constraint(equalTo: trailingAnchor),
1442
+            footerView.bottomAnchor.constraint(equalTo: bottomAnchor)
1412 1443
         ])
1413 1444
         applyChrome()
1414 1445
     }
@@ -1499,6 +1530,11 @@ private final class CVTemplateCard: NSView {
1499 1530
         } else {
1500 1531
             borderColor = palette.border
1501 1532
         }
1533
+        layer?.backgroundColor = palette.cardShellBackground.cgColor
1534
+        previewSurface.layer?.backgroundColor = palette.previewSurface.cgColor
1535
+        footerView.layer?.backgroundColor = palette.footerBackground.cgColor
1536
+        nameLabel.textColor = palette.primaryText
1537
+        categoryLabel.textColor = palette.secondaryText
1502 1538
         layer?.borderColor = borderColor.cgColor
1503 1539
         layer?.borderWidth = uniformBorder
1504 1540
         layer?.shadowColor = NSColor.black.cgColor

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

@@ -15,6 +15,7 @@ struct CVTemplateCardPalette {
15 15
     let borderHover: NSColor
16 16
     let borderSelected: NSColor
17 17
     let selectionGlow: NSColor
18
+    let cardShellBackground: NSColor
18 19
     let footerBackground: NSColor
19 20
     let previewSurface: NSColor
20 21
     let previewPaper: NSColor

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

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