Explorar el Código

Fix job search results visibility and simplify assistant response.

Render the listings scroll section in the main layout so result cards appear after search, and replace verbose row/JSON chat output with a concise card-focused summary.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 hace 3 semanas
padre
commit
a57976258d
Se han modificado 1 ficheros con 8 adiciones y 32 borrados
  1. 8 32
      App for Indeed/Views/DashboardView.swift

+ 8 - 32
App for Indeed/Views/DashboardView.swift

@@ -259,6 +259,7 @@ final class DashboardView: NSView, NSTextFieldDelegate {
259
         mainOverlay.addArrangedSubview(chatStatusStack)
259
         mainOverlay.addArrangedSubview(chatStatusStack)
260
         mainOverlay.addArrangedSubview(listingsTopSpacer)
260
         mainOverlay.addArrangedSubview(listingsTopSpacer)
261
         mainOverlay.addArrangedSubview(chatScrollView)
261
         mainOverlay.addArrangedSubview(chatScrollView)
262
+        mainOverlay.addArrangedSubview(jobListingsScrollView)
262
         mainOverlay.addArrangedSubview(searchBarShadowHost)
263
         mainOverlay.addArrangedSubview(searchBarShadowHost)
263
 
264
 
264
         contentStack.addArrangedSubview(sidebar)
265
         contentStack.addArrangedSubview(sidebar)
@@ -291,10 +292,12 @@ final class DashboardView: NSView, NSTextFieldDelegate {
291
             searchBarShadowHost.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
292
             searchBarShadowHost.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
292
             chatStatusStack.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
293
             chatStatusStack.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
293
             chatScrollView.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
294
             chatScrollView.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
295
+            jobListingsScrollView.widthAnchor.constraint(equalTo: mainOverlay.widthAnchor, multiplier: 0.92),
294
 
296
 
295
             jobListingsContainer.topAnchor.constraint(equalTo: jobListingsScrollView.contentView.topAnchor),
297
             jobListingsContainer.topAnchor.constraint(equalTo: jobListingsScrollView.contentView.topAnchor),
296
             jobListingsContainer.leadingAnchor.constraint(equalTo: jobListingsScrollView.contentView.leadingAnchor),
298
             jobListingsContainer.leadingAnchor.constraint(equalTo: jobListingsScrollView.contentView.leadingAnchor),
297
             jobListingsContainer.widthAnchor.constraint(equalTo: jobListingsScrollView.contentView.widthAnchor),
299
             jobListingsContainer.widthAnchor.constraint(equalTo: jobListingsScrollView.contentView.widthAnchor),
300
+            jobListingsScrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200),
298
 
301
 
299
             greetingLabel.leadingAnchor.constraint(equalTo: mainOverlay.leadingAnchor, constant: 16),
302
             greetingLabel.leadingAnchor.constraint(equalTo: mainOverlay.leadingAnchor, constant: 16),
300
             greetingLabel.trailingAnchor.constraint(equalTo: mainOverlay.trailingAnchor, constant: -16),
303
             greetingLabel.trailingAnchor.constraint(equalTo: mainOverlay.trailingAnchor, constant: -16),
@@ -1209,7 +1212,7 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1209
                 case .success(let output):
1212
                 case .success(let output):
1210
                     let normalizedJobs = self.normalizedJobs(output.jobs)
1213
                     let normalizedJobs = self.normalizedJobs(output.jobs)
1211
                     self.configureJobListings(normalizedJobs, noResultsForQuery: prompt)
1214
                     self.configureJobListings(normalizedJobs, noResultsForQuery: prompt)
1212
-                    let reply = self.makeAssistantSearchReply(query: prompt, jobs: normalizedJobs, jsonResult: output.rawJSON)
1215
+                    let reply = self.makeAssistantSearchReply(query: prompt, jobs: normalizedJobs)
1213
                     self.chatMessages.append(ChatMessage(role: "assistant", content: reply))
1216
                     self.chatMessages.append(ChatMessage(role: "assistant", content: reply))
1214
                     self.appendChatBubble(text: reply, isUser: false)
1217
                     self.appendChatBubble(text: reply, isUser: false)
1215
                     self.chatStatusLabel.stringValue = "Ask for another job, company, or skill match"
1218
                     self.chatStatusLabel.stringValue = "Ask for another job, company, or skill match"
@@ -1234,42 +1237,16 @@ final class DashboardView: NSView, NSTextFieldDelegate {
1234
         return trimmed.filter { !$0.title.isEmpty && !$0.description.isEmpty }
1237
         return trimmed.filter { !$0.title.isEmpty && !$0.description.isEmpty }
1235
     }
1238
     }
1236
 
1239
 
1237
-    private func makeAssistantSearchReply(query: String, jobs: [JobListing], jsonResult: String) -> String {
1240
+    private func makeAssistantSearchReply(query: String, jobs: [JobListing]) -> String {
1238
         if jobs.isEmpty {
1241
         if jobs.isEmpty {
1239
-            return """
1240
-            No jobs were found for "\(query)".
1241
-
1242
-            JSON result:
1243
-            \(jsonResult)
1244
-            """
1245
-        }
1246
-        let rows = jobs.prefix(8).enumerated().map { index, job in
1247
-            let fallback = "https://www.indeed.com/jobs?q=\(job.title.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
1248
-            let link = (job.url?.isEmpty == false) ? job.url! : fallback
1249
-            let compactDescription = compactSingleLine(job.description, maxCharacters: 110)
1250
-            return "\(index + 1)) \(job.title) | \(compactDescription) | \(link)"
1242
+            return "No jobs were found for \"\(query)\". Try another title, skill, company, or location."
1251
         }
1243
         }
1252
         return """
1244
         return """
1253
         Found \(jobs.count) job result(s) for "\(query)".
1245
         Found \(jobs.count) job result(s) for "\(query)".
1254
-
1255
-        Row-wise results:
1256
-        \(rows.joined(separator: "\n"))
1257
-
1258
-        JSON result:
1259
-        \(jsonResult)
1246
+        Each result is shown as a card with the role, job description, and an Apply button that opens the job link.
1260
         """
1247
         """
1261
     }
1248
     }
1262
 
1249
 
1263
-    private func compactSingleLine(_ text: String, maxCharacters: Int) -> String {
1264
-        let single = text
1265
-            .replacingOccurrences(of: "\n", with: " ")
1266
-            .replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression)
1267
-            .trimmingCharacters(in: .whitespacesAndNewlines)
1268
-        guard single.count > maxCharacters, maxCharacters > 1 else { return single }
1269
-        let end = single.index(single.startIndex, offsetBy: maxCharacters - 1)
1270
-        return String(single[..<end]) + "…"
1271
-    }
1272
-
1273
     func controlTextDidBeginEditing(_ obj: Notification) {
1250
     func controlTextDidBeginEditing(_ obj: Notification) {
1274
         applySearchFieldInsertionPoint(obj.object)
1251
         applySearchFieldInsertionPoint(obj.object)
1275
         if (obj.object as? NSTextField) === jobKeywordsField {
1252
         if (obj.object as? NSTextField) === jobKeywordsField {
@@ -1665,7 +1642,7 @@ private final class OpenAIJobSearchService {
1665
                 let cleanedText = Self.extractJSONObject(from: text)
1642
                 let cleanedText = Self.extractJSONObject(from: text)
1666
                 let jsonData = Data(cleanedText.utf8)
1643
                 let jsonData = Data(cleanedText.utf8)
1667
                 let jobsPayload = try JSONDecoder().decode(JobSearchResultsPayload.self, from: jsonData)
1644
                 let jobsPayload = try JSONDecoder().decode(JobSearchResultsPayload.self, from: jsonData)
1668
-                completion(.success(JobSearchOutput(jobs: jobsPayload.jobs, rawJSON: cleanedText)))
1645
+                completion(.success(JobSearchOutput(jobs: jobsPayload.jobs)))
1669
             } catch {
1646
             } catch {
1670
                 let rawBody = String(data: data, encoding: .utf8) ?? "<non-utf8 response>"
1647
                 let rawBody = String(data: data, encoding: .utf8) ?? "<non-utf8 response>"
1671
                 let message = "The API response could not be parsed as job JSON. Raw response: \(rawBody.prefix(600))"
1648
                 let message = "The API response could not be parsed as job JSON. Raw response: \(rawBody.prefix(600))"
@@ -1772,7 +1749,6 @@ private struct JobSearchResultsPayload: Codable {
1772
 
1749
 
1773
 private struct JobSearchOutput {
1750
 private struct JobSearchOutput {
1774
     let jobs: [JobListing]
1751
     let jobs: [JobListing]
1775
-    let rawJSON: String
1776
 }
1752
 }
1777
 
1753
 
1778
 private struct OpenAIAPIErrorResponse: Codable {
1754
 private struct OpenAIAPIErrorResponse: Codable {