ソースを参照

Add spinner while job search response loads

Show an indeterminate NSProgressIndicator in the chat status area when the
status reads Thinking..., replacing the sparkles icon until the API returns.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 3 週間 前
コミット
1e6c0fde28
共有1 個のファイルを変更した43 個の追加1 個の削除を含む
  1. 43 1
      App for Indeed/Views/DashboardView.swift

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

@@ -82,7 +82,9 @@ final class DashboardView: NSView, NSTextFieldDelegate {
82 82
     private let findJobsCTAChrome = HoverableView()
83 83
     private var findJobsCTAGradientLayer: CAGradientLayer?
84 84
     private let chatStatusStack = NSStackView()
85
+    private let chatStatusSymbolContainer = NSView()
85 86
     private let chatStatusIcon = NSImageView()
87
+    private let chatStatusLoadingIndicator = NSProgressIndicator()
86 88
     private let chatStatusLabel = NSTextField(labelWithString: "Opening the vault...")
87 89
     private let chatScrollView = NSScrollView()
88 90
     private let chatDocumentView = JobListingsDocumentView()
@@ -282,20 +284,47 @@ final class DashboardView: NSView, NSTextFieldDelegate {
282 284
         chatStatusStack.alignment = .centerX
283 285
         chatStatusStack.translatesAutoresizingMaskIntoConstraints = false
284 286
 
287
+        chatStatusSymbolContainer.translatesAutoresizingMaskIntoConstraints = false
288
+
285 289
         chatStatusIcon.translatesAutoresizingMaskIntoConstraints = false
286 290
         chatStatusIcon.wantsLayer = true
287 291
         chatStatusIcon.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 36, weight: .regular)
288 292
         chatStatusIcon.image = NSImage(systemSymbolName: "sparkles", accessibilityDescription: "Assistant status")
289 293
         chatStatusIcon.contentTintColor = Theme.brandBlue
290 294
 
295
+        chatStatusLoadingIndicator.translatesAutoresizingMaskIntoConstraints = false
296
+        chatStatusLoadingIndicator.style = .spinning
297
+        chatStatusLoadingIndicator.isIndeterminate = true
298
+        chatStatusLoadingIndicator.isDisplayedWhenStopped = false
299
+        chatStatusLoadingIndicator.controlSize = .regular
300
+        chatStatusLoadingIndicator.isHidden = true
301
+        chatStatusLoadingIndicator.setAccessibilityRole(.progressIndicator)
302
+        chatStatusLoadingIndicator.setAccessibilityLabel("Searching for jobs")
303
+
304
+        chatStatusSymbolContainer.addSubview(chatStatusIcon)
305
+        chatStatusSymbolContainer.addSubview(chatStatusLoadingIndicator)
306
+
307
+        NSLayoutConstraint.activate([
308
+            chatStatusSymbolContainer.widthAnchor.constraint(equalToConstant: 40),
309
+            chatStatusSymbolContainer.heightAnchor.constraint(equalToConstant: 40),
310
+            chatStatusIcon.centerXAnchor.constraint(equalTo: chatStatusSymbolContainer.centerXAnchor),
311
+            chatStatusIcon.centerYAnchor.constraint(equalTo: chatStatusSymbolContainer.centerYAnchor),
312
+            chatStatusLoadingIndicator.centerXAnchor.constraint(equalTo: chatStatusSymbolContainer.centerXAnchor),
313
+            chatStatusLoadingIndicator.centerYAnchor.constraint(equalTo: chatStatusSymbolContainer.centerYAnchor),
314
+            chatStatusLoadingIndicator.widthAnchor.constraint(equalToConstant: 32),
315
+            chatStatusLoadingIndicator.heightAnchor.constraint(equalToConstant: 32)
316
+        ])
317
+
291 318
         chatStatusLabel.font = .systemFont(ofSize: 20, weight: .semibold)
292 319
         chatStatusLabel.textColor = Theme.primaryText
293 320
         chatStatusLabel.alignment = .center
294 321
         chatStatusLabel.maximumNumberOfLines = 1
295 322
 
296
-        chatStatusStack.addArrangedSubview(chatStatusIcon)
323
+        chatStatusStack.addArrangedSubview(chatStatusSymbolContainer)
297 324
         chatStatusStack.addArrangedSubview(chatStatusLabel)
298 325
 
326
+        syncChatStatusLoadingIndicator(forStatusText: chatStatusLabel.stringValue)
327
+
299 328
         chatDocumentView.translatesAutoresizingMaskIntoConstraints = false
300 329
         chatStack.orientation = .vertical
301 330
         chatStack.spacing = 18
@@ -332,9 +361,22 @@ final class DashboardView: NSView, NSTextFieldDelegate {
332 361
 
333 362
     private func setChatStatusLabel(_ text: String) {
334 363
         chatStatusLabel.stringValue = text
364
+        syncChatStatusLoadingIndicator(forStatusText: text)
335 365
         syncChatStatusSparkleAnimation()
336 366
     }
337 367
 
368
+    private func syncChatStatusLoadingIndicator(forStatusText text: String) {
369
+        let loading = (text == "Thinking...")
370
+        if loading {
371
+            chatStatusIcon.isHidden = true
372
+            chatStatusLoadingIndicator.isHidden = false
373
+            chatStatusLoadingIndicator.startAnimation(nil)
374
+        } else {
375
+            chatStatusLoadingIndicator.stopAnimation(nil)
376
+            chatStatusIcon.isHidden = false
377
+        }
378
+    }
379
+
338 380
     private func isWelcomeHeroVisible() -> Bool {
339 381
         !mainOverlay.isHidden
340 382
     }