Skip to content

Refactor diff viewer onto Pierre React primitives#5308

Open
lawrencecchen wants to merge 40 commits into
mainfrom
task-diff-viewer-react-components
Open

Refactor diff viewer onto Pierre React primitives#5308
lawrencecchen wants to merge 40 commits into
mainfrom
task-diff-viewer-react-components

Conversation

@lawrencecchen
Copy link
Copy Markdown
Contributor

@lawrencecchen lawrencecchen commented Jun 3, 2026

Summary

  • Move diff-viewer status/loading rendering into React state instead of direct controller DOM mutation.
  • Keep the CodeView renderer owning #viewer by rendering the loading layer as a sibling overlay.
  • Add focused status-model tests and refresh the bundled diff-viewer app asset.

Testing

  • cd diff-viewer && bun test
  • cd diff-viewer && bun run typecheck
  • cd diff-viewer && bun run lint:ci
  • cd diff-viewer && bun run react-doctor:ci
  • cd diff-viewer && bun run build
  • ./scripts/build-diff-viewer-app.sh --check

Notes


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Protocol and executable-matching change how the local diff viewer server is reused (fewer stale-server bugs, more restarts after rebuilds); asset path/MIME changes must stay aligned with the bundled React app.

Overview
Adds a CI gate that runs ./scripts/check-diff-viewer-react-compiler.mjs in the react-apps-check job so bundled diff-viewer output must pass the React Compiler guard.

Diff viewer HTTP server (Swift): bumps the protocol to react-app-v2 executable-bound, records executablePath in server state, and only reuses an existing listener when the live process path matches the current cmux binary (via proc_pidpath). Stale servers from another install or rebuild are no longer reused silently.

Bundled Pierre assets: upgrades the shipped bundle to pierre-diffs-1.2.7, switches the worker entry from worker-portable.mjs to worker-portable.js, and treats .js as valid JS alongside .mjs for MIME checks and asset enumeration.

Localization: adds diffViewer.copyFailedGitApplyCommand across locales for when copying the git apply command fails.

Reviewed by Cursor Bugbot for commit 9b65c3c. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Refactors the diff viewer onto React @pierre/diffs/@pierre/trees with streamed rendering, an external worker pool, and a transparent code surface. Adds a React Compiler CI guard, a stress‑sample opener, executable‑bound server reuse, and localized copy‑failure feedback.

  • Refactors

    • Replace the controller with React CodeView + FileTree; move lifecycle into hooks; App owns status overlays.
    • Stream diffs with tighter batched updates; avoid full tree prep during streaming; incrementally refresh the file tree; preload highlighter/theme.
    • Use WorkerPoolContextProvider with external Pierre worker assets; sync worker render options; bump to @pierre/diffs@1.2.7/@pierre/trees@1.0.0-beta.4.
    • Bind the viewer HTTP server to the current executable and upgrade the protocol to react-app-v2 executable-bound; allow .js assets, stage worker-portable.js, and enumerate .js/.mjs.
    • Render toolbar icons as React SVG; improve sidebar spacing/labels; add actions.ts for URL rewriting and clipboard‑safe copy; add stress‑sample opener; CI enforces React Compiler output.
  • Bug Fixes

    • Stable IDs for repeated‑path items during streams; preserve tree append planning; use exclusive tree selection.
    • Fix navigation URL rewriting/select styling and CodeView sizing/scrolling; avoid redundant worker option syncs.
    • Apply incremental git‑status patches with fallback and full reset when needed.
    • Ensure readable foreground/selection colors; fix file‑tree lanes and semantic color tokens; localize unnamed diff file and copy‑failure messages.
    • Keep the viewer visible on copy failure; improve toolbar/menu accessibility; fix toolbar overflow on narrow widths.
    • Normalize diff separator backgrounds to keep the code surface consistently transparent.

Written for commit 9b65c3c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Centralized initial status and loading UI; streaming diff loader with incremental file rendering, tree updates, renames, toolbar, file sidebar, icons, options, keyboard shortcuts, and copy‑apply command with safe heredoc handling.
  • Bug Fixes
    • Improved pending-replacement, loading/error states and document-status synchronization.
  • Tests
    • Added unit/integration tests for status logic, copy‑apply fallback, and app startup flows.
  • Style
    • Loading overlay and offscreen copy-fallback styling adjusted.
  • Chores
    • Added runtime/dev package dependencies.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 4, 2026 9:28am
cmux-staging Building Building Preview, Comment Jun 4, 2026 9:28am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

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

Introduces a typed DiffViewerStatus and document sync, seeds App reducer with bootstrap-derived initialStatus, converts controller→host status publishing, repositions LoadingLayer, adds streaming patch parsing, actions/utilities, theme/options helpers, icons, and tests.

Changes

Diff Viewer Status Reactive Architecture

Layer / File(s) Summary
Status data model and tests
diff-viewer/src/status.ts, diff-viewer/test/status.test.ts
Defines DiffViewerStatus/options, createDiffViewerStatus, initialDiffViewerStatus, and applyDiffViewerStatusToDocument; unit tests validate computed flags and messages.
Bootstrap label & initialStatus wiring
diff-viewer/src/main.tsx, diff-viewer/src/styles.css, diff-viewer/package.json
Creates label resolver, derives initialDiffViewerStatus from config payload, applies it to document.body.dataset, ensures root mount, passes initialStatus to <App>, sets #loading-layer { grid-area: viewer; }, and adds runtime/dev deps.
Viewer controller host-based status publishing
diff-viewer/src/viewer-controller.ts
Adds DiffViewerHost with setStatus, changes startDiffViewer to accept host, removes direct DOM status element wiring, and forwards created DiffViewerStatus to host.setStatus after applying it to the document.
App state, reducer, LoadingLayer integration
diff-viewer/src/App.tsx
App now accepts initialStatus and seeds reducer state.status; adds set-status reducer action; LoadingLayer({label,status}) rendered as sibling in #content, binds data-error/data-pending, and displays status.message (with localized fallback). Startup hooks (useRenderDiff, usePendingReplacement, page dataset hooks, keyboard/options behaviors) dispatch status transitions.
Streaming patch parser and model
diff-viewer/src/diff-stream.ts
Implements streamPatch with a StreamingDiffModel, batching, path/id mapping, rename semantics, git-status patch generation, file-splitting on diff --git and commit-metadata boundaries, metrics, and yield-to-frame scheduling; includes helpers fileName, fileStats, and gitStatusType.
Actions: navigation & copy-git-apply
diff-viewer/src/actions.ts
Adds resolveDiffNavigationURL, diffSourceDetail, copyGitApplyCommand (fetches patch, builds safe heredoc git apply command, copies to clipboard with textarea fallback), and safeGitApplyDelimiter.
UI options & theme helpers
diff-viewer/src/pierre-options.ts
Adds DiffViewerOptions, codeViewOptions, workerHighlighterOptions, unsafe CSS helpers for sticky headers/file-tree, and shikiThemeFromGhostty for theme conversion.
Icons
diff-viewer/src/icons.tsx
Adds IconName type, inline SVG Icon component, and IconPaths switch-based path fragments.
Deps and tests
diff-viewer/package.json, diff-viewer/test/app.test.tsx, diff-viewer/test/actions.test.ts
Adds runtime deps @pierre/diffs, @pierre/trees; dev deps jsdom/@types/jsdom; adds App tests ensuring status-only render avoids network fetch and action tests for clipboard fallback.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#5074: Prior React diff viewer work; bootstrapping and status initialization overlap.
  • manaflow-ai/cmux#5252: Touches viewer-controller.ts and status text handling; overlaps with controller host/status changes.
  • manaflow-ai/cmux#5016: Related to payload fields (statusMessage, statusIsError, pendingReplacement) consumed by initialDiffViewerStatus.

Poem

🐰 I hop and nibble at the start,
I seed the status, play my part,
From bootstrap label to reducer hum,
LoadingLayer waits until items come,
Streams arrive — the rabbit drums.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Algorithmic Complexity ❌ Error App.tsx has three O(n) violations: scrollTargetForItem (1088) uses items.some() in hot path; rename-item (107) and collapsed (128) map entire items array without bounds. Add itemId Map for O(1) lookup; replace items.some() with map check; avoid mapping all items for single property updates (collapsed, rename).
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 (16 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: refactoring the diff viewer to use Pierre React primitives, which is the core objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR contains only TypeScript/React changes (diff-viewer refactoring); no Swift files modified, so the Swift actor isolation check is not applicable.
Cmux Swift Blocking Runtime ✅ Passed PR contains no Swift code changes—only TypeScript/React modifications to diff-viewer package, so Swift blocking runtime check is not applicable.
Cmux No Hacky Sleeps ✅ Passed 700ms keyboard chord timeout in App.tsx is user-visible presentation timing. 50ms setTimeout in yieldToNextFrame (diff-stream.ts) is frame-scheduling fallback, not a race workaround.
Cmux Swift Concurrency ✅ Passed PR contains no Swift code; check applies only to cmux-owned Swift code. All modifications are in TypeScript/React diff-viewer (.ts, .tsx, .css, .json files).
Cmux Swift @Concurrent ✅ Passed No Swift files present in this PR; check is scoped to Swift changes only. All modifications are TypeScript/JavaScript/CSS in the diff-viewer package.
Cmux Swift File And Package Boundaries ✅ Passed Custom check for Swift file/package boundaries does not apply—this PR modifies only the diff-viewer TypeScript/React package with no Swift file changes.
Cmux Swift Logging ✅ Passed No Swift files in this PR; check only applies to Swift production code changes. All changes are TypeScript/React in diff-viewer package.
Cmux User-Facing Error Privacy ✅ Passed All user-facing error messages use safe localized labels. Technical errors with HTTP codes are caught and replaced with generic messages. No implementation details exposed to users.
Cmux Full Internationalization ✅ Passed PR is scoped to diff-viewer/, a bundled macOS React app separate from web/. Labels are config-injected by parent app, not hardcoded web UI text. Not applicable to next-intl web i18n rules.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI code—only TypeScript/React changes to diff-viewer package. SwiftUI state layout check does not apply.
Cmux Architecture Rethink ✅ Passed The swift-architectural-rethink rule applies only to Swift changes. This PR modifies only TypeScript/React code in diff-viewer with no Swift files affected.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains only TypeScript/React/CSS changes to diff-viewer package; no Swift files modified, so cmux Swift auxiliary window close shortcuts check is not applicable.
Description check ✅ Passed PR description comprehensively covers changes, includes testing steps, and lists verification commands.
✨ 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 task-diff-viewer-react-components

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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 3, 2026

Greptile Summary

This PR refactors the diff viewer away from direct DOM manipulation via a monolithic controller toward React-managed state (useReducer) and the @pierre/diffs/@pierre/trees component primitives. Status and loading UI are now owned by React, incremental streaming flushes items in batched chunks, and the HTTP server is bound to the current executable via proc_pidpath to prevent stale-server reuse across binary upgrades.

  • React state migration: viewer-controller.ts is removed; App.tsx gains a useReducer-based AppState with useRenderDiff, usePendingReplacement, and usePageDataAttributes hooks driving status overlays, loading skeletons, and document.body.dataset in sync.
  • Streaming diff loader: diff-stream.ts introduces streamPatch with incremental batch flushing, stable per-path item IDs across repeated paths, and a createStreamingPatchFileSplitter that chunks the response body on diff --git boundaries without buffering the entire patch.
  • Executable-bound server reuse: diffViewerHTTPServerStateMatchesCurrentExecutable uses proc_pidpath to verify the running server PID belongs to the current binary before reusing it; protocol bumps to react-app-v2 executable-bound; asset enumeration now includes .js alongside .mjs.

Confidence Score: 5/5

Safe to merge — the React state migration is well-structured, streaming logic is tested, and the executable-bound server reuse check correctly validates the running PID via proc_pidpath before reusing a cached server.

The controller removal and React state migration are complete: every status transition goes through dispatch, the loading layer is a pure React component, and document.body.dataset is kept in sync by usePageDataAttributes. The streaming patch loader handles repeated-path IDs, renames, and git-boundary splitting correctly. The Swift proc_pidpath check uses the correct PROC_PIDPATHINFO_MAXSIZE buffer and falls back gracefully for old state files that lack the executablePath field. Localization for the new copyFailedGitApplyCommand label covers all existing catalog locales. No correctness, data, or security issues were identified.

No files require special attention.

Important Files Changed

Filename Overview
diff-viewer/src/App.tsx Core refactor: replaces controller with useReducer + hook-driven React state; status, loading layer, tree, toolbar, and keyboard shortcuts all well-structured; no issues found.
diff-viewer/src/diff-stream.ts New streaming patch loader with stable per-path item IDs, incremental batching, git-boundary splitter, and rename tracking; logic is well-tested and correct.
diff-viewer/src/actions.ts Introduces copyGitApplyCommand with safe heredoc delimiter generation, navigator.clipboard fallback via execCommand, and URL rewriting for custom scheme; logic is correct and well-tested.
diff-viewer/src/status.ts Thin status model helpers with full test coverage including the previously flagged pendingReplacement-without-statusMessage fallback case.
CLI/cmux_open.swift Adds executable-bound server reuse via proc_pidpath, protocol version bump, copyFailedGitApplyCommand label, .js asset support; all new code is well-structured and correctly guarded.
Resources/Localizable.xcstrings Adds diffViewer.copyFailedGitApplyCommand with translations across all supported locales (ar, bs, da, de, en, es, fr, it, ja, km, ko, nl, pl, pt-BR, ru, sv, tr, uk, vi, zh-Hans, zh-Hant).
diff-viewer/src/pierre-options.ts New file encapsulating CodeView/worker highlighter option builders and unsafe CSS overrides; clean separation of Pierre option construction.
diff-viewer/test/actions.test.ts Tests clipboard fallback, writeText rejection path, fallback failure, and URL rewriting; comprehensive coverage for new action helpers.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App mounts] --> B[initialDiffViewerStatus]
    B --> C{payload type?}
    C -->|pendingReplacement| D[usePendingReplacement]
    C -->|statusMessage| E[set-status: terminal message]
    C -->|normal diff| F[useRenderDiff]
    F --> I[streamPatch fetch patchURL]
    I --> J[createStreamingPatchFileSplitter]
    J --> K[appendFileDiffToModel]
    K --> L{batch ready?}
    L -->|yes| M[onBatch dispatch append-items]
    L -->|no| N[yieldToNextFrame rAF]
    N --> K
    M --> O[onTreeSource dispatch set-tree-source]
    M --> Q[React renders CodeView + LoadingLayer]
    Q --> R[usePageDataAttributes syncs document.body.dataset]
Loading

Reviews (21): Last reviewed commit: "Normalize diff separator backgrounds" | Re-trigger Greptile

Comment thread diff-viewer/src/viewer-controller.ts Outdated
Comment on lines 648 to 651
if (firstRender) {
firstRender = false;
document.body.dataset.loading = "false";
status.remove();
}
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.

P2 Incomplete migration: first-render loading transition bypasses host.setStatus

On the normal rendering path, flushPendingItems clears the loading state with a direct DOM write (document.body.dataset.loading = "false") without calling host.setStatus(). As a result, the React status state is permanently stale (loading: true, original message) after the first batch renders. The #loading-layer is hidden by CSS correctly, so there's no visible bug, but the React state never reflects the completed loading transition. If setStatus is later called for a secondary status update (e.g., an error that makes #loading-layer visible again), React will briefly see the old stale loading status before applying the new one in the same commit — an incomplete move to the React-state ownership the PR intends.

Comment on lines +23 to +38
test("initialDiffViewerStatus uses pending replacement status", () => {
const config: DiffViewerConfig = {
payload: {
pendingReplacement: true,
statusMessage: "Rendering diff",
},
};

expect(initialDiffViewerStatus(config, label)).toEqual({
error: false,
loading: true,
message: "Rendering diff",
pending: true,
statusOnly: false,
});
});
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.

P2 Missing test case: pendingReplacement without a statusMessage

initialDiffViewerStatus contains a fallback path (payload.statusMessage ?? label("loadingDiff")) when pendingReplacement: true but statusMessage is absent, yet only the case with statusMessage present is tested. A test confirming the fallback produces message: "Loading diff" with loading: true, pending: true would close the coverage gap.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Covered earlier in this PR. diff-viewer/test/status.test.ts now has a pendingReplacement-without-statusMessage case that asserts the fallback loading status.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Covered in current head. diff-viewer/test/status.test.ts includes the pendingReplacement-without-statusMessage fallback case.

— Claude Code

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 3, 2026
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@diff-viewer/src/viewer-controller.ts`:
- Around line 291-295: The code clears only document.body.dataset.loading when
loading finishes, leaving the React host state stale; instead update the React
status via the existing helper: replace the direct DOM-only clearing call with a
call to showStatusMessage or host.setStatus using a success/idle
DiffViewerStatus (e.g. createDiffViewerStatus('', { loading: false, pending:
false }) or similar) so that applyDiffViewerStatusToDocument still updates the
DOM and host.setStatus keeps React state in sync; locate the direct manipulation
and swap it to call showStatusMessage(...) or host.setStatus(...) with the
non-loading status.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cf512748-e44a-4452-8519-cf6a8381d3f8

📥 Commits

Reviewing files that changed from the base of the PR and between 6604cc8 and 6efaa2c.

📒 Files selected for processing (7)
  • Resources/markdown-viewer/diff-viewer-app/main.mjs
  • diff-viewer/src/App.tsx
  • diff-viewer/src/main.tsx
  • diff-viewer/src/status.ts
  • diff-viewer/src/styles.css
  • diff-viewer/src/viewer-controller.ts
  • diff-viewer/test/status.test.ts

Comment thread diff-viewer/src/viewer-controller.ts Outdated
Copy link
Copy Markdown

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

No issues found across 7 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Jun 3, 2026

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@lawrencecchen lawrencecchen changed the title Move diff viewer status into React state Refactor diff viewer onto Pierre React primitives Jun 3, 2026
@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e32358b498

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread diff-viewer/src/App.tsx Outdated
onCopyGitApply={async () => {
try {
const message = await copyGitApplyCommand(payload.patchURL, label);
setStatus(createDiffViewerStatus(message, { loading: false, statusOnly: true }));
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 Avoid replacing the diff with the copy confirmation

When a user chooses "Copy git apply command" after a diff has rendered, this sets statusOnly: true; the existing CSS for body[data-status-only="true"] hides the files sidebar and centers #loading-layer as an empty-state screen, so a successful copy replaces the usable diff with a permanent confirmation page until reload. The previous implementation only updated the options button title/aria label, so this should be a transient/non-status-only notification instead of entering the terminal status-only state.

Useful? React with 👍 / 👎.

Comment thread diff-viewer/src/actions.ts Outdated
Comment on lines +38 to +39
if (!navigator.clipboard?.writeText) {
throw new Error("Clipboard API unavailable");
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 Preserve fallback copying when Clipboard API is absent

In viewer contexts where navigator.clipboard.writeText is unavailable, this now always throws and the Copy git apply command cannot work. The deleted controller had an execCommand("copy") fallback for exactly this case, so users on those embedded/custom-scheme WebKit contexts lose the copy action even though a textarea fallback can still succeed.

Useful? React with 👍 / 👎.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 3, 2026
Copy link
Copy Markdown

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@diff-viewer/src/actions.ts`:
- Around line 25-28: The function copyGitApplyCommand currently returns
label("renderFailed") when patchURL is missing which causes callers like
Toolbar.onCopyGitApply to treat the action as a success; change
copyGitApplyCommand to throw an Error (or reject) when patchURL is undefined
instead of returning a string so the failure path is taken. Locate
copyGitApplyCommand and replace the early return that uses label("renderFailed")
with a thrown Error (include a descriptive message that mentions patchURL) so
callers can catch and surface an actual failure.

In `@diff-viewer/src/App.tsx`:
- Around line 600-608: The call to preparePresortedFileTreeInput(source.paths)
is being recomputed on every PierreFileTree render; memoize this preprocessed
value and pass the memoized result into useFileTree as preparedInput to avoid
repeated full scans. Create a memo (e.g., via useMemo) keyed on source.paths (or
a stable derived key) where preparePresortedFileTreeInput is invoked, update the
preparedInput prop to use that memo value, and ensure resetPaths (and any other
callers) reuse the same memoized prepared input so
preparePresortedFileTreeInput, useFileTree, and resetPaths no longer perform
redundant work on unrelated status/selection toggles.

In `@diff-viewer/test/app.test.tsx`:
- Around line 10-15: The afterEach cleanup currently only unmounts React via the
root variable; update the afterEach block to also restore any globals the test
mocks (e.g., window, document, navigator, DOM constructors like
Element/Node/HTMLElement, and fetch) to their original values by assigning back
previously-saved originals (e.g., originalWindow, originalDocument,
originalNavigator, originalElementConstructor, originalFetch) before setting
root = null; ensure these restore assignments occur inside the same afterEach
that references root so later tests don't inherit the mocked globals.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2aab744-674b-43df-af01-f4a122823c3c

📥 Commits

Reviewing files that changed from the base of the PR and between 6efaa2c and 8bb8098.

⛔ Files ignored due to path filters (1)
  • diff-viewer/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • Resources/markdown-viewer/diff-viewer-app/main.mjs
  • diff-viewer/package.json
  • diff-viewer/src/App.tsx
  • diff-viewer/src/actions.ts
  • diff-viewer/src/diff-stream.ts
  • diff-viewer/src/icons.tsx
  • diff-viewer/src/pierre-options.ts
  • diff-viewer/src/viewer-controller.ts
  • diff-viewer/test/app.test.tsx
💤 Files with no reviewable changes (1)
  • diff-viewer/src/viewer-controller.ts

Comment thread diff-viewer/src/actions.ts Outdated
Comment thread diff-viewer/src/App.tsx Outdated
Comment thread diff-viewer/test/app.test.tsx Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2264854f37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread diff-viewer/src/App.tsx Outdated
) {
const started = useRef(false);
useEffect(() => {
if (started.current || config.payload?.pendingReplacement === true || typeof config.payload?.statusMessage === "string") {
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 Do not skip rendering for empty status messages

When the host includes statusMessage: "" (or another empty string), initialDiffViewerStatus treats it as a normal diff render because it checks statusMessage.length > 0, but this guard skips streamPatch for any string value. In that case the patch URL is never fetched and the viewer remains on the initial loading state indefinitely; keep this condition consistent with the non-empty check used in status.ts.

Useful? React with 👍 / 👎.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d0c0fe17d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread diff-viewer/src/actions.ts Outdated
Comment on lines +42 to +43
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(command);
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 Fall back when Clipboard.writeText rejects

When navigator.clipboard.writeText exists but rejects (for example, after the async patch fetch loses the user-activation window or in WebKit contexts that expose the API but deny permission), this throws into the App catch and reports a render failure even though the textarea fallback below can still copy. The previous implementation fell back to execCommand("copy") on writeText failure, so wrap this call in try/catch and use the fallback before surfacing an error.

Useful? React with 👍 / 👎.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

Copy link
Copy Markdown

@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)
diff-viewer/src/App.tsx (1)

593-618: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update useSyncedRef to write synchronously (not via useEffect) so renderRowDecoration can’t read stale source/label.

diff-viewer/src/App.tsx’s useSyncedRef updates ref.current in useEffect, but renderRowDecoration reads latest.current during the same render pass where the callback is created, so the first paint after source/label changes can show stale row stats/titles.

Suggested fix
 function useSyncedRef<T>(value: T): React.MutableRefObject<T> {
   const ref = useRef(value);
-  useEffect(() => {
-    ref.current = value;
-  }, [value]);
+  ref.current = value;
   return ref;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@diff-viewer/src/App.tsx` around lines 593 - 618, The hook useSyncedRef is
updating ref.current inside useEffect which is async, causing latest.current to
be stale for renderRowDecoration; change useSyncedRef so it writes the incoming
value synchronously (e.g., useRef inside the hook and assign ref.current = value
during the hook body or via useLayoutEffect instead of useEffect) so callers
like latest = useSyncedRef({ label, onSelectItem, source }) always see the
latest label/source during the same render and renderRowDecoration reads
up-to-date stats/titles.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@diff-viewer/src/App.tsx`:
- Around line 593-618: The hook useSyncedRef is updating ref.current inside
useEffect which is async, causing latest.current to be stale for
renderRowDecoration; change useSyncedRef so it writes the incoming value
synchronously (e.g., useRef inside the hook and assign ref.current = value
during the hook body or via useLayoutEffect instead of useEffect) so callers
like latest = useSyncedRef({ label, onSelectItem, source }) always see the
latest label/source during the same render and renderRowDecoration reads
up-to-date stats/titles.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60f9914c-7813-4681-a4f4-3eef221cecf0

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb8098 and 2264854.

📒 Files selected for processing (3)
  • Resources/markdown-viewer/diff-viewer-app/main.mjs
  • diff-viewer/src/App.tsx
  • diff-viewer/src/icons.tsx

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 3, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a1148de. Configure here.

Comment thread CLI/cmux_open.swift Outdated
Comment thread CLI/cmux_open.swift Outdated
@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8d7c6bedb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread diff-viewer/src/App.tsx Outdated
Comment on lines +146 to +147
const source = action.source.preparedInput == null
? { ...action.source, preparedInput: preparePresortedFileTreeInput(action.source.paths) }
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 Keep tree preparation off streaming batches

When a large diff is streaming, every onTreeSource dispatch reaches this reducer and calls preparePresortedFileTreeInput over the full source.paths array before React can append just the new paths, so sidebar work grows with the whole diff on each batch and can make large diffs janky. Fresh evidence since the earlier thread: the current reducer now recreates preparedInput here before storing the source, so the append-only file-tree hook no longer avoids the full rebuild.

Useful? React with 👍 / 👎.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Jun 4, 2026

This PR is large and would use a significant portion of your monthly review quota. Comment @cubic-dev-ai review this to confirm that you want cubic to review it.

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