Skip to content

feat(desktop): v2 review tab — PR info, checks, comments#3463

Merged
AviPeltz merged 14 commits intomainfrom
review-v2-screen
Apr 15, 2026
Merged

feat(desktop): v2 review tab — PR info, checks, comments#3463
AviPeltz merged 14 commits intomainfrom
review-v2-screen

Conversation

@AviPeltz
Copy link
Copy Markdown
Collaborator

@AviPeltz AviPeltz commented Apr 14, 2026

Summary

  • Adds a Review tab to the v2 workspace sidebar, replacing the "Checks — Coming soon" stub
  • Shows PR header (title, state icon, review decision badge), collapsible CI checks with status icons/links, and PR comments with avatars and previews
  • Clicking a comment opens a Comment pane that renders the full markdown with styled tables, images, <details> blocks, and a "Copy All" button
  • Uses v2-native host-service endpoints (git.getPullRequest, git.getPullRequestThreads) instead of v1 electron endpoints
  • Comment pane reuses existing tab when clicking between comments, with GitHub link in the pane header

Test plan

  • Open a v2 workspace with a GitHub PR
  • Verify the Review tab shows PR title, review decision, checks, and comments
  • Click a comment — verify it opens in a pane with rendered markdown
  • Click another comment — verify it replaces the existing comment pane
  • Verify "Copy All" copies the raw markdown to clipboard
  • Verify the GitHub icon in the pane header links to the comment on GitHub
  • Verify table "Copy" buttons work
  • Verify the Review tab badge shows 0 when no PR or comments exist
  • Verify checks show correct pass/fail/pending status icons

Summary by cubic

Adds a Review tab to the v2 workspace sidebar, replacing the “Checks — Coming soon” stub. It shows PR info, CI checks, and PR comments; clicking a comment opens a Markdown Comment pane.

  • New Features

    • PR header with title, state icon, and review decision; link to GitHub.
    • Collapsible Checks with status/summary icons, durations, and links; pending icons spin.
    • Comments list with avatars, age, preview; copy one or “Copy all”; click opens a single reusable Comment pane.
    • Comment pane renders Markdown with tables, images, details, mermaid diagrams, and code highlighting; tables are copyable and the header links to GitHub; mermaid uses Streamdown with a dark-contrast theme and hides block chrome.
    • Uses workspaceTrpc.git.getPullRequest and workspaceTrpc.git.getPullRequestThreads from @superset/workspace-client with normalized data; Review tab badge shows open comment count (including 0), and sidebar badges render zero counts.
  • Bug Fixes

    • Prevent comments from not loading until refresh by removing stale caching and enabling threads only after PR data is ready; composite keys for duplicate check names and a single-pass checks summary.
    • Show “Unable to load review status” on tRPC failures.
    • Guard state updates after unmount and clear timeouts; add .catch to all clipboard actions; fallback icon when no avatar; minor accessibility labels.
    • Extract a stable code-block renderer to avoid remounts on theme change and keep mermaid/code rendering smooth.

Written for commit e6253be. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added a full "Review" tab showing PR header, collapsible checks summary, and comments (active/resolved).
    • Click a comment to open a Comment pane with full Markdown rendering, table-copy, and "Copy All".
    • Per-comment actions: copy single comment, open in browser, resolve/unresolve threads, and "Mark all done".
  • Bug Fixes

    • Sidebar tab badge now displays whenever a badge value is present (including zero).
  • Documentation

    • Added design/spec plan for the v2 Review tab.

Port the v1 Review tab functionality into the v2 workspace sidebar,
replacing the "Checks — Coming soon" stub. Uses v2-native host-service
endpoints (git.getPullRequest, git.getPullRequestThreads) instead of
v1 electron endpoints.

Features:
- PR header with title, state icon, review decision badge
- Collapsible CI checks section with status icons and links
- PR comments with avatars, timestamps, and preview text
- Copy individual comments or copy all to clipboard
- Comment pane: click a comment to view rendered markdown in a tab
- GitHub link in pane header to open comment on GitHub
- Copyable tables in rendered markdown
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a v2 Workspace Sidebar "Review" tab and CommentPane: new useReviewTab hook, normalized types, PR/checks/comments UI components, pane registration and opening logic, comment pane renderer with Markdown and copy features, CSS, and page wiring to open/reuse comment panes. No backend/API changes.

Changes

Cohort / File(s) Summary
Spec / Plan
apps/desktop/plans/20260413-1600-v2-review-tab.md
New ExecPlan documenting Review tab design, fetch flow, UI composition, interactions, file layout, and validation steps.
Sidebar integration
apps/desktop/src/renderer/.../WorkspaceSidebar/WorkspaceSidebar.tsx, apps/desktop/src/renderer/.../SidebarHeader/SidebarHeader.tsx
Added optional onOpenComment prop; replaced static Checks stub with reviewTab from useReviewTab; badge rendering adjusted to show any non-null badge value.
Review hook & types
.../hooks/useReviewTab/useReviewTab.tsx, .../hooks/useReviewTab/types.ts, .../hooks/useReviewTab/index.ts
New useReviewTab hook with tRPC queries for PR and threads, normalization for PR/checks/comments, checks status coercion and aggregation, unresolved-count badge, and exported normalized types.
Review tab components
.../useReviewTab/components/PRHeader/*, .../ChecksSection/*, .../CommentsSection/*, .../ReviewTabContent/*
Added PRHeader, ChecksSection (collapsible, filtered, status/duration/linking), CommentsSection (active/resolved lists, copy single/all, open comment), and ReviewTabContent (memoized container with loading/error/empty states).
Pane registry & CommentPane
.../hooks/usePaneRegistry/usePaneRegistry.tsx, .../CommentPane/CommentPane.tsx, .../CommentPane/comment-pane.css, .../CommentPane/index.ts
Registered "comment" pane (icon/title/renderer/header extras); added CommentPane with author metadata, Markdown (GFM + sanitize), per-table CopyableTable behavior, copy-to-clipboard via tRPC, and CSS styles.
Types & page wiring
.../types.ts, .../page.tsx
Added CommentPaneData type and included it in PaneViewerData; implemented openCommentPane in page to reuse/create comment panes and wired onOpenComment into WorkspaceSidebar.

Sequence Diagram

sequenceDiagram
    participant User
    participant Sidebar as "Workspace Sidebar\n(Review Tab)"
    participant Hook as "useReviewTab Hook"
    participant tRPC as "Electron tRPC"
    participant PaneRegistry as "Pane Registry"
    participant CommentPane as "CommentPane"

    User->>Sidebar: Open Review tab
    Sidebar->>Hook: mount → fetch PR and threads
    Hook->>tRPC: getPullRequest(workspaceId)
    Hook->>tRPC: getPullRequestThreads(workspaceId)
    tRPC-->>Hook: PR + threads
    Hook->>Hook: normalize PR, checks, comments
    Hook-->>Sidebar: render ReviewTabContent(pr, comments)
    User->>Sidebar: Click comment (open)
    Sidebar->>PaneRegistry: open/reuse "comment" pane with CommentPaneData
    PaneRegistry->>CommentPane: render CommentPane
    User->>CommentPane: Click "Copy All"
    CommentPane->>tRPC: copyText(payload)
    tRPC-->>CommentPane: success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped in with a cheerful tap,
A Review tab bloomed upon the app,
Checks that spin and comments to share,
Copy, open panes — I twitched my ear with flair,
A tiny hop, the sidebar sings: hooray for review care!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.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
Title check ✅ Passed The title clearly and concisely describes the main feature: adding a Review tab to the v2 workspace that displays PR info, checks, and comments.
Description check ✅ Passed PR description includes all required template sections with clear implementation details, test plan, and motivation.

✏️ 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 review-v2-screen

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.

- Fix memory leaks: add timeout cleanup refs in CommentPane and
  CopyableTable, clear previous timeout on rapid clicks
- Add error state: show "Unable to load review status" when tRPC
  query fails instead of infinite loading spinner
- Fix getIcon fallback: show MessageSquare icon when avatarUrl is
  missing instead of rendering <img src={undefined}>
- Add aria-label to comment open button for screen readers
- Add aria-hidden to decorative arrow icon in PRHeader
- Add group-focus-visible:opacity-100 to PRHeader arrow for keyboard nav
@AviPeltz AviPeltz marked this pull request as ready for review April 15, 2026 00:15
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 15, 2026

Greptile Summary

This PR ships the v2 workspace Review tab — replacing the "Checks — Coming soon" stub with a full-featured panel showing the PR header (title, state, review decision), collapsible CI checks with pass/fail/pending icons, and a threaded comments list. Clicking a comment opens a CommentPane that renders full GFM markdown (including <details> blocks and tables) with Copy All and per-table copy helpers. The pane reuses an existing tab when navigating between comments. Data is sourced from the v2 workspaceTrpc host-service endpoints (git.getPullRequest, git.getPullRequestThreads) with normalization helpers to map the raw shapes into a stable UI model.

  • One confirmed bug: The "Comments" section header badge (commentsCountLabel) uses comments.length — the total including resolved comments — but the collapsible only renders activeComments. This produces a misleading count (e.g., "Comments 5" with only 3 items visible). Fix: use activeComments.length instead.
  • Badge condition change: SidebarHeader now renders badges for any non-null value (including 0), which is intentional for the Review tab's always-visible count. Other tabs are unaffected since useChangesTab returns undefined (not 0) when there are no changes.
  • No GitHub link for review thread comments: The host-service PullRequestReviewComment type does not expose a url field (unlike conversation comments which have htmlUrl), so the "Open on GitHub" row action and pane-header link won't appear for review comments. The code handles this correctly, but it's a UX gap worth addressing upstream in the host-service router.
  • The markdown rendering pipeline (rehypeRawrehypeSanitize) is in the correct order and is safe against XSS.
  • Timeout cleanup in CommentPane and CommentsSection is handled properly via useEffect cleanup.
  • The openCommentPane reuse logic (searching all tabs for an existing comment pane) is correct Zustand usage.

Confidence Score: 4/5

Safe to merge after fixing the commentsCountLabel count mismatch; all other concerns are P2 or informational.

One P1 logic bug (commentsCountLabel showing total instead of active count) is a one-line fix and doesn't break core functionality — comments still display correctly, just the badge count in the header is off. The rest of the implementation is well-structured, correctly handles edge cases (loading/error/no-PR states, rapid clicks, timer cleanup), uses safe markdown rendering, and follows the existing v2 hook pattern. No data loss, security, or runtime error risks.

CommentsSection.tsx — commentsCountLabel count mismatch; SidebarHeader.tsx — badge condition semantics change worth verifying against all current tabs.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx Renders collapsible active/resolved comment lists. Bug: commentsCountLabel uses comments.length (total including resolved) but the section only renders activeComments, producing a misleading count badge. Should use activeComments.length.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx Core data hook: fetches PR + thread data via v2 host-service endpoints, normalizes shapes, and returns a SidebarTabDefinition. Normalization logic for check status coercion and thread flattening is sound; review thread comments hardcode url: undefined (correct given the API contract, but creates a UX gap for GitHub links).
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx Badge rendering condition relaxed (tab.badge > 0 → tab.badge != null) to support the Review tab showing "0". Safe given existing tabs never return badge: 0, but broadens semantics for future tabs.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx New CommentPane renders full markdown (GFM, raw HTML sanitized via rehypeSanitize, details/summary) with copy-all and per-table copy; timer cleanup on unmount is correct; rehypeRaw+rehypeSanitize plugin order is safe.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx Adds comment pane entry to the registry with avatar icon, author title, renderHeaderExtras GitHub link, and Close Comment context menu label. All fields are correctly typed and handled.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx Adds openCommentPane callback that reuses an existing comment pane across all tabs (updating its data) or opens a new tab; wired to WorkspaceSidebar via onOpenComment prop. Logic is correct.

Sequence Diagram

sequenceDiagram
    participant WS as WorkspaceSidebar
    participant URT as useReviewTab
    participant TRPC as workspaceTrpc
    participant RTC as ReviewTabContent
    participant CS as CommentsSection
    participant CP as CommentPane
    participant PR as page.tsx (openCommentPane)

    WS->>URT: useReviewTab({ workspaceId, onOpenComment })
    URT->>TRPC: git.getPullRequest (10s interval)
    TRPC-->>URT: V2PullRequest | null
    URT->>TRPC: git.getPullRequestThreads (30s interval, enabled if PR exists)
    TRPC-->>URT: { reviewThreads[], conversationComments[] }
    URT->>URT: normalizeThreadsToComments()
    URT->>URT: openCommentCount = unresolved.length
    URT-->>WS: SidebarTabDefinition { id:review, badge:openCommentCount, content }

    WS->>RTC: render(pr, comments, isLoading, isError)
    RTC->>CS: render(comments, isLoading, onOpenComment)
    CS->>CS: activeComments = comments.filter(!isResolved)
    CS->>CS: resolvedComments = comments.filter(isResolved)

    note over CS,PR: User clicks a comment row
    CS->>PR: onOpenComment(CommentPaneData)
    PR->>PR: search state.tabs for existing comment pane
    alt Existing comment pane found
        PR->>PR: setPaneData(paneId, comment)
        PR->>PR: setActiveTab + setActivePane
    else No existing pane
        PR->>PR: addTab with kind=comment
    end
    PR->>CP: render CommentPane with updated data
    CP->>CP: ReactMarkdown + rehypeRaw + rehypeSanitize
Loading

Reviews (1): Last reviewed commit: "fix(desktop): address review tab PR feed..." | Re-trigger Greptile

});
}, [copyToClipboard, activeComments, markCopied]);

const commentsCountLabel = isLoading ? "..." : comments.length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 "Comments" badge count includes resolved comments

commentsCountLabel uses comments.length (total = active + resolved combined), but the "Comments" collapsible section only renders activeComments. This creates a confusing mismatch — e.g., the header shows "Comments 5" while only 3 rows are visible, with the remaining 2 appearing under the separate "Resolved" section.

Change it to activeComments.length so the count matches what's actually displayed:

Suggested change
const commentsCountLabel = isLoading ? "..." : comments.length;
const commentsCountLabel = isLoading ? "..." : activeComments.length;

Comment on lines 30 to 32
)}
>
{tab.label}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge condition change now shows "0" for all tabs

Removing the tab.badge > 0 guard means any tab returning badge: 0 will now render the badge span. The useChangesTab hook uses badge: totalChanges > 0 ? totalChanges : undefined (never 0), so there's no regression from existing tabs. However, useReviewTab always returns badge: openCommentCount — including 0 — which is the driver for this change.

The intent is clearly to show "0" on the Review tab when there are no open comments. If that's the desired UX, the change is correct. It's worth noting that any future tab returning badge: 0 will also display it, which may be surprising. A narrower approach would have useReviewTab return badge: openCommentCount only when pr != null and let callers stay consistent:

Suggested change
)}
>
{tab.label}
{tab.badge != null && tab.badge > 0 && (

…and instead ensure useReviewTab always returns a value (even 0) only when a PR is active. This avoids making the global badge condition permanently lenient.

Comment on lines +181 to +196
for (const thread of data.reviewThreads) {
const first = thread.comments[0];
if (!first) continue;
comments.push({
id: first.id,
authorLogin: first.author.login,
avatarUrl: first.author.avatarUrl || undefined,
body: first.body,
createdAt: first.createdAt,
url: undefined,
kind: "review",
path: thread.path || undefined,
line: thread.line ?? undefined,
isResolved: thread.isResolved,
threadId: thread.id,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Review thread comments have no GitHub link (API limitation)

url: undefined is hardcoded for every review thread comment. This is correct given the PullRequestReviewComment interface in the host-service (id, databaseId, author, body, createdAt — no url field), so no code change is needed.

However, this means the "Open on GitHub" action button will never appear in CommentRow for review comments, and the GitHub icon link in the CommentPane header will also be absent. Conversation comments (htmlUrl) correctly get the link, but review comments don't — which can be a noticeable UX gap since review comments make up the bulk of code-review feedback. Consider whether the host-service router can be updated to include the url (GitHub returns html_url on review comments via the REST API), so users can navigate to the exact review comment on GitHub.

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 5

🧹 Nitpick comments (4)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx (1)

122-130: Consider adding a brief comment explaining the CodeRabbit special case.

The resolveCheckUrl function has special handling for "coderabbit" checks to redirect to the PR URL. A brief comment would help future maintainers understand this intentional fallback.

📝 Optional documentation
 function resolveCheckUrl(
 	check: NormalizedCheck,
 	prUrl: string,
 ): string | undefined {
 	if (check.url) return check.url;
+	// CodeRabbit checks don't provide direct URLs; link to PR instead
 	const name = check.name.trim().toLowerCase();
 	if (name.includes("coderabbit") || name.includes("code rabbit")) return prUrl;
 	return undefined;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx
around lines 122 - 130, The resolveCheckUrl function contains a special-case
fallback that routes checks whose name contains "coderabbit" or "code rabbit" to
the PR URL but lacks an explanatory comment; add a short inline comment above or
within resolveCheckUrl (referencing resolveCheckUrl, NormalizedCheck, check.url,
check.name, and prUrl) explaining that CodeRabbit checks intentionally fallback
to the PR URL when no explicit check.url is provided so future maintainers
understand this behavior.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/PRHeader/PRHeader.tsx (1)

31-48: Consider adding an accessible name to the PR link.

The link wrapping the PR title relies on the visible title text for its accessible name. For better screen reader experience, consider adding an explicit aria-label that includes "Open PR on GitHub" or similar context.

♻️ Optional improvement
 			<a
 				href={pr.url}
 				target="_blank"
 				rel="noopener noreferrer"
 				className="group flex items-center gap-1.5 cursor-pointer"
+				aria-label={`Open pull request: ${pr.title} on GitHub`}
 			>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/PRHeader/PRHeader.tsx
around lines 31 - 48, The anchor wrapping the PR title (in PRHeader.tsx around
the <a> using pr.url and pr.title with PRIcon and LuArrowUpRight) lacks an
explicit accessible name; update the anchor to include an aria-label like "Open
PR on GitHub: {pr.title}" (or "Open PR: {pr.title}") so screen readers get clear
context while preserving existing children and attributes.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx (2)

305-312: Avoid rendering a no-op action button when pane opening is unavailable.

onOpen is optional, but the row is always a clickable <button>. If onOpen is undefined, this creates a focusable control with no action. Consider rendering non-interactive content (or disabling the button) in that state.

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
around lines 305 - 312, The current row always renders an interactive <button>
that calls handleClick, but onOpen is optional so the control can be focusable
with no action; update the render in CommentsSection so when onOpen is undefined
it does not render a clickable button — either render a non-interactive
container (e.g., a <div> or <span>) showing {content} with the same layout and
aria-label, or render the <button> with disabled and appropriate aria-disabled
state; ensure handleClick only exists when onOpen is present and reference the
existing identifiers (handleClick, onOpen, content, comment.authorLogin) when
changing the JSX.

243-344: Split CommentRow into its own file.

This file defines multiple React components under a components path. Please move CommentRow into its own CommentRow/CommentRow.tsx (with barrel export) and keep CommentsSection.tsx focused on the section component.

As per coding guidelines, **/components/**/*.tsx: "Keep one component per file; no multi-component files."

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
around lines 243 - 344, Extract the CommentRow component and its CommentRowProps
interface into a new folder CommentRow/CommentRow.tsx and export it (named
export CommentRow); create a barrel file CommentRow/index.ts that re-exports the
component, then update the original CommentsSection.tsx to import { CommentRow }
from './CommentRow'. Move all local helper and UI imports used by CommentRow
(formatShortAge, getPreviewText, Avatar, AvatarImage, AvatarFallback, LuCheck,
LuCopy, LuArrowUpRight) into the new file so it remains self-contained, and
ensure the onOpen, onCopy prop signatures and the normalized comment shape
(NormalizedComment/CommentPaneData) are imported or re-exported as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx:
- Around line 113-115: The rendering key for CheckRow is currently just
check.name which can duplicate; update either the NormalizedCheck type to
include a unique id from the raw API (e.g., checkRunId) and use that as the key
in relevantChecks.map, or add a stable fallback by combining name with the map
index (e.g., `${check.name}-${index}`) when mapping in the ChecksSection
component so keys are unique; adjust where NormalizedCheck is constructed to
populate the id if you choose the first option and update CheckRow props
accordingly.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:
- Around line 61-70: The copy handlers (e.g., handleCopySingle and the
multiple-copy handler using copyToClipboard and markCopied) currently only
handle successful promise resolution; update them to explicitly handle rejected
promises by adding a .catch or try/catch around copyToClipboard and call an
error-handling path (e.g., markCopyFailed or processLogger/errorToast) with
context like `comment:${comment.id}` or relevant workspace id so users get
feedback on failure; ensure you still call markCopied on success and do not
swallow errors silently.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx:
- Around line 75-80: The tab's error UI only uses prQuery.isError so failures
from threadsQuery (git.getPullRequestThreads) are shown as "no comments" instead
of an error; update the ReviewTabContent props in useReviewTab to propagate the
threads query failure by combining errors (e.g., set isError to prQuery.isError
|| threadsQuery.isError) and, if applicable, pass threadsQuery.error to
ReviewTabContent or an appropriate prop so the component can render the error
state for threads as well.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx:
- Around line 34-43: The then() callback in handleCopyAll can set state after
unmount; to fix, add a mounted ref (e.g., const mountedRef = useRef(true)) and
in a useEffect cleanup set mountedRef.current = false and clear copyTimerRef if
present; then in the promise resolution inside handleCopyAll (after
electronTrpcClient.external.copyText.mutate(...).then(...)) guard calls to
setCopied and setting copyTimerRef with if (!mountedRef.current) return; and
ensure the timeout is cleared in the cleanup so copyTimerRef doesn't outlive the
component.
- Around line 115-138: The handleCopy callback in CommentPane may call setCopied
and manipulate timerRef after the component unmounts; add a mounted ref (e.g.,
isMountedRef) initialized true in a useEffect and set to false on cleanup, then
in handleCopy's .then() guard all state/timer actions with if
(!isMountedRef.current) return; also ensure the effect cleanup clears timerRef
(clearTimeout) to avoid leaks; update references to tableRef, timerRef,
setCopied, and electronTrpcClient.external.copyText.mutate in this change.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx:
- Around line 122-130: The resolveCheckUrl function contains a special-case
fallback that routes checks whose name contains "coderabbit" or "code rabbit" to
the PR URL but lacks an explanatory comment; add a short inline comment above or
within resolveCheckUrl (referencing resolveCheckUrl, NormalizedCheck, check.url,
check.name, and prUrl) explaining that CodeRabbit checks intentionally fallback
to the PR URL when no explicit check.url is provided so future maintainers
understand this behavior.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:
- Around line 305-312: The current row always renders an interactive <button>
that calls handleClick, but onOpen is optional so the control can be focusable
with no action; update the render in CommentsSection so when onOpen is undefined
it does not render a clickable button — either render a non-interactive
container (e.g., a <div> or <span>) showing {content} with the same layout and
aria-label, or render the <button> with disabled and appropriate aria-disabled
state; ensure handleClick only exists when onOpen is present and reference the
existing identifiers (handleClick, onOpen, content, comment.authorLogin) when
changing the JSX.
- Around line 243-344: Extract the CommentRow component and its CommentRowProps
interface into a new folder CommentRow/CommentRow.tsx and export it (named
export CommentRow); create a barrel file CommentRow/index.ts that re-exports the
component, then update the original CommentsSection.tsx to import { CommentRow }
from './CommentRow'. Move all local helper and UI imports used by CommentRow
(formatShortAge, getPreviewText, Avatar, AvatarImage, AvatarFallback, LuCheck,
LuCopy, LuArrowUpRight) into the new file so it remains self-contained, and
ensure the onOpen, onCopy prop signatures and the normalized comment shape
(NormalizedComment/CommentPaneData) are imported or re-exported as needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/PRHeader/PRHeader.tsx:
- Around line 31-48: The anchor wrapping the PR title (in PRHeader.tsx around
the <a> using pr.url and pr.title with PRIcon and LuArrowUpRight) lacks an
explicit accessible name; update the anchor to include an aria-label like "Open
PR on GitHub: {pr.title}" (or "Open PR: {pr.title}") so screen readers get clear
context while preserving existing children and attributes.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93b39d55-5836-4d4c-b48d-2c3c3d09ab47

📥 Commits

Reviewing files that changed from the base of the PR and between 2bf1049 and 9ac590b.

📒 Files selected for processing (20)
  • apps/desktop/plans/20260413-1600-v2-review-tab.md
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/PRHeader/PRHeader.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/PRHeader/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ReviewTabContent/ReviewTabContent.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ReviewTabContent/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/types.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/comment-pane.css
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/types.ts

Comment on lines +75 to +80
<ReviewTabContent
pr={pr}
comments={comments}
isLoading={prQuery.isLoading}
isError={prQuery.isError}
isCommentsLoading={threadsQuery.isLoading}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Propagate thread-query failures into the tab error state.

Right now only prQuery.isError drives the error UI. If git.getPullRequestThreads fails, the tab can degrade into “no comments” instead of surfacing an error.

Suggested fix
 		<ReviewTabContent
 			pr={pr}
 			comments={comments}
 			isLoading={prQuery.isLoading}
-			isError={prQuery.isError}
+			isError={prQuery.isError || threadsQuery.isError}
 			isCommentsLoading={threadsQuery.isLoading}
 			onOpenComment={onOpenComment}
 		/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ReviewTabContent
pr={pr}
comments={comments}
isLoading={prQuery.isLoading}
isError={prQuery.isError}
isCommentsLoading={threadsQuery.isLoading}
<ReviewTabContent
pr={pr}
comments={comments}
isLoading={prQuery.isLoading}
isError={prQuery.isError || threadsQuery.isError}
isCommentsLoading={threadsQuery.isLoading}
onOpenComment={onOpenComment}
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx
around lines 75 - 80, The tab's error UI only uses prQuery.isError so failures
from threadsQuery (git.getPullRequestThreads) are shown as "no comments" instead
of an error; update the ReviewTabContent props in useReviewTab to propagate the
threads query failure by combining errors (e.g., set isError to prQuery.isError
|| threadsQuery.isError) and, if applicable, pass threadsQuery.error to
ReviewTabContent or an appropriate prop so the component can render the error
state for threads as well.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

6 issues found across 20 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx:35">
P2: Handle copyText failures explicitly; this promise chain currently has no rejection handler and can trigger unhandled promise rejections in the renderer.

(Based on your team's feedback about handling async rejections explicitly.) [FEEDBACK_USED]</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx:130">
P2: Add rejection handling for the table copy promise to avoid unhandled promise rejections on copy failures.

(Based on your team's feedback about handling async rejections explicitly.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:63">
P2: Handle copy failures explicitly to avoid unhandled promise rejections.

(Based on your team's feedback about handling async errors explicitly and avoiding unhandled rejections.) [FEEDBACK_USED]</violation>

<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:99">
P2: `commentsCountLabel` uses `comments.length` (total including resolved), but the "Comments" collapsible section only renders `activeComments`. This creates a confusing mismatch — e.g., the header shows "5" while only 3 rows are visible. Use `activeComments.length` so the count matches the displayed rows.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx:79">
P2: Thread-query failures are not surfaced in the error state. If `git.getPullRequestThreads` fails, the tab silently degrades to showing "no comments" instead of displaying the error UI. Include `threadsQuery.isError` in the error condition.</violation>
</file>

<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx:114">
P2: Using `check.name` as the sole React key is fragile. GitHub allows multiple check runs with the same name (e.g., from different workflows or retries), which would produce duplicate keys and cause rendering issues. Use a composite key like `` `${check.name}-${index}` `` or include a unique ID from the API.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

});
}, [copyToClipboard, activeComments, markCopied]);

const commentsCountLabel = isLoading ? "..." : comments.length;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 15, 2026

Choose a reason for hiding this comment

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

P2: commentsCountLabel uses comments.length (total including resolved), but the "Comments" collapsible section only renders activeComments. This creates a confusing mismatch — e.g., the header shows "5" while only 3 rows are visible. Use activeComments.length so the count matches the displayed rows.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx, line 99:

<comment>`commentsCountLabel` uses `comments.length` (total including resolved), but the "Comments" collapsible section only renders `activeComments`. This creates a confusing mismatch — e.g., the header shows "5" while only 3 rows are visible. Use `activeComments.length` so the count matches the displayed rows.</comment>

<file context>
@@ -0,0 +1,344 @@
+		});
+	}, [copyToClipboard, activeComments, markCopied]);
+
+	const commentsCountLabel = isLoading ? "..." : comments.length;
+	const copyAllLabel =
+		copiedActionKey === "comments:all" ? "Copied" : "Copy all";
</file context>
Fix with Cubic

pr={pr}
comments={comments}
isLoading={prQuery.isLoading}
isError={prQuery.isError}
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 15, 2026

Choose a reason for hiding this comment

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

P2: Thread-query failures are not surfaced in the error state. If git.getPullRequestThreads fails, the tab silently degrades to showing "no comments" instead of displaying the error UI. Include threadsQuery.isError in the error condition.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx, line 79:

<comment>Thread-query failures are not surfaced in the error state. If `git.getPullRequestThreads` fails, the tab silently degrades to showing "no comments" instead of displaying the error UI. Include `threadsQuery.isError` in the error condition.</comment>

<file context>
@@ -0,0 +1,220 @@
+			pr={pr}
+			comments={comments}
+			isLoading={prQuery.isLoading}
+			isError={prQuery.isError}
+			isCommentsLoading={threadsQuery.isLoading}
+			onOpenComment={onOpenComment}
</file context>
Fix with Cubic

Prevents unhandled promise rejections in the renderer if
electronTrpcClient.external.copyText.mutate() fails.
Add isMountedRef to both CommentPane and CopyableTable to prevent
setCopied calls after unmount when the async clipboard IPC resolves
after the component is gone.
Copy link
Copy Markdown
Contributor

@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 (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx (1)

249-350: Extract CommentRow into its own component file.

CommentsSection.tsx currently contains two components (CommentsSection and CommentRow). Please split CommentRow into its own folder/file (CommentRow/CommentRow.tsx + index.ts) and import it here.

Suggested refactor sketch
- interface CommentRowProps {
-   ...
- }
-
- function CommentRow(...) {
-   ...
- }
+ import { CommentRow } from "../CommentRow";
// CommentRow/CommentRow.tsx
export interface CommentRowProps {
  comment: NormalizedComment;
  copiedActionKey: string | null;
  onCopy: (comment: NormalizedComment) => void;
  onOpen?: (comment: CommentPaneData) => void;
}

export function CommentRow(props: CommentRowProps) {
  // moved implementation
}
// CommentRow/index.ts
export { CommentRow } from "./CommentRow";
export type { CommentRowProps } from "./CommentRow";

As per coding guidelines **/components/**/*.tsx: “Keep one component per file; no multi-component files.”

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
around lines 249 - 350, Separate the CommentRow component into its own module:
create a new file that exports the CommentRow function and its
CommentRowProps/NormalizedComment/CommentPaneData types (matching the interface
and implementation currently inside CommentsSection), move the entire CommentRow
implementation there, and add an index export that re-exports CommentRow and its
props type; then update the original file to import { CommentRow } (and the
props type if needed) and remove the in-file CommentRow definition so only
CommentsSection remains. Ensure the moved component preserves handleClick,
onCopy, copiedActionKey logic, avatar/rendering JSX, and aria labels exactly as
in the original.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:
- Around line 249-350: Separate the CommentRow component into its own module:
create a new file that exports the CommentRow function and its
CommentRowProps/NormalizedComment/CommentPaneData types (matching the interface
and implementation currently inside CommentsSection), move the entire CommentRow
implementation there, and add an index export that re-exports CommentRow and its
props type; then update the original file to import { CommentRow } (and the
props type if needed) and remove the in-file CommentRow definition so only
CommentsSection remains. Ensure the moved component preserves handleClick,
onCopy, copiedActionKey logic, avatar/rendering JSX, and aria labels exactly as
in the original.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ef65b68-1f1d-4669-b50b-0807aa7efd85

📥 Commits

Reviewing files that changed from the base of the PR and between 9ac590b and 515bd34.

📒 Files selected for processing (2)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx

Copy link
Copy Markdown
Contributor

@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)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx (2)

16-16: Use a tsconfig alias import for types instead of a deep relative path.

Line 16 is brittle with ../../../../types, especially since this file already uses alias-style imports (e.g., renderer/...) elsewhere.

As per coding guidelines, apps/desktop/**/*.{ts,tsx}: Use alias as defined in tsconfig.json when possible.

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx
at line 16, Replace the brittle deep-relative import of the types in
CommentPane.tsx (currently importing CommentPaneData and PaneViewerData via
"../../../../types") with the project tsconfig alias used elsewhere (e.g., the
same "renderer/..." alias pattern), updating the import to reference the aliased
path that exports CommentPaneData and PaneViewerData so the file uses the
canonical tsconfig alias instead of "../../../../types".

107-181: Split CopyableTable into its own component file.

This file currently defines multiple components (CommentPane and CopyableTable). Please move CopyableTable to its own component folder/file and import it here.

♻️ Suggested structure change
-const commentComponents = {
-	table: ({ children }: { children?: ReactNode }) => (
-		<CopyableTable>{children}</CopyableTable>
-	),
-};
-
-function CopyableTable({ children }: { children?: ReactNode }) {
-  ...
-}
+import { CopyableTable } from "./CopyableTable";
+
+const commentComponents = {
+	table: ({ children }: { children?: ReactNode }) => (
+		<CopyableTable>{children}</CopyableTable>
+	),
+};
// CommentPane/CopyableTable/CopyableTable.tsx
export function CopyableTable({ children }: { children?: ReactNode }) {
  // moved unchanged logic
}
// CommentPane/CopyableTable/index.ts
export { CopyableTable } from "./CopyableTable";

As per coding guidelines, **/components/**/*.tsx: Keep one component per file; no multi-component files and Organize components with one folder per component: ComponentName/ComponentName.tsx + index.ts for barrel export.

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx
around lines 107 - 181, The file defines two components (CommentPane and
CopyableTable); split CopyableTable into its own component folder/file, export
it from CommentPane/CopyableTable/index.ts, and import it into the original
file. Specifically, move the CopyableTable function (including its hooks:
useRef, useState, useEffect, useCallback and handleCopy logic and JSX) into
CommentPane/CopyableTable/CopyableTable.tsx, add a barrel export in
CommentPane/CopyableTable/index.ts, update the original file to import {
CopyableTable } and keep commentComponents.table referencing CopyableTable
unchanged, and ensure any references to electronTrpcClient and React types
remain imported in the new file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx:
- Line 16: Replace the brittle deep-relative import of the types in
CommentPane.tsx (currently importing CommentPaneData and PaneViewerData via
"../../../../types") with the project tsconfig alias used elsewhere (e.g., the
same "renderer/..." alias pattern), updating the import to reference the aliased
path that exports CommentPaneData and PaneViewerData so the file uses the
canonical tsconfig alias instead of "../../../../types".
- Around line 107-181: The file defines two components (CommentPane and
CopyableTable); split CopyableTable into its own component folder/file, export
it from CommentPane/CopyableTable/index.ts, and import it into the original
file. Specifically, move the CopyableTable function (including its hooks:
useRef, useState, useEffect, useCallback and handleCopy logic and JSX) into
CommentPane/CopyableTable/CopyableTable.tsx, add a barrel export in
CommentPane/CopyableTable/index.ts, update the original file to import {
CopyableTable } and keep commentComponents.table referencing CopyableTable
unchanged, and ensure any references to electronTrpcClient and React types
remain imported in the new file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8afcd95-1373-4df1-a3b8-1d8e2f541655

📥 Commits

Reviewing files that changed from the base of the PR and between 515bd34 and b633fcc.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/CommentPane/CommentPane.tsx

The threads query had staleTime: 30_000 which cached empty results
when the query fired before the host-service had PR data synced.
Remove staleTime so every mount/refocus fetches fresh data (background
polling at 30s still runs via refetchInterval).

Also tighten the enabled condition to use prQuery.isSuccess instead
of !!prQuery.data for clearer intent, and use composite key for
CheckRow to handle duplicate check names.
Copy link
Copy Markdown
Contributor

@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.

♻️ Duplicate comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx (1)

113-115: ⚠️ Potential issue | 🟠 Major

Replace the index-based key with a stable check identifier.

Line 115 still uses index in the key. This is unstable on reordering and also trips lint/suspicious/noArrayIndexKey.

Suggested direction
- relevantChecks.map((check, index) => (
+ relevantChecks.map((check) => (
    <CheckRow
-     key={`${check.name}-${index}`}
+     key={check.id}
      check={check}
      prUrl={prUrl}
    />
  ))

If NormalizedCheck currently has no id, add one during normalization (e.g., check run id) and thread it through.

#!/bin/bash
set -euo pipefail

CHECKS_FILE='apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx'
HOOK_ROOT='apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab'

echo "1) Verify index-based key usage in ChecksSection:"
rg -n 'key=\{`\$\{check\.name\}-\$\{index\}`\}' "$CHECKS_FILE"

echo
echo "2) Inspect NormalizedCheck type for a stable id field:"
rg -n --type ts --type tsx 'interface NormalizedCheck|type NormalizedCheck|id\s*:' "$HOOK_ROOT"

echo
echo "3) Locate check normalization points to confirm whether id can be propagated:"
rg -n --type ts --type tsx 'normalize|normalized|checks.*map|checkRun|statusCheck' "$HOOK_ROOT"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx
around lines 113 - 115, The CheckRow list uses a fragile key
`${check.name}-${index}`; replace the index with a stable identifier: ensure the
NormalizedCheck type includes a unique id (e.g., checkRun id) at normalization
time, propagate that id through relevantChecks, and change the key on CheckRow
to use that stable field (e.g., key={check.id} or
key={`${check.id}-${check.name}`}) in the component rendering loop that maps
relevantChecks to CheckRow.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx (1)

136-175: Split CheckRow into its own component file.

Line 136 introduces a second component in ChecksSection.tsx. Please move CheckRow to its own folder/file and re-export via index.ts to match repo component structure rules.

As per coding guidelines, "Keep one component per file; no multi-component files."

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx
around lines 136 - 175, Move the CheckRow component out of ChecksSection.tsx
into its own component folder (e.g., a new folder named CheckRow) with a file
like CheckRow.tsx that exports the CheckRow React component (retain usage of
checkIconConfig, resolveCheckUrl, NormalizedCheck typing and LuArrowUpRight
import as needed). Add an index.ts that re-exports CheckRow as the default or
named export to match repo conventions, then replace the inline CheckRow
definition in ChecksSection.tsx with an import from the new module and ensure
props and usage (check, prUrl) remain unchanged. Run a quick type/build check to
fix any missing imports (cn, checkIconConfig, resolveCheckUrl, LuArrowUpRight)
and update exports/import paths accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx:
- Around line 113-115: The CheckRow list uses a fragile key
`${check.name}-${index}`; replace the index with a stable identifier: ensure the
NormalizedCheck type includes a unique id (e.g., checkRun id) at normalization
time, propagate that id through relevantChecks, and change the key on CheckRow
to use that stable field (e.g., key={check.id} or
key={`${check.id}-${check.name}`}) in the component rendering loop that maps
relevantChecks to CheckRow.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx:
- Around line 136-175: Move the CheckRow component out of ChecksSection.tsx into
its own component folder (e.g., a new folder named CheckRow) with a file like
CheckRow.tsx that exports the CheckRow React component (retain usage of
checkIconConfig, resolveCheckUrl, NormalizedCheck typing and LuArrowUpRight
import as needed). Add an index.ts that re-exports CheckRow as the default or
named export to match repo conventions, then replace the inline CheckRow
definition in ChecksSection.tsx with an import from the new module and ensure
props and usage (check, prUrl) remain unchanged. Run a quick type/build check to
fix any missing imports (cn, checkIconConfig, resolveCheckUrl, LuArrowUpRight)
and update exports/import paths accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5ac870d-0829-45bb-ad99-9d6d3fc55e99

📥 Commits

Reviewing files that changed from the base of the PR and between b633fcc and 278b7f9.

📒 Files selected for processing (2)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/ChecksSection/ChecksSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx

…atus

- Add isMountedRef to CommentsSection so markCopied doesn't set state
  after unmount (same pattern as CommentPane/CopyableTable)
- Rewrite computeChecksStatus as single loop instead of 3 passes
  (filter + some(failure) + some(pending) each called coerceCheckStatus)
…nt pane

Use the existing CodeBlock component which handles mermaid via
@streamdown/mermaid and syntax highlighting via react-syntax-highlighter.
Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx (1)

75-80: ⚠️ Potential issue | 🟠 Major

Include thread-query failures in the tab error state.

At Line 79, only prQuery.isError is forwarded. If getPullRequestThreads fails, the tab can render a misleading non-error state.

Suggested fix
 		<ReviewTabContent
 			pr={pr}
 			comments={comments}
 			isLoading={prQuery.isLoading}
-			isError={prQuery.isError}
+			isError={prQuery.isError || threadsQuery.isError}
 			isCommentsLoading={threadsQuery.isLoading}
 			onOpenComment={onOpenComment}
 		/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx
around lines 75 - 80, The ReviewTabContent is only receiving prQuery.isError for
its isError prop so thread-query failures aren't surfaced; update the prop
passed to ReviewTabContent (the isError prop) to combine both error states (e.g.
isError={prQuery.isError || threadsQuery.isError}) so failures from
getPullRequestThreads (threadsQuery) are reflected in the tab error state.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx (1)

260-354: Move CommentRow into its own component file.

CommentsSection.tsx currently defines both CommentsSection and CommentRow. Split CommentRow into its own folder/file pair (with barrel export) to align with the component structure rules.

As per coding guidelines: **/components/**/*.tsx: “Keep one component per file; no multi-component files.”

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

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
around lines 260 - 354, Extract the CommentRow component into its own folder and
file (e.g., components/CommentRow/index.tsx) and export it via a barrel
(components/CommentRow/index.ts) so CommentsSection imports the standalone
component; move the CommentRow function and its related types (CommentRowProps,
handleClick, isCopied, age, content JSX) out of CommentsSection.tsx, ensure the
new file imports the same dependencies (Avatar, AvatarImage, AvatarFallback,
LuCheck, LuCopy, LuArrowUpRight, formatShortAge, getPreviewText) and keeps the
same props API (comment, copiedActionKey, onCopy, onOpen), then update
CommentsSection to import CommentRow from the new barrel and remove the local
definition so only CommentsSection remains in the original file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:
- Around line 162-167: The empty-panel bug is caused by checking comments.length
instead of activeComments.length in the CommentsSection render branch; update
the conditional in the CommentsSection component so it checks
activeComments.length === 0 (and renders an explicit "No open comments" empty
state) instead of comments.length === 0 so resolved comments don't produce a
blank panel; ensure the same message is used where activeComments is mapped and
keep unchanged behavior when there are zero total comments if you want a
different message for “no comments at all.”

---

Duplicate comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx:
- Around line 75-80: The ReviewTabContent is only receiving prQuery.isError for
its isError prop so thread-query failures aren't surfaced; update the prop
passed to ReviewTabContent (the isError prop) to combine both error states (e.g.
isError={prQuery.isError || threadsQuery.isError}) so failures from
getPullRequestThreads (threadsQuery) are reflected in the tab error state.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx:
- Around line 260-354: Extract the CommentRow component into its own folder and
file (e.g., components/CommentRow/index.tsx) and export it via a barrel
(components/CommentRow/index.ts) so CommentsSection imports the standalone
component; move the CommentRow function and its related types (CommentRowProps,
handleClick, isCopied, age, content JSX) out of CommentsSection.tsx, ensure the
new file imports the same dependencies (Avatar, AvatarImage, AvatarFallback,
LuCheck, LuCopy, LuArrowUpRight, formatShortAge, getPreviewText) and keeps the
same props API (comment, copiedActionKey, onCopy, onOpen), then update
CommentsSection to import CommentRow from the new barrel and remove the local
definition so only CommentsSection remains in the original file.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf66e0ec-0dd3-4bff-b915-6bdfa50bd39c

📥 Commits

Reviewing files that changed from the base of the PR and between 278b7f9 and b95339a.

📒 Files selected for processing (2)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/useReviewTab.tsx

Comment on lines +162 to +167
) : comments.length === 0 ? (
<div className="px-1.5 py-1 text-xs text-muted-foreground">
No comments yet.
</div>
) : (
activeComments.map((comment) => (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Show an explicit empty state when there are no open comments.

At Line 162, the condition checks comments.length === 0, but this section renders activeComments. If all comments are resolved, the panel appears blank instead of showing a message.

Suggested fix
-					) : comments.length === 0 ? (
+					) : activeComments.length === 0 ? (
 						<div className="px-1.5 py-1 text-xs text-muted-foreground">
-							No comments yet.
+							No open comments.
 						</div>
 					) : (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/components/WorkspaceSidebar/hooks/useReviewTab/components/CommentsSection/CommentsSection.tsx
around lines 162 - 167, The empty-panel bug is caused by checking
comments.length instead of activeComments.length in the CommentsSection render
branch; update the conditional in the CommentsSection component so it checks
activeComments.length === 0 (and renders an explicit "No open comments" empty
state) instead of comments.length === 0 so resolved comments don't produce a
blank panel; ensure the same message is used where activeComments is mapped and
keep unchanged behavior when there are zero total comments if you want a
different message for “no comments at all.”

- Hide the "mermaid" label and action buttons on the block wrapper
- Strip the outer border/padding so it blends with the page
- Target the SVG element itself to lighten its background color
Revert the custom Streamdown/SyntaxHighlighter approach. Go back to
using the shared CodeBlock component. Instead of changing the mermaid
canvas background, override the SVG text fill and node/edge label
colors via CSS to use --foreground, making them readable on both
light and dark themes.
Use Streamdown directly with custom themeVariables to set light text
colors (primaryTextColor, nodeTextColor, labelTextColor, etc.) on the
dark mermaid theme. Removes the CSS hacks that couldn't override
mermaid's inline SVG styles.
- Use theme: "base" with full dark color palette so themeVariables
  actually control node fills (dark gray nodes, light text)
- Hide "mermaid" label and action buttons via CSS
- Strip background/border from outer block and inner wrapper so
  diagram sits directly on the page background
@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 15, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

Move code block rendering out of useMemo into a proper React component
so the component identity is stable across renders. Prevents unmount/
remount of Streamdown/SyntaxHighlighter when the theme changes.

Mermaid theme variable objects extracted as module-level constants.
@AviPeltz AviPeltz merged commit de70163 into main Apr 15, 2026
6 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
…uperset-sh#3463)

* feat(desktop): add Review tab to v2 workspace sidebar

Port the v1 Review tab functionality into the v2 workspace sidebar,
replacing the "Checks — Coming soon" stub. Uses v2-native host-service
endpoints (git.getPullRequest, git.getPullRequestThreads) instead of
v1 electron endpoints.

Features:
- PR header with title, state icon, review decision badge
- Collapsible CI checks section with status icons and links
- PR comments with avatars, timestamps, and preview text
- Copy individual comments or copy all to clipboard
- Comment pane: click a comment to view rendered markdown in a tab
- GitHub link in pane header to open comment on GitHub
- Copyable tables in rendered markdown

* fix(desktop): address review tab PR feedback

- Fix memory leaks: add timeout cleanup refs in CommentPane and
  CopyableTable, clear previous timeout on rapid clicks
- Add error state: show "Unable to load review status" when tRPC
  query fails instead of infinite loading spinner
- Fix getIcon fallback: show MessageSquare icon when avatarUrl is
  missing instead of rendering <img src={undefined}>
- Add aria-label to comment open button for screen readers
- Add aria-hidden to decorative arrow icon in PRHeader
- Add group-focus-visible:opacity-100 to PRHeader arrow for keyboard nav

* fix(desktop): add .catch() to all clipboard promise chains

Prevents unhandled promise rejections in the renderer if
electronTrpcClient.external.copyText.mutate() fails.

* fix(desktop): guard state updates after unmount in CommentPane

Add isMountedRef to both CommentPane and CopyableTable to prevent
setCopied calls after unmount when the async clipboard IPC resolves
after the component is gone.

* fix(desktop): fix comments not loading until refresh

The threads query had staleTime: 30_000 which cached empty results
when the query fired before the host-service had PR data synced.
Remove staleTime so every mount/refocus fetches fresh data (background
polling at 30s still runs via refetchInterval).

Also tighten the enabled condition to use prQuery.isSuccess instead
of !!prQuery.data for clearer intent, and use composite key for
CheckRow to handle duplicate check names.

* fix(desktop): unmount guard in CommentsSection + single-pass check status

- Add isMountedRef to CommentsSection so markCopied doesn't set state
  after unmount (same pattern as CommentPane/CopyableTable)
- Rewrite computeChecksStatus as single loop instead of 3 passes
  (filter + some(failure) + some(pending) each called coerceCheckStatus)

* feat(desktop): render mermaid diagrams + syntax highlighting in comment pane

Use the existing CodeBlock component which handles mermaid via
@streamdown/mermaid and syntax highlighting via react-syntax-highlighter.

* fix(desktop): lighten mermaid diagram background in comment pane

* fix(desktop): hide mermaid label + lighten actual diagram background

- Hide the "mermaid" label and action buttons on the block wrapper
- Strip the outer border/padding so it blends with the page
- Target the SVG element itself to lighten its background color

* fix(desktop): revert hacky mermaid overrides, use CSS text color instead

Revert the custom Streamdown/SyntaxHighlighter approach. Go back to
using the shared CodeBlock component. Instead of changing the mermaid
canvas background, override the SVG text fill and node/edge label
colors via CSS to use --foreground, making them readable on both
light and dark themes.

* fix(desktop): use mermaid themeVariables for light text in dark mode

Use Streamdown directly with custom themeVariables to set light text
colors (primaryTextColor, nodeTextColor, labelTextColor, etc.) on the
dark mermaid theme. Removes the CSS hacks that couldn't override
mermaid's inline SVG styles.

* fix(desktop): mermaid dark theme contrast + hide label/wrapper chrome

- Use theme: "base" with full dark color palette so themeVariables
  actually control node fills (dark gray nodes, light text)
- Hide "mermaid" label and action buttons via CSS
- Strip background/border from outer block and inner wrapper so
  diagram sits directly on the page background

* fix(desktop): extract CommentCodeBlock as standalone component

Move code block rendering out of useMemo into a proper React component
so the component identity is stable across renders. Prevents unmount/
remount of Streamdown/SyntaxHighlighter when the theme changes.

Mermaid theme variable objects extracted as module-level constants.
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
Three functional issues Codex found on upstream superset-sh#3463 cherry-pick:

1. useReviewTab.tsx normalizeThreadsToComments only picked
   thread.comments[0], silently dropping every reply in a review
   thread. v1 flattened all comments per thread. Loop over
   thread.comments instead so the listing and badge match the real
   PR state.

2. CommentPane.tsx fed ReactMarkdown the raw comment body without
   overriding <a> or <img>. GitHub-supplied links would navigate the
   main window away, and arbitrary remote images would load. Add
   CommentLink (opens via electronTrpcClient.external.openUrl) and
   CommentImage (uses SafeImage, which only renders data: URLs).

3. useReviewTab.tsx returned badge: openCommentCount unconditionally,
   so the tab showed a "0" badge when there were no open comments.
   Changes tab already suppresses zero badges; match that.

Deferred (upstream bug, host-service schema change required):
- Review comments never get a GitHub URL because the GraphQL query
  and host-service types in packages/host-service don't fetch/expose
  reviewComment.url. Fixing this needs host-service router changes
  and is out of scope for this cherry-pick.
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
-s ours merge to record that upstream commits a3e34bf through
de70163 (13 commits) are semantically already present on origin/main
via the PR1-6 cherry-pick series (PRs #176, #177, #178, #179, #180,
#182), plus fork-adaptation fixes layered on top.

This merge target is de70163 specifically (not upstream/main) so
newer upstream commits (9fff075 and later) remain visible in future
behind counts.

Upstream commits covered by this audit merge:
- a3e34bf  fix(desktop): restore cmd+click requirement for v1 terminal file links (superset-sh#3457)  [PR1/#176]
- 57557f8  fix(desktop): gate v2 workspace children on collection readiness (superset-sh#3464)       [PR1/#176]
- 4ee2e61  fix(desktop): use native clipboard for copy path in v2 sidebar (superset-sh#3462)         [PR1/#176]
- 87d6e93  feat(desktop): close settings with Escape key (superset-sh#3466)                          [PR1/#176]
- 9c7f5f4  chore(desktop): auto-restart host-service on bundle change in dev (superset-sh#3461)      [PR1/#176]
- 93140d9  fix(mcp): accept MCP resource URL as valid OAuth audience (superset-sh#3459)              [PR2/#177]
- be9e000  fix(desktop): drive tray menu off events, fetch real org name (superset-sh#3458)          [PR2/#177]
- c5f791e  feat(v2): unify workspace delete through host-service (superset-sh#3443)                  [PR3/#178]
- 2c24d93  feat(desktop): paginated branch picker with checkout + open actions (superset-sh#3397)    [PR4/#179]
- 2bf1049  feat(desktop/hotkeys): v1 directional pane focus + best-effort v1 override migrator (superset-sh#3460)  [PR5/#180]
- 1294a7d  feat(desktop/hotkeys): restore Cmd+Alt+Arrow for tab/workspace nav (superset-sh#3472)    [PR5/#180]
- de70163  feat(desktop): v2 review tab first pass — PR info, checks, comments (superset-sh#3463)    [PR6/#182]

Intentionally skipped (version bump, fork has independent versioning):
- 1e23353  chore(desktop): bump version to 1.5.5 (superset-sh#3473)

Fork-adaptation fixes layered on top of the cherry-picks:
- PR1: host-service-coordinator alias import fix, settings Escape
       selector narrowing (role-based + popper wrapper), Escape
       close uses replace navigation
- PR2: dual quit mode preservation (requestQuit "release"/"stop"),
       trayUpdateToken guard for stale async fetchHostInfo results
- PR4: ChangesHeader.normalizeBranchName regex rewrite (lint false
       positive), worktree add uses fullRef for remote-tracking
       refs, syncTimedOut reset on pendingId change, GIT_REFS.md
       barrel example fix
- PR5: migrate.ts re-sanitize of existing localStorage overrides
       (v2 marker bump intent), FOCUS_PANE_* enabled:isActive for
       KeepAliveWorkspaces, CATEGORY_ORDER merges Navigation (upstream)
       and Browser (fork)
- PR6: normalizeThreadsToComments flattens all thread.comments (not
       just first), CommentPane overrides <a> (openUrl) and <img>
       (SafeImage), zero-badge suppression, pr-null comments gate

Fork features verified intact (Explore agent audit of combined
36d4de4..35d95f3 range):
- BROWSER_RELOAD / BROWSER_HARD_RELOAD hotkeys
- dual quit mode menu in tray
- v1 terminal cold-restore + retry reconnect (out of range but
  unaffected)
- KeepAliveWorkspaces (FOCUS_PANE_* gated on isActive)
- useCommandPalette + addMemoTab in v2 workspace
- host-service-coordinator rename alias pattern
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