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 settimane fa
parent
commit
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()