Repository navigation
Add read-only diff viewer to iOS app - #5629
lawrencecchen wants to merge 1 commit into
Conversation
Reviewing what an agent changed is the #1 away-from-desk task, so the iOS app now hosts a read-only git diff/file viewer for a workspace's changes. Reuses the existing desktop diff-viewer React bundle (webviews/, served on desktop via the CLI's generated host page) rather than inventing a new viewer. A new CmuxMobileDiffViewer package embeds that bundle and serves the host HTML, the patch, and every asset (React entry, chunks, and the vendored @pierre/diffs worker assets) under one app-private cmux-diff:// custom scheme, so the bundle, its worker, and the patch are same-origin with no network or ATS exposure. The generated config matches the desktop's ensureDiffViewerAssets layout, so the bundle boots unchanged. Data path mirrors the desktop's default "View diff" source: a new mobile.workspace.diff RPC resolves the workspace's working directory on the Mac and returns its unstaged git working-tree patch (same git arguments as the CLI diff command), produced off the main actor with a size cap and bounded timeout. The verb sits in the mobileHostHandleRPC allowlist and is fully Stack-auth gated like the rest of the mobile data plane; the Mac advertises workspace.diff.v1 so clients can detect support. The viewer is review-only: no staging, committing, or editing. A "View diff" toolbar button on the workspace screen presents it; loading/empty/error/ truncated states are handled with .task (no useEffect) and localized (en+ja). The iOS package embeds a copy of the desktop bundle (SwiftPM resources must live in-package); scripts/sync-ios-diff-viewer-bundle.sh keeps it in lockstep, is called from build-webviews-app.sh, and a --check guard runs in CI next to the desktop bundle check so the copy can't silently drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 300 files, which is 150 over the limit of 150. To get a review, narrow the scope: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (300)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 73726ca89c
ℹ️ 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".
| let chunk = handle.availableData | ||
| if chunk.isEmpty { break } | ||
| collectedLock.lock() | ||
| collected.append(chunk) |
There was a problem hiding this comment.
Enforce the diff byte cap while reading stdout
For workspaces with very large binary/generated changes, this reads and appends the entire git diff --binary output into collected before applying mobileWorkspaceDiffMaxPatchBytes at the end, so the advertised 4 MiB cap does not actually bound Mac-side memory or RPC latency. A large diff can still allocate hundreds of MB and hang/OOM the host before the truncation path runs; cap or stop collecting as chunks arrive.
Useful? React with 👍 / 👎.
| data: ["workspace_id": workspaceId.uuidString] | ||
| ) | ||
| } | ||
| let directory = workspace.currentDirectory.trimmingCharacters(in: .whitespacesAndNewlines) |
There was a problem hiding this comment.
Resolve the same working directory as desktop diff
When the focused panel has its own tracked/requested directory, the desktop diff action targets workspace.resolvedWorkingDirectory() (focused panel directory, then requested directory, then workspace cwd), but this mobile RPC always uses workspace.currentDirectory. In that common split/panel scenario the phone can show a diff for the workspace fallback rather than the directory the user is currently reviewing on the Mac; use the same resolution path as the desktop diff action.
Useful? React with 👍 / 👎.
Reviewing what an agent changed is the #1 away-from-desk task. The iOS app now hosts a read-only git diff/file viewer for a workspace's changes, reusing the existing desktop diff-viewer React bundle rather than inventing a new one.
What it does
A "View diff" button on the workspace screen presents a sheet that renders the workspace's unstaged git diff read-only (no staging, committing, or editing). Loading / empty / error / truncated states are handled, and all native strings are localized (en + ja).
How it reuses the desktop viewer
The desktop diff viewer is the
webviews/React app (@pierre/diffs), served on desktop via the CLI's generated host page (CLI/cmux_open.swiftwriteDiffViewerHTML+ensureDiffViewerAssets). The newPackages/CmuxMobileDiffViewerpackage embeds that same bundle and serves it inside aWKWebViewover an app-privatecmux-diff://custom scheme:DiffViewerSchemeHandlerserves the generated host HTML at the origin root, the patch atdiff.patch, and every bundled asset (the React entry, its chunks, and the vendored@pierre/diffsworker assets) from the package'sDiffViewerBundleresources. The bundle, its worker, and the patch are same-origin, so no network or ATS exposure and the React app's relative URLs resolve unchanged.DiffViewerHostHTMLgenerates thecmux-diff-viewer-configpage the bundle reads inmountDiffSurface, withassets.*ModuleURLmatching the desktop'sensureDiffViewerAssetsdirectory layout (cmux-webviews-app/,pierre-diffs-1.2.7-trees-1.0.0-beta.4/), so the bundle boots without changes.Data source
Mirrors the desktop's default "View diff" source (
launchDiffViewerProcess→cmux diff --unstaged). A newmobile.workspace.diffRPC:workspace_id→Workspace.currentDirectoryon the main actor (Sources/TerminalController.swiftv2MobileWorkspaceDiff), then runsgit diff --no-ext-diff --no-color --binary --off the main actor (Task.detached) — byte-identical arguments to the CLI diff command (gitDiffPatchArguments). Patch text is capped (4 MiB) with atruncatedflag and a bounded git timeout.mobileHostHandleRPCallowlist switch and is fully Stack-auth gated like the rest of the mobile data plane (it falls into thedefaultauth-required branch inMobileCoreRPCClient).workspace.diff.v1inmobileHostCapabilities. The button is intentionally always shown (not capability-gated): an older Mac answersmethod_not_found, surfaced as the friendly "Couldn't load diff" + Retry state.MobileShellComposite.fetchWorkspaceDiff(workspaceID:)issues the RPC and decodes off-main intoMobileWorkspaceDiff(CmuxMobileShellModel).Bundle sync
SwiftPM resources must live in-package, so the iOS package embeds a copy of the desktop bundle.
scripts/sync-ios-diff-viewer-bundle.shkeeps it in lockstep, is called frombuild-webviews-app.sh, and a--checkguard runs in CI right next to the desktop bundle check so the copy can't silently drift.Verification
DiffViewerBundleresource tree (incl.main.mjs,diffs.mjs,worker-portable.js) is embedded incmux.app.swift testforCmuxMobileDiffViewer: 3 tests pass (host-HTML config shape, asset URLs,</script>escaping).xcodebuild testlocally (guarded); these are build-verify + package-level checks.Dogfood checks (need a paired Mac + phone)
These are runtime paths that can't be verified from a build:
@pierre/diffsworker is loaded asnew Worker(url, { type: "module" })from the custom scheme; module-worker-over-custom-scheme is a known iOS WebKit sharp edge. Not a blocker for read-only review: the diff structure (files, hunks, +/- lines) renders from the parsed patch on the main thread;CodeVieweven exposesdisableWorkerPool, so the worker only adds syntax-highlight colors. If it fails on iOS, the diff is still fully readable (monochrome) and highlighting is a follow-up.Bundle.moduleruntime lookup of the copied resource tree inside the running app.🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Medium Risk
Touches mobile RPC/git output, large embedded web assets, and WKWebView custom-scheme loading (including diff workers); scope is read-only and auth-gated but runtime WebKit behavior needs dogfooding.
Overview
Adds a read-only git diff viewer on iOS: a workspace View diff sheet loads the unstaged patch from the paired Mac and renders it with the same desktop
webviewsdiff React bundle inside aWKWebView.Introduces
Packages/CmuxMobileDiffViewer, which embeds a copiedDiffViewerBundle, serves host HTML + assets +diff.patchover an app-privatecmux-diff://scheme, and generatescmux-diff-viewer-configURLs to match desktopensureDiffViewerAssetslayout.scripts/sync-ios-diff-viewer-bundle.shkeeps the embedded bundle aligned with desktop output (hooked frombuild-webviews-app.sh); CIreact-apps-checknow runs that script with--check.On the data path,
mobile.workspace.diffon the Mac runsgit diff --unstaged(capped/truncated) for aworkspace_id; the iOS shell calls it viafetchWorkspaceDiffintoMobileWorkspaceDiff.CmuxMobileShellUIwires the toolbar button andMobileDiffViewerView(loading/empty/error/truncated, localized).Reviewed by Cursor Bugbot for commit 73726ca. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds a read-only git diff viewer to the iOS app so you can review workspace changes on your phone. Reuses the desktop diff React bundle for a consistent look and faster delivery.
CmuxMobileDiffViewerSwift package that embeds the desktopwebviewsReact bundle and serves it in aWKWebViewvia a privatecmux-diff://scheme; all assets (including@pierre/diffsworker) are same-origin with no network/ATS exposure.mobile.workspace.diffRPC to fetch the workspace’s unstaged patch from the paired Mac (same git args as the CLI), with a 4 MiB cap, bounded timeout, auth gating, andworkspace.diff.v1capability signaling; friendly retry on unsupported hosts.scripts/sync-ios-diff-viewer-bundle.sh --check.Written for commit 73726ca. Summary will update on new commits.