Skip to content

feat(cue): UI polish — trigger selector, flexible nodes, activity graph, bolt animation#591

Merged
reachraza merged 4 commits intorcfrom
cue-polish
Mar 19, 2026
Merged

feat(cue): UI polish — trigger selector, flexible nodes, activity graph, bolt animation#591
reachraza merged 4 commits intorcfrom
cue-polish

Conversation

@reachraza
Copy link
Contributor

@reachraza reachraza commented Mar 19, 2026

Summary

  • Remove agent.completed from trigger selector — hidden from the TriggerDrawer drag-and-drop list so users can't manually create standalone "Agent Done" trigger nodes. The auto-generated chaining mechanism behind agent-to-agent edges remains fully intact.
  • Flexible trigger node sizing — TriggerNode now uses minWidth: 220px / maxWidth: 320px instead of a fixed width: 220px, gracefully growing to fit longer labels. Truncated text shows the full value via native tooltip on hover.
  • CUE entries in activity graph — The History pane's ActivityGraph now counts and renders CUE entries as a third stacked bar segment (cyan #06b6d4), with tooltip and summary title support. CUE-only bars are clickable.
  • Bolt pulse animation — The Zap icon next to Cue-enabled agents in the left sidebar now pulses (animate-pulse) when the agent is actively running under a Cue pipeline, and stays static when idle. Tooltip updates to "running" vs "active" accordingly.

Test plan

  • npx tsc --noEmit — zero type errors
  • npm run test — all tests pass (349 across affected suites)
  • Open pipeline editor → "Add Trigger" drawer should show 6 trigger types (no "Agent Done")
  • Create a trigger node with a long custom label → node should grow up to 320px, then truncate with tooltip
  • Open History pane → activity graph bars should show cyan segments for CUE entries, hover tooltip includes "Cue" row
  • Run a Cue pipeline → bolt icon in left sidebar should pulse; stops pulsing when run completes

Summary by CodeRabbit

  • New Features

    • Activity graph now tracks and displays Cue counts and shows Cue segments in bars.
    • Visual pulse animation indicates when Cue pipelines are actively running.
  • Improvements

    • Session item Cue tooltip now shows "running" when a Cue is active; subscription counts preserved.
    • Removed "Agent Done" trigger from available trigger options.
    • Unified Cue color applied across history and tooltips.
  • Layout

    • More responsive sizing in the pipeline editor with full-text hover tooltips.

… nodes, activity graph CUE bars, bolt pulse animation

- Remove agent.completed from TriggerDrawer selector (chaining mechanism kept intact)
- TriggerNode: flexible width (220–320px) with title tooltips on label and config summary
- ActivityGraph: include CUE entries as stacked bar segments with cyan color and tooltip
- SessionItem: pulse Zap icon via animate-pulse when Cue pipeline is actively running
- SessionList: propagate activeRuns from CueSessionStatus to drive bolt animation
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

📝 Walkthrough

Walkthrough

Removed the "Agent Done" trigger; added Maestro Cue tracking (counts + running state) across session and history UIs; made TriggerNode layout responsive with truncation tooltips; extended ActivityGraph to aggregate/render CUE counts; and added/updated tests covering these behaviors.

Changes

Cohort / File(s) Summary
Trigger Editor / Tests
src/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.tsx, src/__tests__/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.test.tsx
Removed agent.completed ("Agent Done") from TRIGGER_ITEMS. Tests updated to assert absence, verify exactly 6 draggable items, and assert filter yields "No triggers match" for "agent".
Trigger Node / Tests
src/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsx, src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx
Changed layout to use minWidth: 220 / maxWidth: 320, minWidth: 0 for content, maxWidth: '100%' on header; added title attributes for label/config summary. Tests added for display, truncation tooltips, config interaction, selection styling, and per-event color mapping.
History: ActivityGraph / Constants / Tests
src/renderer/components/History/ActivityGraph.tsx, src/renderer/components/History/historyConstants.tsx, src/__tests__/renderer/components/History/ActivityGraph.test.tsx, src/renderer/components/History/HistoryEntryItem.tsx, src/renderer/components/History/HistoryFilterToggle.tsx
Introduced CUE_COLOR and moved pill/icon helpers to historyConstants. ActivityGraph now tracks cue per bucket, includes cue in totals/max normalization, renders a cyan cue segment and tooltip row, and appends cue counts to top-level title when >0. Tests added/updated for cue tooltip row, title text, color usage, click handling, and bar sizing.
Session Item / Session List / Tests
src/renderer/components/SessionItem.tsx, src/renderer/components/SessionList/SessionList.tsx, src/__tests__/renderer/components/SessionItemCue.test.tsx
cueSessionMap now stores { count, active }. SessionList passes cueSubscriptionCount and cueActiveRun to SessionItem. SessionItem conditionally adds animate-pulse and changes tooltip to "Maestro Cue running" when cueActiveRun is true. Tests added for pulse class and tooltip variants.
History UI helpers / Imports
src/renderer/components/History/historyConstants.tsx, src/renderer/components/History/HistoryEntryItem.tsx, src/renderer/components/History/HistoryFilterToggle.tsx
Extracted getPillColor and getEntryIcon into historyConstants and replaced local implementations; components now import and use these helpers (including CUE color).
Misc tests & config
src/__tests__/renderer/components/..., .coderabbit.yaml
Multiple new/updated tests across renderer components to cover UI/behavior changes; CI config key nesting updated (reviews.auto_review.base_branches).

Sequence Diagram

sequenceDiagram
    participant SL as SessionList
    participant FS as Cue Fetch Service
    participant SM as cueSessionMap (state)
    participant SI as SessionItem
    participant UI as Cue Indicator

    SL->>FS: request cue status for sessionIds
    FS-->>SL: return [{ sessionId, subscriptionCount, activeRuns }, ...]
    SL->>SM: set(sessionId -> { count: subscriptionCount, active: activeRuns > 0 })
    SL->>SI: render(sessionId, cueSubscriptionCount, cueActiveRun)
    SI->>UI: render indicator with count + title
    alt cueActiveRun === true
        UI->>UI: add animate-pulse
        UI->>UI: title "Maestro Cue running (X subscriptions)"
    else
        UI->>UI: no pulse
        UI->>UI: title "Maestro Cue active (X subscriptions)"
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇
I nibbled triggers — one hopped away,
Cues now count and softly sway,
Pulses blink when pipelines run,
Nodes stretch wide to show the sun,
Tests tap paws — the changes play! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: removal of agent.completed from trigger selector, flexible TriggerNode sizing, CUE support in activity graph, and bolt animation for active runs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cue-polish
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@reachraza
Copy link
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps
Copy link

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR delivers four UI-polish features for the CUE pipeline system: hiding agent.completed from the trigger selector, flexible node sizing in the pipeline editor, CUE entry rendering in the activity graph, and a pulse animation on the Zap icon for actively-running CUE pipelines. The changes are well-scoped, all touch confirmed types (activeRuns is present in cue-types.ts), and are backed by 349 new/updated test cases.

Key changes:

  • TriggerDraweragent.completed removed from TRIGGER_ITEMS (6 trigger types remain); auto-generated agent chaining in TriggerNode still handles the event type in EVENT_COLORS/EVENT_ICONS.
  • TriggerNode — Fixed width: 220 replaced with minWidth: 220 / maxWidth: 320; native title tooltips added to label and config-summary spans. The label span lost its direct maxWidth: 110 constraint without gaining min-width: 0, which may prevent text-overflow: ellipsis from firing correctly in the flex-row context for very long labels (the ancestor overflow: hidden will hard-clip instead).
  • ActivityGraph — CUE entries are bucketed and rendered as a cyan (#06b6d4) middle segment in stacked bars; totalCue is included in the summary title when > 0, and the hover tooltip conditionally shows a Cue row. Click handling already included CUE totals.
  • SessionList / SessionItemcueSessionMap widened to store { count, active } derived from s.activeRuns > 0; cueActiveRun prop drives animate-pulse on the Zap wrapper and toggles tooltip text between "running" and "active".

Confidence Score: 4/5

  • Safe to merge — changes are isolated, well-tested, and only carry a minor cosmetic concern around label ellipsis in TriggerNode.
  • All four features are independently scoped, backed by solid test coverage, and reference confirmed types. The only finding is a non-critical flex-layout nuance in TriggerNode where the label span could hard-clip rather than show an ellipsis for very long text — the tooltip mitigates the UX impact.
  • src/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsx — label span flex truncation.

Important Files Changed

Filename Overview
src/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.tsx Clean removal of the agent.completed entry from TRIGGER_ITEMS and the now-unused Zap import; no logic changes.
src/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsx Flexible minWidth/maxWidth sizing works correctly; title tooltips added to both text spans. The label span is missing min-width: 0, which may cause hard-clipping instead of ellipsis truncation for long labels in the row-flex layout.
src/renderer/components/History/ActivityGraph.tsx CUE bucket counting, max-value calculation, click handling, tooltip, and stacked bar segment all wired up correctly. CUE_COLOR constant avoids magic-string duplication.
src/renderer/components/SessionItem.tsx New cueActiveRun prop cleanly toggles animate-pulse class and updates the tooltip between "running" and "active".
src/renderer/components/SessionList/SessionList.tsx cueSessionMap type correctly widened to { count, active }; active is derived from s.activeRuns > 0 which is confirmed present in cue-types.ts. Both the flat and child-session render paths are updated.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[window.maestro.cue.getStatus] -->|activeRuns > 0| B{cueActiveRun = true}
    A -->|activeRuns = 0| C{cueActiveRun = false}

    B --> D["SessionItem\ncueSubscriptionCount prop"]
    C --> D

    D -->|cueActiveRun = true| E["Zap icon\nanimate-pulse\ntooltip: 'running'"]
    D -->|cueActiveRun = false| F["Zap icon\nstatic\ntooltip: 'active'"]

    G[HistoryEntry type = CUE] --> H[ActivityGraph bucket.cue++]
    H --> I[cuePercent = bucket.cue / total * 100]
    I --> J["Cyan bar segment #06b6d4\n(middle, between auto and user)"]
    J --> K{hoveredIndex?}
    K -->|yes| L["Tooltip: Cue row shown\n(only when cue > 0)"]
    K -->|no| M["Summary title:\ntotalCue > 0 ? add ', N cue' : omit"]

    N[TriggerDrawer] -->|agent.completed removed| O[6 trigger types shown]
    N --> P[TriggerNode still handles\nagent.completed in EVENT_COLORS/ICONS]
Loading

Comments Outside Diff (1)

  1. src/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsx, line 122-134 (link)

    P2 Label span missing min-width: 0 — ellipsis may not fire

    The old code used maxWidth: 110 directly on this span to force a constraint and trigger text-overflow: ellipsis. That direct constraint was removed here, but min-width: 0 was not added as a replacement.

    In flexbox, items default to min-width: auto, which prevents a flex item from shrinking below its intrinsic text width (especially with white-space: nowrap). Without either a direct maxWidth or an explicit min-width: 0, the span will expand to fit the full text, so overflow: hidden on the span itself never triggers — long labels will be hard-clipped by the ancestor overflow: hidden on the content div, losing the ellipsis indicator.

    The configSummary span below is a direct column-flex child and gets constrained by maxWidth: '100%', so it behaves correctly. The label span is inside a nested row flex container alongside the icon, so it needs the extra nudge.

Last reviewed commit: "feat(cue): UI polish..."

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/renderer/components/History/ActivityGraph.tsx (1)

6-7: Consider centralizing CUE_COLOR in shared history constants.

This avoids color drift between ActivityGraph, HistoryFilterToggle, and HistoryEntryItem over time.

♻️ Suggested refactor
-/** Hardcoded CUE brand color — matches HistoryFilterToggle and HistoryEntryItem */
-const CUE_COLOR = '#06b6d4';
+// src/renderer/components/History/historyConstants.ts
+export const CUE_COLOR = '#06b6d4';
-import { LOOKBACK_OPTIONS } from './historyConstants';
+import { LOOKBACK_OPTIONS, CUE_COLOR } from './historyConstants';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/History/ActivityGraph.tsx` around lines 6 - 7,
Extract the hardcoded CUE_COLOR constant into a shared history constants module
(export const CUE_COLOR = '#06b6d4') and update ActivityGraph (where CUE_COLOR
is currently defined), HistoryFilterToggle, and HistoryEntryItem to import
CUE_COLOR from that module; ensure all references use the imported symbol so a
single source of truth controls the color and prevents drift.
src/__tests__/renderer/components/History/ActivityGraph.test.tsx (1)

352-352: Scope the "2" assertion to the Cue row to avoid accidental matches.

screen.getByText('2') is global and can become brittle as UI text evolves.

♻️ Suggested test hardening
-import { render, screen, fireEvent, act } from '@testing-library/react';
+import { render, screen, fireEvent, act, within } from '@testing-library/react';
-		expect(screen.getByText('Cue')).toBeInTheDocument();
-		expect(screen.getByText('2')).toBeInTheDocument(); // 2 CUE entries
+		const cueLabel = screen.getByText('Cue');
+		expect(cueLabel).toBeInTheDocument();
+		const cueRow = cueLabel.closest('div');
+		expect(cueRow).not.toBeNull();
+		expect(within(cueRow as HTMLElement).getByText('2')).toBeInTheDocument(); // 2 CUE entries
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/renderer/components/History/ActivityGraph.test.tsx` at line
352, The assertion using screen.getByText('2') is global and brittle; instead
scope it to the Cue row by locating the Cue row first (e.g., use
screen.getByRole('row', { name: /Cue/i }) or find the element that contains
'Cue') and then use within(foundRow).getByText('2') so the '2' is asserted only
inside the Cue row; update the assertion in ActivityGraph.test.tsx that
currently calls screen.getByText('2') to this scoped approach.
src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx (2)

119-123: Strengthen selected-style assertion to avoid false positives.

borderColor string checks against '60' are brittle because style values may be normalized. Compare selected vs unselected root styles directly instead.

Proposed test hardening
 it('should apply selection styling when selected', () => {
-	const { container } = renderTriggerNode({}, true);
-
-	const rootDiv = container.querySelector('div[style*="min-width: 220px"]') as HTMLElement;
-	// Selected nodes have the full color border (not the 60% opacity variant)
-	expect(rootDiv.style.borderColor).not.toContain('60');
-	// Selected nodes have a box shadow
-	expect(rootDiv.style.boxShadow).toBeTruthy();
+	const { container: selectedContainer } = renderTriggerNode({}, true);
+	const { container: unselectedContainer } = renderTriggerNode({}, false);
+
+	const selectedRoot = selectedContainer.querySelector('div[style*="min-width: 220px"]') as HTMLElement;
+	const unselectedRoot = unselectedContainer.querySelector('div[style*="min-width: 220px"]') as HTMLElement;
+
+	expect(selectedRoot.style.boxShadow).toBeTruthy();
+	expect(unselectedRoot.style.boxShadow).toBe('');
+	expect(selectedRoot.style.border).not.toBe(unselectedRoot.style.border);
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx`
around lines 119 - 123, The current test checks selected node styling by
searching for '60' inside rootDiv.style.borderColor which is brittle; instead
query an unselected node (e.g., locate the unselected root element similarly to
how rootDiv is found), read its computed styles for borderColor and boxShadow
(or rootDiv.style for inline styles used in the component), and assert that
rootDiv.style.borderColor !== unselectedRoot.style.borderColor and that
rootDiv.style.boxShadow !== unselectedRoot.style.boxShadow (or that rootDiv has
a truthy boxShadow while the unselected one is falsy) so the test directly
compares selected vs unselected styles using the element identifiers rootDiv and
the newly located unselectedRoot.

126-134: Add agent.completed to the event-color test matrix.

TriggerNode still supports this event type, so excluding it here leaves one runtime path untested.

Small coverage patch
 const eventColors: Record<string, string> = {
 	'time.heartbeat': '#f59e0b',
 	'time.scheduled': '#8b5cf6',
 	'file.changed': '#3b82f6',
+	'agent.completed': '#22c55e',
 	'github.pull_request': '#a855f7',
 	'github.issue': '#f97316',
 	'task.pending': '#06b6d4',
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx`
around lines 126 - 134, The test matrix in the "should use correct color for
each event type" test omits the 'agent.completed' event; update the eventColors
object in TriggerNode.test.tsx to include an 'agent.completed' key with the same
color string used by the TriggerNode implementation for that event. Locate the
test's eventColors variable and add 'agent.completed':
'<color-from-TriggerNode>' (use the exact hex/color literal from the TriggerNode
code) so the runtime path for agent.completed is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx`:
- Around line 119-123: The current test checks selected node styling by
searching for '60' inside rootDiv.style.borderColor which is brittle; instead
query an unselected node (e.g., locate the unselected root element similarly to
how rootDiv is found), read its computed styles for borderColor and boxShadow
(or rootDiv.style for inline styles used in the component), and assert that
rootDiv.style.borderColor !== unselectedRoot.style.borderColor and that
rootDiv.style.boxShadow !== unselectedRoot.style.boxShadow (or that rootDiv has
a truthy boxShadow while the unselected one is falsy) so the test directly
compares selected vs unselected styles using the element identifiers rootDiv and
the newly located unselectedRoot.
- Around line 126-134: The test matrix in the "should use correct color for each
event type" test omits the 'agent.completed' event; update the eventColors
object in TriggerNode.test.tsx to include an 'agent.completed' key with the same
color string used by the TriggerNode implementation for that event. Locate the
test's eventColors variable and add 'agent.completed':
'<color-from-TriggerNode>' (use the exact hex/color literal from the TriggerNode
code) so the runtime path for agent.completed is covered.

In `@src/__tests__/renderer/components/History/ActivityGraph.test.tsx`:
- Line 352: The assertion using screen.getByText('2') is global and brittle;
instead scope it to the Cue row by locating the Cue row first (e.g., use
screen.getByRole('row', { name: /Cue/i }) or find the element that contains
'Cue') and then use within(foundRow).getByText('2') so the '2' is asserted only
inside the Cue row; update the assertion in ActivityGraph.test.tsx that
currently calls screen.getByText('2') to this scoped approach.

In `@src/renderer/components/History/ActivityGraph.tsx`:
- Around line 6-7: Extract the hardcoded CUE_COLOR constant into a shared
history constants module (export const CUE_COLOR = '#06b6d4') and update
ActivityGraph (where CUE_COLOR is currently defined), HistoryFilterToggle, and
HistoryEntryItem to import CUE_COLOR from that module; ensure all references use
the imported symbol so a single source of truth controls the color and prevents
drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 892aaa6a-833a-4300-858c-d789061b4cdd

📥 Commits

Reviewing files that changed from the base of the PR and between 2d86e2e and 9b73dd9.

📒 Files selected for processing (9)
  • src/__tests__/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.test.tsx
  • src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx
  • src/__tests__/renderer/components/History/ActivityGraph.test.tsx
  • src/__tests__/renderer/components/SessionItemCue.test.tsx
  • src/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.tsx
  • src/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsx
  • src/renderer/components/History/ActivityGraph.tsx
  • src/renderer/components/SessionItem.tsx
  • src/renderer/components/SessionList/SessionList.tsx

…ertions

- Extract CUE_COLOR into historyConstants.tsx; update ActivityGraph,
  HistoryFilterToggle, and HistoryEntryItem to import from single source
- TriggerNode selection test: compare selected vs unselected styles directly
  instead of brittle string containment check
- TriggerNode color test: add agent.completed to event type coverage matrix
- ActivityGraph CUE bucket test: scope count assertion to Cue row via within()
@reachraza
Copy link
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/renderer/components/History/ActivityGraph.tsx (1)

308-315: Consider showing Cue row consistently in tooltip.

Auto and User rows display unconditionally (even when 0), but Cue only appears when cue > 0. This asymmetry may be intentional for a "new feature" feel, but for consistency you could show all three rows always.

♻️ Optional: Show Cue row unconditionally
-						{bucketData[hoveredIndex].cue > 0 && (
-							<div className="flex items-center justify-between gap-3">
-								<span style={{ color: CUE_COLOR }}>Cue</span>
-								<span className="font-bold" style={{ color: CUE_COLOR }}>
-									{bucketData[hoveredIndex].cue}
-								</span>
-							</div>
-						)}
+						<div className="flex items-center justify-between gap-3">
+							<span style={{ color: CUE_COLOR }}>Cue</span>
+							<span className="font-bold" style={{ color: CUE_COLOR }}>
+								{bucketData[hoveredIndex].cue}
+							</span>
+						</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/History/ActivityGraph.tsx` around lines 308 - 315,
The tooltip currently conditionally renders the Cue row only when
bucketData[hoveredIndex].cue > 0, which creates inconsistency with Auto and User
rows; update the JSX in ActivityGraph.tsx to render the Cue row unconditionally
(like Auto/User) by removing the conditional and always outputting the <div>
that uses bucketData[hoveredIndex].cue and CUE_COLOR so the Cue label and value
appear even when the value is 0. Ensure you reference bucketData, hoveredIndex
and CUE_COLOR when making the change.
src/renderer/components/History/HistoryEntryItem.tsx (1)

9-50: Consider extracting shared getPillColor and getEntryIcon helpers.

Both HistoryFilterToggle.tsx and this file define identical getPillColor and getEntryIcon functions. These could be co-located with CUE_COLOR in historyConstants.tsx to reduce duplication.

♻️ Proposed consolidation

In historyConstants.tsx:

+import { Bot, User, Zap } from 'lucide-react';
+import type { Theme, HistoryEntryType } from '../../types';
+
+export const getPillColor = (type: HistoryEntryType, theme: Theme) => {
+	switch (type) {
+		case 'AUTO':
+			return { bg: theme.colors.warning + '20', text: theme.colors.warning, border: theme.colors.warning + '40' };
+		case 'USER':
+			return { bg: theme.colors.accent + '20', text: theme.colors.accent, border: theme.colors.accent + '40' };
+		case 'CUE':
+			return { bg: CUE_COLOR + '20', text: CUE_COLOR, border: CUE_COLOR + '40' };
+		default:
+			return { bg: theme.colors.bgActivity, text: theme.colors.textDim, border: theme.colors.border };
+	}
+};
+
+export const getEntryIcon = (type: HistoryEntryType) => {
+	switch (type) {
+		case 'AUTO': return Bot;
+		case 'USER': return User;
+		case 'CUE': return Zap;
+		default: return Bot;
+	}
+};

Then import from both components.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/History/HistoryEntryItem.tsx` around lines 9 - 50,
Extract the duplicated getPillColor and getEntryIcon implementations into
historyConstants.tsx (next to CUE_COLOR), export them, and replace the local
copies in HistoryEntryItem and HistoryFilterToggle with imports; ensure
historyConstants.tsx also imports/export the lucide icons (Bot, User, Zap) and
the types (Theme, HistoryEntryType) used by getPillColor/getEntryIcon so callers
can simply import { getPillColor, getEntryIcon } and remove the duplicated
functions from those components.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/renderer/components/History/ActivityGraph.tsx`:
- Around line 308-315: The tooltip currently conditionally renders the Cue row
only when bucketData[hoveredIndex].cue > 0, which creates inconsistency with
Auto and User rows; update the JSX in ActivityGraph.tsx to render the Cue row
unconditionally (like Auto/User) by removing the conditional and always
outputting the <div> that uses bucketData[hoveredIndex].cue and CUE_COLOR so the
Cue label and value appear even when the value is 0. Ensure you reference
bucketData, hoveredIndex and CUE_COLOR when making the change.

In `@src/renderer/components/History/HistoryEntryItem.tsx`:
- Around line 9-50: Extract the duplicated getPillColor and getEntryIcon
implementations into historyConstants.tsx (next to CUE_COLOR), export them, and
replace the local copies in HistoryEntryItem and HistoryFilterToggle with
imports; ensure historyConstants.tsx also imports/export the lucide icons (Bot,
User, Zap) and the types (Theme, HistoryEntryType) used by
getPillColor/getEntryIcon so callers can simply import { getPillColor,
getEntryIcon } and remove the duplicated functions from those components.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95497b4f-9b13-44df-b1d8-a205a148c452

📥 Commits

Reviewing files that changed from the base of the PR and between 9b73dd9 and 2033ca5.

📒 Files selected for processing (6)
  • src/__tests__/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx
  • src/__tests__/renderer/components/History/ActivityGraph.test.tsx
  • src/renderer/components/History/ActivityGraph.tsx
  • src/renderer/components/History/HistoryEntryItem.tsx
  • src/renderer/components/History/HistoryFilterToggle.tsx
  • src/renderer/components/History/historyConstants.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/renderer/components/History/historyConstants.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tests/renderer/components/History/ActivityGraph.test.tsx
  • src/tests/renderer/components/CuePipelineEditor/nodes/TriggerNode.test.tsx

…w unconditionally

- Extract getPillColor and getEntryIcon into historyConstants.tsx; remove
  duplicated implementations from HistoryFilterToggle and HistoryEntryItem
- Render Cue row unconditionally in ActivityGraph tooltip (consistent with
  Auto/User rows that always show even when count is 0)
- Update test to assert Cue row shows with zero count instead of being absent
@reachraza
Copy link
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/components/History/ActivityGraph.tsx (1)

141-145: ⚠️ Potential issue | 🟠 Major

Make the newly-clickable bars keyboard-accessible.

Including cue in the bucket total makes CUE-only buckets actionable, but the bars are still mouse-only clickable divs. Keyboard users still can't focus or trigger that drill-down path. Prefer a <button> here, or add tabIndex={0}, outline-none, an accessible label, and Enter/Space handling that mirrors the existing click behavior.

As per coding guidelines, src/renderer/components/**/*.tsx: For focus issues, add tabIndex={0} or tabIndex={-1} and add outline-none class.

Also applies to: 324-345

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/History/ActivityGraph.tsx` around lines 141 - 145,
ActivityGraph makes bars clickable via the onBarClick handler when total
(bucketData[index].auto + bucketData[index].user + bucketData[index].cue) > 0
but the bars are plain divs and not keyboard accessible; update the interactive
bar element (where you call getBucketTimeRange(index) and invoke onBarClick) to
be keyboard-focusable and operable—either replace the div with a semantic
<button> or add tabIndex={0}, className="outline-none", role="button", an
accessible aria-label describing the bucket time range, and keyDown handlers
that trigger the same logic on Enter/Space (mirror the click behavior); apply
the same changes to the other bar block referenced around lines 324-345 so both
mouse and keyboard users can drill down via onBarClick.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/renderer/components/History/ActivityGraph.tsx`:
- Around line 141-145: ActivityGraph makes bars clickable via the onBarClick
handler when total (bucketData[index].auto + bucketData[index].user +
bucketData[index].cue) > 0 but the bars are plain divs and not keyboard
accessible; update the interactive bar element (where you call
getBucketTimeRange(index) and invoke onBarClick) to be keyboard-focusable and
operable—either replace the div with a semantic <button> or add tabIndex={0},
className="outline-none", role="button", an accessible aria-label describing the
bucket time range, and keyDown handlers that trigger the same logic on
Enter/Space (mirror the click behavior); apply the same changes to the other bar
block referenced around lines 324-345 so both mouse and keyboard users can drill
down via onBarClick.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a53cff7b-07ad-46ac-8ec7-c717c9ff8a93

📥 Commits

Reviewing files that changed from the base of the PR and between 2033ca5 and b48b639.

📒 Files selected for processing (6)
  • .coderabbit.yaml
  • src/__tests__/renderer/components/History/ActivityGraph.test.tsx
  • src/renderer/components/History/ActivityGraph.tsx
  • src/renderer/components/History/HistoryEntryItem.tsx
  • src/renderer/components/History/HistoryFilterToggle.tsx
  • src/renderer/components/History/historyConstants.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/tests/renderer/components/History/ActivityGraph.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/renderer/components/History/HistoryFilterToggle.tsx
  • src/renderer/components/History/HistoryEntryItem.tsx
  • src/renderer/components/History/historyConstants.tsx

@reachraza reachraza merged commit df6a800 into rc Mar 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant