feat: Phase 1 BrainBar formatting foundation#155
Conversation
…d shared result cards
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 25 minutes and 27 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (17)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@coderabbitai review |
|
@codex review |
|
@greptileai review |
|
You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
✅ Actions performedReview triggered.
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
| private static func decodeMetadata(_ raw: Any?) -> [String: String] { | ||
| if let metadata = raw as? [String: String] { | ||
| return metadata | ||
| } | ||
| guard let text = raw as? String, let data = text.data(using: .utf8), | ||
| let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { | ||
| return [:] | ||
| } | ||
| var result: [String: String] = [:] | ||
| for (key, value) in parsed { | ||
| result[key] = String(describing: value) | ||
| } | ||
| return result | ||
| } |
There was a problem hiding this comment.
🟢 Low Models/EntityCard.swift:78
decodeMetadata silently returns an empty dictionary when raw is a [String: Any] dictionary containing non-String values (e.g., integers or booleans). The cast to [String: String] fails, the subsequent cast to String also fails, and all metadata is lost without warning. Consider checking for [String: Any] first and applying String(describing:) to the values, so mixed-type dictionaries are preserved.
- private static func decodeMetadata(_ raw: Any?) -> [String: String] {
- if let metadata = raw as? [String: String] {
- return metadata
- }
- guard let text = raw as? String, let data = text.data(using: .utf8),
- let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
- return [:]
- }
- var result: [String: String] = [:]
- for (key, value) in parsed {
- result[key] = String(describing: value)
- }
- return result
- }🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Sources/BrainBar/Models/EntityCard.swift around lines 78-91:
`decodeMetadata` silently returns an empty dictionary when `raw` is a `[String: Any]` dictionary containing non-String values (e.g., integers or booleans). The cast to `[String: String]` fails, the subsequent cast to `String` also fails, and all metadata is lost without warning. Consider checking for `[String: Any]` first and applying `String(describing:)` to the values, so mixed-type dictionaries are preserved.
Evidence trail:
brain-bar/Sources/BrainBar/Models/EntityCard.swift lines 78-93 at REVIEWED_COMMIT show the `decodeMetadata` function. Line 79-81: first tries `raw as? [String: String]`, fails for mixed-type dictionaries. Line 82: then tries `raw as? String`, fails for any dictionary type. Line 83: returns empty `[:]` when guard fails. The code path for `[String: Any]` dictionaries is not handled, causing silent data loss.
| private static func extractImportance(from metadata: String) -> Int? { | ||
| guard let range = metadata.range(of: "imp ") else { return nil } | ||
| let digits = metadata[range.upperBound...].prefix { $0.isNumber } | ||
| return Int(digits) | ||
| } |
There was a problem hiding this comment.
🟢 Low Models/SearchResult.swift:110
extractImportance(from:) fails to parse negative importance values. When compactMetadata produces "imp -5", the prefix { $0.isNumber } check stops at the - character since it isn't a digit, resulting in an empty string that converts to nil. This causes data loss when reconstructing a SearchResult via init(rowID:title:metadata:tags:) from metadata originally generated by compactMetadata. Consider updating the extraction logic to handle an optional leading - before the digits.
+ private static func extractImportance(from metadata: String) -> Int? {
+ guard let range = metadata.range(of: "imp ") else { return nil }
+ let remainder = metadata[range.upperBound...]
+ let sign = remainder.first == "-" ? "-" : ""
+ let startIndex = sign.isEmpty ? remainder.startIndex : remainder.index(after: remainder.startIndex)
+ let digits = remainder[startIndex...].prefix { $0.isNumber }
+ return Int(sign + digits)
+ }🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Sources/BrainBar/Models/SearchResult.swift around lines 110-114:
`extractImportance(from:)` fails to parse negative importance values. When `compactMetadata` produces `"imp -5"`, the `prefix { $0.isNumber }` check stops at the `-` character since it isn't a digit, resulting in an empty string that converts to `nil`. This causes data loss when reconstructing a `SearchResult` via `init(rowID:title:metadata:tags:)` from metadata originally generated by `compactMetadata`. Consider updating the extraction logic to handle an optional leading `-` before the digits.
Evidence trail:
brain-bar/Sources/BrainBar/Models/SearchResult.swift lines 10 (importance: Int?), 23-37 (compactMetadata producing "imp \(importance)"), 112-115 (extractImportance using prefix { $0.isNumber }) at REVIEWED_COMMIT. Swift's Character.isNumber returns false for '-' character, causing prefix to return empty string for negative numbers.
| ForEach(results) { result in | ||
| SearchResultCard( | ||
| result: result, | ||
| isSelected: result.id == selectedResultID, | ||
| isCopied: result.id == copiedResultID | ||
| ) | ||
| .onTapGesture { | ||
| onSelect(result.id) | ||
| } | ||
| .onTapGesture(count: 2) { | ||
| onActivate(result.id) | ||
| } | ||
| .focusable(false) | ||
| } | ||
| } |
There was a problem hiding this comment.
🟡 Medium Components/SearchResultsList.swift:24
The double-tap gesture at line 33 will never fire because the single-tap gesture at line 30 intercepts all touches first. In SwiftUI, gestures are evaluated in modifier order; the single tap wins immediately, so onActivate is never called. Apply the double-tap gesture modifier before the single-tap gesture so both can be recognized.
ForEach(results) { result in
SearchResultCard(
result: result,
isSelected: result.id == selectedResultID,
isCopied: result.id == copiedResultID
)
+ .onTapGesture(count: 2) {
+ onActivate(result.id)
+ }
.onTapGesture {
onSelect(result.id)
}
- .onTapGesture(count: 2) {
- onActivate(result.id)
- }
.focusable(false)
}🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Sources/BrainBar/Views/Components/SearchResultsList.swift around lines 24-38:
The double-tap gesture at line 33 will never fire because the single-tap gesture at line 30 intercepts all touches first. In SwiftUI, gestures are evaluated in modifier order; the single tap wins immediately, so `onActivate` is never called. Apply the double-tap gesture modifier before the single-tap gesture so both can be recognized.
Evidence trail:
brain-bar/Sources/BrainBar/Views/Components/SearchResultsList.swift lines 30-35 at REVIEWED_COMMIT - shows single tap gesture (.onTapGesture) applied at line 30 before double tap gesture (.onTapGesture(count: 2)) at line 33. SwiftUI documentation confirms that when combining tap gestures with different counts, the higher count gesture should be applied first to allow proper recognition.
…er size Five QA fixes from user testing after overnight sprint PRs #155-#160: (1) Store freezes UI: Added storeAsync() — runs DB write on background DispatchQueue via withCheckedThrowingContinuation. QuickCapture submitCapture now uses async path, UI stays responsive. (2) Search results missing dates: SearchQueryCandidate now includes date, project, importance fields. searchCandidates SQL extended to SELECT created_at, project, importance. SearchViewModel passes these through to SearchResult for display. (3) Enter in search goes to capture: Changed applySelectedSearchResult() to copy result to clipboard instead of switching to capture mode. Also auto-selects first result when selectedResultIndex is nil. (4) Popover oversized: Reduced contentSize from 420x620 to 360x320. (5) VoiceBar duplicate struct: Merged voicelayer PR #109. Also: Updated busy_timeout test to match PR #160's 30s value. 212 tests, 0 failures. Build and sign pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…er size (#161) Five QA fixes from user testing after overnight sprint PRs #155-#160: (1) Store freezes UI: Added storeAsync() — runs DB write on background DispatchQueue via withCheckedThrowingContinuation. QuickCapture submitCapture now uses async path, UI stays responsive. (2) Search results missing dates: SearchQueryCandidate now includes date, project, importance fields. searchCandidates SQL extended to SELECT created_at, project, importance. SearchViewModel passes these through to SearchResult for display. (3) Enter in search goes to capture: Changed applySelectedSearchResult() to copy result to clipboard instead of switching to capture mode. Also auto-selects first result when selectedResultIndex is nil. (4) Popover oversized: Reduced contentSize from 420x620 to 360x320. (5) VoiceBar duplicate struct: Merged voicelayer PR #109. Also: Updated busy_timeout test to match PR #160's 30s value. 212 tests, 0 failures. Build and sign pass. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port Python formatting into native Swift renderers. Models for chunk/entity/digest/kg/stats. Shared SearchResultCard and SearchResultsList. 151 tests pass. Requesting review from @coderabbitai @codex @cursor @BugBot @greptileai
Note
Add typed result models and box-drawing TextFormatter for BrainBar MCP tool responses
SearchResult,EntityCard,StatsResult,DigestResult,KGSearchResult) in brain-bar/Sources/BrainBar/Models/ to replace raw dictionary handling across MCP tool handlers.Formattersutility.MCPRouterhandlers (handleBrainSearch,handleBrainRecall,handleBrainEntity,handleBrainDigest) to map database payloads into typed models and format responses viaTextFormatter.BrainDatabase.recallStatsto include distinctprojectsandcontent_typesarrays from the chunks table.SearchResultCardandSearchResultsListSwiftUI components;QuickCapturePanelnow renders search results through these shared components.brain_search,brain_recall,brain_entity, andbrain_digest— clients parsing raw output may see different formatting.📊 Macroscope summarized 4f9fc99. 16 files reviewed, 4 issues evaluated, 0 issues filtered, 3 comments posted
🗂️ Filtered Issues