|
|
@@ -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) {
|