Browse Source

Remove home status banner from dashboard

Drop the light-blue status strip above chat (label, thinking indicator,
brand row, and related layout/animation). Job search flow still uses
inline chat thinking rows; status text updates tied to the banner are
removed.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 3 weeks ago
parent
commit
d0596f75f7
1 changed files with 0 additions and 228 deletions
  1. 0 228
      App for Indeed/Views/DashboardView.swift

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

@@ -29,8 +29,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
29 29
         static let secondaryText = NSColor(srgbRed: 118 / 255, green: 118 / 255, blue: 118 / 255, alpha: 1)
30 30
         static let tertiaryText = NSColor(srgbRed: 118 / 255, green: 118 / 255, blue: 118 / 255, alpha: 1)
31 31
         static let border = NSColor(srgbRed: 212 / 255, green: 210 / 255, blue: 208 / 255, alpha: 1)
32
-        /// Home status strip and soft accents (light blue panel in the reference UI).
33
-        static let statusBannerBackground = NSColor(srgbRed: 232 / 255, green: 242 / 255, blue: 252 / 255, alpha: 1)
34 32
         static let featureIconWell = NSColor(srgbRed: 220 / 255, green: 235 / 255, blue: 252 / 255, alpha: 1)
35 33
         /// Job search bar outer stroke (soft blue-gray, pill field in the reference UI).
36 34
         static let searchBarBorder = NSColor(srgbRed: 180 / 255, green: 200 / 255, blue: 228 / 255, alpha: 1)
@@ -84,16 +82,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
84 82
     private let findJobsCTAHost = NSView()
85 83
     private let findJobsCTAChrome = HoverableView()
86 84
     private var findJobsCTAGradientLayer: CAGradientLayer?
87
-    private let statusBannerContainer = NSView()
88
-    private let statusBannerInner = NSView()
89
-    private let statusBannerRow = NSStackView()
90
-    private let chatStatusSymbolContainer = NSView()
91
-    private let chatStatusIcon = NSImageView()
92
-    private let bannerChatThinkingIndicator = ChatThinkingIndicatorView(compact: true)
93
-    private let chatStatusLabel = NSTextField(wrappingLabelWithString: "Opening the vault...")
94
-    private let statusBrandTrailing = NSStackView()
95
-    private let statusBrandStarIcon = NSImageView()
96
-    private let statusBrandLabel = NSTextField(labelWithString: "AI Job Finder")
97 85
     private let welcomeSparkleIcon = NSImageView()
98 86
     private let featureCardsRow = NSStackView()
99 87
     private let chatScrollView = NSScrollView()
@@ -126,7 +114,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
126 114
     private var isAwaitingResponse = false
127 115
     /// Shown under the latest user message while a job search request is in flight.
128 116
     private var chatThinkingRowHost: NSView?
129
-    private var chatStatusSymbolWidthConstraint: NSLayoutConstraint?
130 117
     private let jobSearchService = OpenAIJobSearchService()
131 118
 
132 119
     /// Upper bound sent to the model per request (was fixed at 8 in the prompt). Clamped when calling the API.
@@ -138,9 +125,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
138 125
         min(jobsPerSearchMaxCap, max(jobsPerSearchMin, requested))
139 126
     }
140 127
 
141
-    private static let chatStatusSparklePulseKey = "chatStatusSparklePulse"
142
-    /// Status label value while the model/API is working; drives the thinking affordance in the banner and chat.
143
-    private static let chatStatusLoadingMessage = "Searching…"
144 128
     private static let welcomeSubtitleBreathKey = "welcomeSubtitleBreath"
145 129
 
146 130
     override init(frame frameRect: NSRect) {
@@ -160,7 +144,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
160 144
         updateFindJobsCTAShadowPath()
161 145
         updateJobListingDescriptionWidths()
162 146
         updateChatBubbleWidths()
163
-        updateStatusBannerLabelWidth()
164 147
     }
165 148
 
166 149
     func render(_ data: DashboardData) {
@@ -270,7 +253,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
270 253
         mainOverlay.addArrangedSubview(titleBlock)
271 254
         mainOverlay.addArrangedSubview(featureCardsRow)
272 255
         mainOverlay.addArrangedSubview(midSpacer)
273
-        mainOverlay.addArrangedSubview(statusBannerContainer)
274 256
         mainOverlay.addArrangedSubview(chatTopSpacer)
275 257
         mainOverlay.addArrangedSubview(chatScrollView)
276 258
         mainOverlay.addArrangedSubview(chatBottomSpacer)
@@ -304,7 +286,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
304 286
             nonHomeHost.bottomAnchor.constraint(equalTo: mainHost.bottomAnchor, constant: -24),
305 287
 
306 288
             searchBarShadowHost.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
307
-            statusBannerContainer.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
308 289
             featureCardsRow.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
309 290
             chatScrollView.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
310 291
 
@@ -340,111 +321,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
340 321
     }
341 322
 
342 323
     private func configureChatViews() {
343
-        statusBannerContainer.translatesAutoresizingMaskIntoConstraints = false
344
-        statusBannerContainer.setContentHuggingPriority(.defaultHigh, for: .vertical)
345
-
346
-        statusBannerInner.translatesAutoresizingMaskIntoConstraints = false
347
-        statusBannerInner.wantsLayer = true
348
-        statusBannerInner.layer?.backgroundColor = Theme.statusBannerBackground.cgColor
349
-        statusBannerInner.layer?.cornerRadius = 14
350
-        if #available(macOS 11.0, *) {
351
-            statusBannerInner.layer?.cornerCurve = .continuous
352
-        }
353
-        statusBannerInner.layer?.masksToBounds = true
354
-
355
-        statusBannerContainer.addSubview(statusBannerInner)
356
-
357
-        statusBannerRow.orientation = .horizontal
358
-        statusBannerRow.spacing = 12
359
-        statusBannerRow.alignment = .top
360
-        statusBannerRow.translatesAutoresizingMaskIntoConstraints = false
361
-
362
-        chatStatusSymbolContainer.translatesAutoresizingMaskIntoConstraints = false
363
-        chatStatusSymbolContainer.wantsLayer = true
364
-        chatStatusSymbolContainer.layer?.backgroundColor = Theme.brandBlue.cgColor
365
-        chatStatusSymbolContainer.layer?.cornerRadius = 20
366
-
367
-        chatStatusIcon.translatesAutoresizingMaskIntoConstraints = false
368
-        chatStatusIcon.wantsLayer = true
369
-        chatStatusIcon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 15, weight: .semibold)
370
-        chatStatusIcon.image = NSImage(systemSymbolName: "sparkles", accessibilityDescription: "Assistant status")
371
-        chatStatusIcon.contentTintColor = .white
372
-
373
-        bannerChatThinkingIndicator.translatesAutoresizingMaskIntoConstraints = false
374
-        bannerChatThinkingIndicator.isHidden = true
375
-
376
-        chatStatusSymbolContainer.addSubview(chatStatusIcon)
377
-        chatStatusSymbolContainer.addSubview(bannerChatThinkingIndicator)
378
-
379
-        let symbolWidth = chatStatusSymbolContainer.widthAnchor.constraint(equalToConstant: 40)
380
-        chatStatusSymbolWidthConstraint = symbolWidth
381
-
382
-        NSLayoutConstraint.activate([
383
-            symbolWidth,
384
-            chatStatusSymbolContainer.heightAnchor.constraint(equalToConstant: 40),
385
-            chatStatusIcon.centerXAnchor.constraint(equalTo: chatStatusSymbolContainer.centerXAnchor),
386
-            chatStatusIcon.centerYAnchor.constraint(equalTo: chatStatusSymbolContainer.centerYAnchor),
387
-            bannerChatThinkingIndicator.centerXAnchor.constraint(equalTo: chatStatusSymbolContainer.centerXAnchor),
388
-            bannerChatThinkingIndicator.centerYAnchor.constraint(equalTo: chatStatusSymbolContainer.centerYAnchor)
389
-        ])
390
-
391
-        chatStatusLabel.font = .systemFont(ofSize: 13, weight: .regular)
392
-        chatStatusLabel.textColor = Theme.primaryText
393
-        chatStatusLabel.alignment = .left
394
-        chatStatusLabel.maximumNumberOfLines = 0
395
-        chatStatusLabel.lineBreakMode = .byWordWrapping
396
-        chatStatusLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
397
-        chatStatusLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
398
-        chatStatusLabel.translatesAutoresizingMaskIntoConstraints = false
399
-
400
-        statusBrandStarIcon.translatesAutoresizingMaskIntoConstraints = false
401
-        statusBrandStarIcon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
402
-        statusBrandStarIcon.image = NSImage(systemSymbolName: "sparkle", accessibilityDescription: nil)
403
-        statusBrandStarIcon.contentTintColor = Theme.brandBlue
404
-
405
-        statusBrandLabel.font = .systemFont(ofSize: 12, weight: .semibold)
406
-        statusBrandLabel.textColor = Theme.brandBlue
407
-        statusBrandLabel.alignment = .right
408
-        statusBrandLabel.maximumNumberOfLines = 1
409
-
410
-        statusBrandTrailing.orientation = .horizontal
411
-        statusBrandTrailing.spacing = 5
412
-        statusBrandTrailing.alignment = .centerY
413
-        statusBrandTrailing.translatesAutoresizingMaskIntoConstraints = false
414
-        statusBrandTrailing.addArrangedSubview(statusBrandStarIcon)
415
-        statusBrandTrailing.addArrangedSubview(statusBrandLabel)
416
-        statusBrandTrailing.setContentHuggingPriority(.required, for: .horizontal)
417
-        statusBrandTrailing.setContentCompressionResistancePriority(.required, for: .horizontal)
418
-
419
-        statusBannerRow.addArrangedSubview(chatStatusSymbolContainer)
420
-        statusBannerRow.addArrangedSubview(chatStatusLabel)
421
-
422
-        statusBannerInner.addSubview(statusBannerRow)
423
-        statusBannerContainer.addSubview(statusBannerInner)
424
-        statusBannerContainer.addSubview(statusBrandTrailing)
425
-        statusBannerInner.setContentHuggingPriority(.defaultHigh, for: .horizontal)
426
-        statusBannerInner.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
427
-
428
-        NSLayoutConstraint.activate([
429
-            statusBannerInner.leadingAnchor.constraint(equalTo: statusBannerContainer.leadingAnchor),
430
-            statusBannerInner.topAnchor.constraint(equalTo: statusBannerContainer.topAnchor),
431
-            statusBannerInner.bottomAnchor.constraint(equalTo: statusBannerContainer.bottomAnchor),
432
-            statusBannerInner.trailingAnchor.constraint(lessThanOrEqualTo: statusBrandTrailing.leadingAnchor, constant: -16),
433
-
434
-            statusBrandTrailing.trailingAnchor.constraint(equalTo: statusBannerContainer.trailingAnchor),
435
-            statusBrandTrailing.centerYAnchor.constraint(equalTo: statusBannerContainer.centerYAnchor),
436
-
437
-            statusBannerRow.leadingAnchor.constraint(equalTo: statusBannerInner.leadingAnchor, constant: 14),
438
-            statusBannerRow.trailingAnchor.constraint(equalTo: statusBannerInner.trailingAnchor, constant: -16),
439
-            statusBannerRow.topAnchor.constraint(equalTo: statusBannerInner.topAnchor, constant: 10),
440
-            statusBannerRow.bottomAnchor.constraint(equalTo: statusBannerInner.bottomAnchor, constant: -10),
441
-
442
-            statusBannerContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: 52)
443
-        ])
444
-
445
-        syncChatStatusLoadingIndicator(forStatusText: chatStatusLabel.stringValue)
446
-        syncChatStatusBannerVisuals(forStatusText: chatStatusLabel.stringValue)
447
-
448 324
         chatDocumentView.translatesAutoresizingMaskIntoConstraints = false
449 325
         chatStack.orientation = .vertical
450 326
         chatStack.spacing = 18
@@ -479,66 +355,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
479 355
         ])
480 356
     }
481 357
 
482
-    private func setChatStatusLabel(_ text: String) {
483
-        chatStatusLabel.stringValue = text
484
-        syncChatStatusLoadingIndicator(forStatusText: text)
485
-        syncChatStatusBannerVisuals(forStatusText: text)
486
-        syncChatStatusSparkleAnimation()
487
-        updateStatusBannerLabelWidth()
488
-    }
489
-
490
-    private func updateStatusBannerLabelWidth() {
491
-        guard statusBannerContainer.bounds.width > 1 else { return }
492
-        let trailingWidth = max(96, statusBrandTrailing.fittingSize.width)
493
-        let symbolChrome = chatStatusSymbolWidthConstraint?.constant ?? 40
494
-        let chrome: CGFloat = 14 + symbolChrome + 12 + 16 + 16 + trailingWidth
495
-        let target = max(80, statusBannerContainer.bounds.width - chrome)
496
-        if abs(chatStatusLabel.preferredMaxLayoutWidth - target) > 0.5 {
497
-            chatStatusLabel.preferredMaxLayoutWidth = target
498
-            chatStatusLabel.invalidateIntrinsicContentSize()
499
-        }
500
-    }
501
-
502
-    /// Leading status glyph: sparkles for prompts, magnifying glass for search-result summaries and errors.
503
-    private func syncChatStatusBannerVisuals(forStatusText text: String) {
504
-        if text == Self.chatStatusLoadingMessage { return }
505
-        let lower = text.lowercased()
506
-        let useMagnifyingGlass =
507
-            text.hasPrefix("Found ")
508
-            || text.hasPrefix("Here are")
509
-            || text.hasPrefix("No jobs")
510
-            || text.contains("couldn't find")
511
-            || lower.contains("try again")
512
-            || lower.contains("could not reach")
513
-            || lower.contains("search did not finish")
514
-        let config = NSImage.SymbolConfiguration(pointSize: 15, weight: .semibold)
515
-        chatStatusIcon.symbolConfiguration = config
516
-        if useMagnifyingGlass {
517
-            chatStatusIcon.image = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: "Search status")
518
-        } else {
519
-            chatStatusIcon.image = NSImage(systemSymbolName: "sparkles", accessibilityDescription: "Assistant status")
520
-        }
521
-        chatStatusIcon.contentTintColor = .white
522
-        chatStatusSymbolContainer.layer?.backgroundColor = Theme.brandBlue.cgColor
523
-    }
524
-
525
-    private func syncChatStatusLoadingIndicator(forStatusText text: String) {
526
-        let loading = (text == Self.chatStatusLoadingMessage)
527
-        if loading {
528
-            chatStatusIcon.isHidden = true
529
-            bannerChatThinkingIndicator.isHidden = false
530
-            bannerChatThinkingIndicator.startAnimatingIfNeeded()
531
-            chatStatusSymbolContainer.layer?.backgroundColor = NSColor.clear.cgColor
532
-            chatStatusSymbolWidthConstraint?.constant = 56
533
-        } else {
534
-            bannerChatThinkingIndicator.stopAnimating()
535
-            bannerChatThinkingIndicator.isHidden = true
536
-            chatStatusIcon.isHidden = false
537
-            chatStatusSymbolWidthConstraint?.constant = 40
538
-            chatStatusSymbolContainer.layer?.backgroundColor = Theme.brandBlue.cgColor
539
-        }
540
-    }
541
-
542 358
     private func isWelcomeHeroVisible() -> Bool {
543 359
         !mainOverlay.isHidden
544 360
     }
@@ -547,37 +363,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
547 363
         NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
548 364
     }
549 365
 
550
-    private func shouldAnimateChatStatusSparkles(for statusText: String) -> Bool {
551
-        statusText == "Ask me to find jobs"
552
-            || statusText == "Opening the vault..."
553
-    }
554
-
555
-    private func syncChatStatusSparkleAnimation() {
556
-        guard !prefersReducedMotion else {
557
-            stopChatStatusSparkleAnimation()
558
-            return
559
-        }
560
-        guard isWelcomeHeroVisible(), shouldAnimateChatStatusSparkles(for: chatStatusLabel.stringValue) else {
561
-            stopChatStatusSparkleAnimation()
562
-            return
563
-        }
564
-        guard let layer = chatStatusIcon.layer, layer.animation(forKey: Self.chatStatusSparklePulseKey) == nil else { return }
565
-
566
-        let scale = CABasicAnimation(keyPath: "transform.scale")
567
-        scale.fromValue = 1.0
568
-        scale.toValue = 1.1
569
-        scale.duration = 1.25
570
-        scale.autoreverses = true
571
-        scale.repeatCount = .greatestFiniteMagnitude
572
-        scale.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
573
-        layer.add(scale, forKey: Self.chatStatusSparklePulseKey)
574
-    }
575
-
576
-    private func stopChatStatusSparkleAnimation() {
577
-        chatStatusIcon.layer?.removeAnimation(forKey: Self.chatStatusSparklePulseKey)
578
-        chatStatusIcon.layer?.transform = CATransform3DIdentity
579
-    }
580
-
581 366
     private func syncWelcomeSubtitleBreathingAnimation() {
582 367
         guard !prefersReducedMotion else {
583 368
             stopWelcomeSubtitleBreathingAnimation()
@@ -1520,10 +1305,8 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1520 1305
         }
1521 1306
         if home {
1522 1307
             syncWelcomeSubtitleBreathingAnimation()
1523
-            syncChatStatusSparkleAnimation()
1524 1308
         } else {
1525 1309
             stopWelcomeSubtitleBreathingAnimation()
1526
-            stopChatStatusSparkleAnimation()
1527 1310
         }
1528 1311
     }
1529 1312
 
@@ -1590,7 +1373,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1590 1373
 
1591 1374
     private func startJobSearchRequest(effectiveQuery: String, isContinuation: Bool) {
1592 1375
         isAwaitingResponse = true
1593
-        setChatStatusLabel(Self.chatStatusLoadingMessage)
1594 1376
         addInlineChatThinkingRow()
1595 1377
         setInputEnabled(false)
1596 1378
         let contextMessages = chatMessages
@@ -1620,14 +1402,8 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1620 1402
                     )
1621 1403
                     self.chatMessages.append(ChatMessage(role: "assistant", content: reply))
1622 1404
                     self.appendChatBubble(text: reply, isUser: false, jobs: freshJobs)
1623
-                    self.setChatStatusLabel(reply)
1624 1405
                 case .failure(let error):
1625 1406
                     self.appendChatBubble(text: error.localizedDescription, isUser: false)
1626
-                    if error is URLError {
1627
-                        self.setChatStatusLabel("Could not reach API. Try again.")
1628
-                    } else {
1629
-                        self.setChatStatusLabel("Search did not finish. Try again.")
1630
-                    }
1631 1407
                 }
1632 1408
             }
1633 1409
         }
@@ -1733,9 +1509,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1733 1509
 
1734 1510
     func controlTextDidBeginEditing(_ obj: Notification) {
1735 1511
         applySearchFieldInsertionPoint(obj.object)
1736
-        if (obj.object as? NSTextField) === jobKeywordsField {
1737
-            setChatStatusLabel("Opening the vault...")
1738
-        }
1739 1512
     }
1740 1513
 
1741 1514
     func controlTextDidChange(_ obj: Notification) {
@@ -1770,7 +1543,6 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1770 1543
         let welcome = "Tell me what role you want and I will return job descriptions, key skills, and a quick fit summary."
1771 1544
         chatMessages.append(ChatMessage(role: "assistant", content: welcome))
1772 1545
         appendChatBubble(text: welcome, isUser: false)
1773
-        setChatStatusLabel("Ask me to find jobs")
1774 1546
     }
1775 1547
 
1776 1548
     private func appendChatBubble(text: String, isUser: Bool, jobs: [JobListing]? = nil) {