feat: V1 signal bars side-by-side (horizontal)#522
Conversation
📝 WalkthroughWalkthroughDashboard write activity is split into agent-originating and watcher-originating time-series buckets in ChangesMulti-series write split and signal coverage refactor
Sequence DiagramsequenceDiagram
participant BrainDatabase
participant DashboardStats
participant DashboardFlowLane
participant SparklineChartPresentation
participant SparklineChart
BrainDatabase->>BrainDatabase: recentWriteBuckets(agentWriteSourceWhereClause)
BrainDatabase->>BrainDatabase: recentWriteBuckets(watcherWriteSourceWhereClause)
BrainDatabase->>DashboardStats: init(recentAgentWriteBuckets, recentWatcherWriteBuckets)
DashboardStats-->>DashboardStats: vectorNetDrainRatePerHour, vectorBacklogETAHours
DashboardStats->>DashboardFlowLane: Writes lane(secondaryValues: agentBuckets, tertiaryValues: watcherBuckets)
DashboardFlowLane->>SparklineChartPresentation: init(secondaryValues, tertiaryValues, labels, accents)
SparklineChartPresentation-->>SparklineChart: enabledRoles, legend entries, tooltipText
SparklineChart->>SparklineChart: draw SparklineSeriesPathShape per role
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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.
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.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f1902db. Configure here.
| for (index, signal) in signals.enumerated() { | ||
| DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.06) { | ||
| withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) { | ||
| _ = revealedSignalIDs.insert(signal.id) |
There was a problem hiding this comment.
Uncancelled reveal stagger callbacks
Medium Severity
updateRevealedSignals schedules per-signal updates with DispatchQueue.main.asyncAfter but never cancels them or checks isExpanded when they run. Collapsing and re-expanding quickly can leave stale callbacks that insert into revealedSignalIDs during a new stagger, so signal bars may pop in early or out of sequence.
Reviewed by Cursor Bugbot for commit f1902db. Configure here.
| withAnimation(.easeOut(duration: 0.6)) { | ||
| lineRevealProgress = 1 | ||
| } | ||
| } |
There was a problem hiding this comment.
Line reveal state unused
Low Severity
SparklineChart animates lineRevealProgress in onAppear, but chartBody never reads that state, so the intended line-draw reveal never affects rendering and the chart appears at full length immediately.
Reviewed by Cursor Bugbot for commit f1902db. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1902dbf3e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| now: now | ||
| ), | ||
| values: stats.recentActivityBuckets, | ||
| values: stats.recentAgentWriteBuckets, |
There was a problem hiding this comment.
Preserve all write sources in the ingress sparkline
When recent writes come only from sources outside the two new split buckets (for example Quick Capture writes use source: "quick-capture" in QuickCaptureController.swift), recentWriteCount still drives the card status/rate but the plotted primary/secondary arrays are both zero, so the Writes card can say there are recent writes while the chart shows “Listening for writes...”. Please keep an “other/all writes” series or include every counted write source in one of these buckets.
Useful? React with 👍 / 👎.
| var vectorNetDrainRatePerHour: Double { | ||
| // Vector rows do not currently carry per-vector timestamps. Until | ||
| // they do, use the real dashboard window: recent enrichment | ||
| // completions minus recent writes, which is the net backlog drain | ||
| // signal available in DashboardStats. | ||
| max(recentEnrichmentRatePerHour - recentWriteRatePerHour, 0) |
There was a problem hiding this comment.
Base vector ETA on vector progress, not enrichment
In environments where enrichment is completing but embedding/vector indexing is stuck or disabled, this reports a positive Vector drain rate even though vectorIndexedChunkCount from chunk_vectors_rowids has not moved, causing the Vector detail panel to show “falling” and a finite ETA for a backlog that is not actually draining. Please derive this from vector-index deltas or leave it unavailable until vector progress is measured.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift (1)
304-365: 🧹 Nitpick | 🔵 Trivial | 💤 Low value
lineRevealProgressstate is animated but never read.The
lineRevealProgressstate is initialized, animated inonAppear, but never consumed in the view body orchartBody. This appears to be dead code from a planned but incomplete line-reveal animation feature.🧹 Remove unused state if line reveal animation is not needed
- `@State` private var lineRevealProgress: CGFloat = 1 ... - .onAppear { - if reduceMotion { - lineRevealProgress = 1 - } else { - lineRevealProgress = 0 - withAnimation(.easeOut(duration: 0.6)) { - lineRevealProgress = 1 - } - } - }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift` around lines 304 - 365, The lineRevealProgress state variable declared in SparklineRenderer is initialized and animated in the onAppear block but is never actually used in the view body or chartBody, making it dead code. Remove the `@State` private var lineRevealProgress declaration and delete the animation logic in the onAppear block that sets lineRevealProgress to 0 and animates it to 1, keeping only the essential appearance setup if any other logic remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift`:
- Around line 847-864: The updateRevealedSignals function uses
DispatchQueue.main.asyncAfter to stagger animations, but these pending
dispatches are not cancelled if isExpanded toggles quickly or the view
disappears, causing unexpected state mutations. Replace the
DispatchQueue.main.asyncAfter approach with a Task-based implementation that
stores a reference to the task as a property, cancels any pending task before
starting a new staggered reveal sequence, and checks for task cancellation
within the animation closure to prevent modifications to revealedSignalIDs when
the task has been cancelled.
In `@brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift`:
- Around line 496-521: The computed property chartForegroundStyleScale in
SparklineRenderer is no longer used because the chart now uses custom
SparklineSeriesPathShape drawing instead of Swift Charts' built-in styling.
Remove the entire chartForegroundStyleScale computed property definition from
the class, including all its logic for building the KeyValuePairs with different
color combinations based on whether secondary and tertiary series are present.
---
Outside diff comments:
In `@brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift`:
- Around line 304-365: The lineRevealProgress state variable declared in
SparklineRenderer is initialized and animated in the onAppear block but is never
actually used in the view body or chartBody, making it dead code. Remove the
`@State` private var lineRevealProgress declaration and delete the animation logic
in the onAppear block that sets lineRevealProgress to 0 and animates it to 1,
keeping only the essential appearance setup if any other logic remains.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3dae0122-369b-478a-95fc-f2fe73b133e2
📒 Files selected for processing (6)
brain-bar/Sources/BrainBar/BrainBarWindowRootView.swiftbrain-bar/Sources/BrainBar/BrainDatabase.swiftbrain-bar/Sources/BrainBar/Dashboard/PipelineState.swiftbrain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swiftbrain-bar/Sources/BrainBar/DesignTokens.swiftbrain-bar/Tests/BrainBarTests/DashboardTests.swift
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: Macroscope - Correctness Check
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-18T00:12:08.774Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 87
File: brain-bar/Sources/BrainBar/BrainBarServer.swift:118-129
Timestamp: 2026-03-18T00:12:08.774Z
Learning: In Swift files under brain-bar/Sources/BrainBar, enforce that when a critical dependency like the database is nil due to startup ordering (socket before DB), any tool handler that accesses the database must throw an explicit error (e.g., ToolError.noDatabase) instead of returning a default/empty value. Do not allow silent defaults (e.g., guard let db else { return ... }). Flag patterns that silently return defaults when db is nil, as this masks startup timing issues. This guidance applies broadly to similar Swift files in the BrainBar module, not just this one location.
Applied to files:
brain-bar/Sources/BrainBar/DesignTokens.swiftbrain-bar/Sources/BrainBar/BrainDatabase.swiftbrain-bar/Sources/BrainBar/Dashboard/PipelineState.swiftbrain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swiftbrain-bar/Sources/BrainBar/BrainBarWindowRootView.swift
📚 Learning: 2026-03-29T18:45:40.988Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 133
File: brain-bar/Sources/BrainBar/BrainDatabase.swift:0-0
Timestamp: 2026-03-29T18:45:40.988Z
Learning: In the BrainBar module’s Swift database layer (notably BrainDatabase.swift), ensure that the `search()` function’s `unreadOnly=true` path orders results by the delivery frontier cursor so the watermark `maxRowID` stays contiguous. Specifically, when `unreadOnly` is enabled, the query must include `ORDER BY c.rowid ASC` (e.g., via `let orderByClause = unreadOnly ? "c.rowid ASC" : "f.rank"`). Do not replace the unread-only ordering with relevance-based sorting (e.g., `f.rank`) unconditionally or for the unread-only path, as it can introduce gaps in the watermark and incorrectly mark unseen rows as delivered. Flag any future change to the `ORDER BY` clause in this function that makes relevance sorting apply to the unread-only case.
Applied to files:
brain-bar/Sources/BrainBar/DesignTokens.swiftbrain-bar/Sources/BrainBar/BrainDatabase.swiftbrain-bar/Sources/BrainBar/BrainBarWindowRootView.swift
🪛 SwiftLint (0.63.3)
brain-bar/Sources/BrainBar/BrainDatabase.swift
[Warning] 127-127: Prefer empty collection over optional collection
(discouraged_optional_collection)
[Warning] 128-128: Prefer empty collection over optional collection
(discouraged_optional_collection)
🔇 Additional comments (36)
brain-bar/Sources/BrainBar/BrainDatabase.swift (1)
32-33: LGTM!Also applies to: 100-101, 127-128, 153-154, 177-183, 194-214, 268-269, 1688-1699, 1716-1717, 1906-1921
brain-bar/Sources/BrainBar/DesignTokens.swift (1)
31-38: LGTM!Also applies to: 172-178
brain-bar/Sources/BrainBar/Dashboard/PipelineState.swift (1)
197-203: LGTM!Also applies to: 235-236, 333-343, 396-403
brain-bar/Tests/BrainBarTests/DashboardTests.swift (4)
245-279: LGTM!
281-324: LGTM!
326-344: LGTM!
786-816: LGTM!brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift (15)
14-23: LGTM!
25-59: LGTM!
71-94: LGTM!
95-113: LGTM!
115-119: LGTM!
121-131: LGTM!
133-149: LGTM!
151-153: LGTM!
155-200: LGTM!
233-250: LGTM!
298-321: LGTM!
367-427: LGTM!
429-465: LGTM!
523-543: LGTM!
564-592: LGTM!brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift (14)
491-505: LGTM!
694-698: LGTM!
708-845: LGTM!
875-876: LGTM!
897-925: LGTM!
928-970: LGTM!
993-993: LGTM!Also applies to: 1004-1050
1071-1074: LGTM!
1091-1138: LGTM!
1275-1329: LGTM!
1476-1485: LGTM!
1544-1546: LGTM!
1667-1696: LGTM!
1724-1759: LGTM!
| private func updateRevealedSignals(animated: Bool) { | ||
| guard isExpanded else { | ||
| revealedSignalIDs.removeAll() | ||
| return | ||
| } | ||
| if reduceMotion || !animated { | ||
| revealedSignalIDs = Set(signals.map(\.id)) | ||
| return | ||
| } | ||
| revealedSignalIDs.removeAll() | ||
| for (index, signal) in signals.enumerated() { | ||
| DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.06) { | ||
| withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) { | ||
| _ = revealedSignalIDs.insert(signal.id) | ||
| } | ||
| } label: { | ||
| BrainBarSignalCoverageRow( | ||
| signal: signal, | ||
| compact: compact, | ||
| isSelected: isVectorDetailExpanded | ||
| ) | ||
| } | ||
| .buttonStyle(.plain) | ||
| .help("Show Vector backlog details") | ||
| } else { | ||
| BrainBarSignalCoverageRow(signal: signal, compact: compact, isSelected: false) | ||
| } | ||
| } |
There was a problem hiding this comment.
Staggered reveal dispatches are not cancelled on view disappear or rapid re-toggle.
If isExpanded toggles quickly or the view disappears while the staggered asyncAfter dispatches are pending, the closures will still execute and modify revealedSignalIDs. This could cause unexpected state mutations or animation glitches.
Consider using a Task-based approach with cancellation, or add a flag to track view presence.
🛡️ Proposed fix using task cancellation
+ `@State` private var revealTask: Task<Void, Never>?
+
private func updateRevealedSignals(animated: Bool) {
+ revealTask?.cancel()
guard isExpanded else {
revealedSignalIDs.removeAll()
return
}
if reduceMotion || !animated {
revealedSignalIDs = Set(signals.map(\.id))
return
}
revealedSignalIDs.removeAll()
- for (index, signal) in signals.enumerated() {
- DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.06) {
- withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) {
- _ = revealedSignalIDs.insert(signal.id)
+ revealTask = Task { `@MainActor` in
+ for (index, signal) in signals.enumerated() {
+ guard !Task.isCancelled else { return }
+ try? await Task.sleep(nanoseconds: UInt64(index) * 60_000_000)
+ guard !Task.isCancelled else { return }
+ withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) {
+ _ = revealedSignalIDs.insert(signal.id)
}
}
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift` around lines 847 -
864, The updateRevealedSignals function uses DispatchQueue.main.asyncAfter to
stagger animations, but these pending dispatches are not cancelled if isExpanded
toggles quickly or the view disappears, causing unexpected state mutations.
Replace the DispatchQueue.main.asyncAfter approach with a Task-based
implementation that stores a reference to the task as a property, cancels any
pending task before starting a new staggered reveal sequence, and checks for
task cancellation within the animation closure to prevent modifications to
revealedSignalIDs when the task has been cancelled.
| private var chartForegroundStyleScale: KeyValuePairs<String, Color> { | ||
| let primaryColor = color(for: .primary) | ||
| let secondaryColor = color(for: .secondary) | ||
| let tertiaryColor = color(for: .tertiary) | ||
|
|
||
| if presentation.hasSecondarySeries && presentation.hasTertiarySeries { | ||
| return [ | ||
| presentation.primaryLegendLabel: primaryColor, | ||
| presentation.secondaryLegendLabel: secondaryColor, | ||
| presentation.tertiaryLegendLabel: tertiaryColor, | ||
| ] | ||
| } | ||
| if presentation.hasSecondarySeries { | ||
| return [ | ||
| presentation.primaryLegendLabel: primaryColor, | ||
| presentation.secondaryLegendLabel: secondaryColor, | ||
| ] | ||
| } | ||
| if presentation.hasTertiarySeries { | ||
| return [ | ||
| presentation.primaryLegendLabel: primaryColor, | ||
| presentation.tertiaryLegendLabel: tertiaryColor, | ||
| ] | ||
| } | ||
| return [presentation.primaryLegendLabel: primaryColor] | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for usages of chartForegroundStyleScale
rg -n "chartForegroundStyleScale" brain-bar/Repository: EtanHey/brainlayer
Length of output: 201
Remove unused chartForegroundStyleScale computed property.
This property builds a KeyValuePairs for Swift Charts' foregroundStyle scale, but the chart now uses custom SparklineSeriesPathShape drawing and the property is never referenced in the codebase.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift` around lines
496 - 521, The computed property chartForegroundStyleScale in SparklineRenderer
is no longer used because the chart now uses custom SparklineSeriesPathShape
drawing instead of Swift Charts' built-in styling. Remove the entire
chartForegroundStyleScale computed property definition from the class, including
all its logic for building the KeyValuePairs with different color combinations
based on whether secondary and tertiary series are present.
| var seriesComponents = ["\(primarySeriesLabel ?? "Primary") \(primaryValue)"] | ||
| if let secondarySeriesLabel, hasSecondarySeries { | ||
| let value = secondaryValues.indices.contains(clampedBucket) ? secondaryValues[clampedBucket] : 0 | ||
| seriesComponents.append("\(secondarySeriesLabel) \(value)") | ||
| } | ||
| if let tertiarySeriesLabel, hasTertiarySeries { | ||
| let value = tertiaryValues.indices.contains(clampedBucket) ? tertiaryValues[clampedBucket] : 0 | ||
| seriesComponents.append("\(tertiarySeriesLabel) \(value)") | ||
| } |
There was a problem hiding this comment.
🟢 Low Dashboard/SparklineRenderer.swift:240
tooltipText(forBucket:) silently drops the secondary and tertiary series from the tooltip when secondarySeriesLabel or tertiarySeriesLabel is nil, even though the corresponding values arrays are non-empty. The if let secondarySeriesLabel guard requires a non-nil label, but legendEntries adds those same series based only on hasSecondarySeries/hasTertiarySeries and uses the fallback properties secondaryLegendLabel/tertiaryLegendLabel. The primary series in this same function already uses primarySeriesLabel ?? "Primary" as a fallback. Replace the if let bindings with if hasSecondarySeries { ... secondaryLegendLabel ... } and if hasTertiarySeries { ... tertiaryLegendLabel ... } for consistency.
- var seriesComponents = ["\(primarySeriesLabel ?? "Primary") \(primaryValue)"]
- if let secondarySeriesLabel, hasSecondarySeries {
+ var seriesComponents = ["\(primaryLegendLabel) \(primaryValue)"]
+ if hasSecondarySeries {
let value = secondaryValues.indices.contains(clampedBucket) ? secondaryValues[clampedBucket] : 0
- seriesComponents.append("\(secondarySeriesLabel) \(value)")
+ seriesComponents.append("\(secondaryLegendLabel) \(value)")
}
- if let tertiarySeriesLabel, hasTertiarySeries {
+ if hasTertiarySeries {
let value = tertiaryValues.indices.contains(clampedBucket) ? tertiaryValues[clampedBucket] : 0
- seriesComponents.append("\(tertiarySeriesLabel) \(value)")
+ seriesComponents.append("\(tertiaryLegendLabel) \(value)")
}🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift around lines 240-248:
`tooltipText(forBucket:)` silently drops the secondary and tertiary series from the tooltip when `secondarySeriesLabel` or `tertiarySeriesLabel` is `nil`, even though the corresponding values arrays are non-empty. The `if let secondarySeriesLabel` guard requires a non-nil label, but `legendEntries` adds those same series based only on `hasSecondarySeries`/`hasTertiarySeries` and uses the fallback properties `secondaryLegendLabel`/`tertiaryLegendLabel`. The primary series in this same function already uses `primarySeriesLabel ?? "Primary"` as a fallback. Replace the `if let` bindings with `if hasSecondarySeries { ... secondaryLegendLabel ... }` and `if hasTertiarySeries { ... tertiaryLegendLabel ... }` for consistency.
Evidence trail:
brain-bar/Sources/BrainBar/Dashboard/SparklineRenderer.swift lines 30-32 (optional labels), lines 83-89 (hasSecondarySeries/hasTertiarySeries), lines 95-105 (legendEntries uses hasSecondarySeries with fallback labels), lines 121-131 (primaryLegendLabel/secondaryLegendLabel/tertiaryLegendLabel with ?? fallbacks), lines 233-249 (tooltipText uses `if let secondarySeriesLabel` guard but primary uses `?? "Primary"` fallback).


Etan's iterative fix on V1 (#520): lay the 3 signal bars (Vector/FTS5/Trigram) in a horizontal row instead of stacked vertically (matches the V1.png mockup). 11-line layout change (VStack→HStack), behavior/health-colors/progressive-disclosure unchanged. NSHostingView-verified at full/compact + vector-detail; 62 DashboardTests pass.
Note
Medium Risk
Changes add SQL source filters and heuristic vector ETA math that could misread backlog if rates are noisy; scope is mostly dashboard read paths and UI with new tests.
Overview
This PR extends the BrainBar V1 dashboard with layout, styling, and richer metrics—not only the horizontal signal bars from the title.
Pipeline & signal coverage UI: The pipeline panel puts writes and signal coverage above the enrichments/queue row (queue moves beside enrichments instead of full width below). Signal coverage gets a section header, ViewThatFits horizontal/vertical columns for Vector/FTS5/Trigram, dedicated signal colors, animated coverage bars (with reduce-motion support), staggered expand animations, and Vector detail nested under the Vector column. Card styling and typography are tightened across flow lanes and overview stats.
Data & writes chart:
DashboardStatsnow buckets writes by source into Agent stores vs JSONL watcher time series, plus computed vector net drain and backlog ETA from enrichment vs write rates (replacing hardcoded Vector detail placeholders). The ingress sparkline plots dual series with a legend, dimmed inactive sources, and a “Listening for writes…” empty state; rendering moves from Swift Charts to a custom multi-series path inSparklineRenderer.Tests: New dashboard/sparkline tests cover source splitting, palette labels, vector ETA math, and legend/caption behavior.
Reviewed by Cursor Bugbot for commit f1902db. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add horizontal signal bars, multi-series sparklines, and animated coverage bars to the Brain Bar dashboard
SparklineChartfrom Swift Charts to a customGeometryReader/ZStackrenderer supporting up to three labeled series with per-series markers, hover tooltips, and a 'Listening for writes...' caption when no activity is present.recentAgentWriteBuckets/recentWatcherWriteBucketsstats, computed inBrainDatabase.swiftwith source-filtered SQL queries and hourly rate/backlog ETA derived values.BrainBarSignalCoverageRowwith a newBrainBarAnimatedCoverageBarthat spring-animates fill on appear and percent changes, respectingaccessibilityReduceMotion.BrainBarSignalCoveragePanel, and wiresBrainBarVectorSignalDetailto live drain rate and ETA from dashboard stats.BrainBarSeriesLegendfor multi-series charts and introduces new design tokens for signal and series colors inDesignTokens.swift.SparklineChartno longer uses Swift Charts; hover and animation behavior are now fully custom.📊 Macroscope summarized f1902db. 5 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.
Summary by CodeRabbit
New Features
Tests