Skip to content

fix(mobile): respect word wrap in diffs - #12590

Open
jakeleventhal wants to merge 3 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/mobile-diff-word-wrap
Open

jakeleventhal wants to merge 3 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/mobile-diff-word-wrap

Conversation

@jakeleventhal

@jakeleventhal jakeleventhal commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

What Changed

The mobile diff view now follows the Settings → Appearance → Code & Diffs → Word break setting. When it's on, long lines wrap to the screen width instead of running off-screen.

  • createNativeReviewDiffStyle passes wordWrap through the native diff style.
  • On iOS and Android the diff renderer:
    • splits each code line into segments that fit the screen width and makes the row taller by one line per extra segment. Breaks move back to a character boundary, so emoji and combining marks stay whole;
    • keeps the line number next to the first segment;
    • splits word-diff highlights at the same boundaries;
    • turns off horizontal code panning.
  • The row layout is rebuilt when the view width changes, for example on iPad rotation.
  • Hunk headers stay on one line.
  • Comment card snippets stay unwrapped, because their height is sized from the row count.

Why

The setting only reached the file viewers. The native review diff view never received it, so diff rows kept a fixed height and scrolled sideways, and wrap had no effect on diffs.

Each wrapped row's line starts are computed once during layout, as UTF-16 offsets (the same indices the word-diff ranges use). The row height, the drawn text, and the highlights all read those offsets, so they can't drift apart. TextKit and Android's text layout would each wrap at their own points, especially around tabs, which diff content does not expand.

UI Changes

Offscreen renders of the real T3ReviewDiffView (iOS source, Mac Catalyst UIKit) with the same rows, tokens, and wordWrap: true style. Before uses the parent commit's view; after uses this branch.

Before After
Diff with word wrap on, before: long lines are cut off at the right edge Diff with word wrap on, after: long lines wrap, with word diff highlights split across lines

With wordWrap: false, the before and after renders are byte-identical.

Validation

  • The iOS view type-checks against UIKit (Mac Catalyst SDK) with no errors or warnings. An offscreen render confirms that a combining-mark character on a wrap boundary moves to the next line whole.
  • The Android sources compile with kotlinc against Android API stubs, and pass ktlint and detekt with the repo config. wrapLineStarts was run on ASCII, emoji, combining-mark, and ZWJ-cluster boundary cases.
  • Mobile tsc --noEmit passes.
  • vp test run src/features/review src/features/diffs: 98/98 pass.
  • Formatting of the touched files passes.
  • Not tested: the running app on a simulator or device, which wasn't available.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Model: Claude Opus 5. Harness: Claude Code in T3 Code.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added optional word wrapping for mobile code review diffs.
    • Wrapped code adapts to available width, adjusts row height, and disables horizontal panning.
    • Word-diff highlights, line numbers, and change indicators render correctly across wrapped lines.
    • Appearance settings now control code word wrapping.
  • Bug Fixes

    • Prevented review comment snippets from wrapping unexpectedly.
    • Improved diff rendering when the viewport width changes.

The Code & Diffs "Word break" setting only reached the file viewers. The
native review diff view never received it, so long diff lines always ran
off-screen behind a horizontal pan.

Pass wordWrap through the native diff style. When it is on, iOS and
Android split each code line into fixed-width column segments, grow the
row by one line height per extra segment, split word diff highlights at
the same boundaries, and disable horizontal code panning. Layout rebuilds
when the view width changes. Comment card snippets stay unwrapped because
their height is sized from the row count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 19, 2026
Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift Outdated
@macroscopeapp

macroscopeapp Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — The change wires an existing word-wrap preference into native iOS and Android diff rendering, adding dynamic row layout, multiline drawing, highlight splitting, and scroll behavior. Its cross-platform runtime scope and newly added static-analysis suppressions warrant human review.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: aae4972c-7ba8-428b-adf5-49b4f494ee76

📥 Commits

Reviewing files that changed from the base of the PR and between 16368f1 and f95e22a.

📒 Files selected for processing (3)
  • apps/mobile/modules/t3-review-diff/android/src/main/java/expo/modules/t3reviewdiff/ReviewDiffCanvasDrawing.kt
  • apps/mobile/modules/t3-review-diff/android/src/main/java/expo/modules/t3reviewdiff/T3ReviewDiffView.kt
  • apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The change adds configurable word wrapping to native review-diff views on Android and iOS. Wrapped rows recalculate heights, disable horizontal panning, render code across visual lines, and split word-diff highlights at wrap boundaries.

Changes

Review diff word wrapping

Layer / File(s) Summary
Word-wrap style wiring
apps/mobile/src/features/settings/appearance/useAppearanceCodeSurface.ts, apps/mobile/src/features/review/nativeReviewDiffAdapter.ts, apps/mobile/src/features/review/ReviewCommentCard.tsx, apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
The native style accepts wordWrap from appearance settings. Review comment cards serialize wordWrap: false.
Android wrapped layout and rendering
apps/mobile/modules/t3-review-diff/android/src/main/java/expo/modules/t3reviewdiff/*
Android parses wordWrap, calculates composed-character-safe wrapped row dimensions, disables horizontal panning, and draws wrapped code and word-diff highlights.
iOS wrapped layout and rendering
apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
iOS resolves wordWrap, rebuilds layouts when the viewport changes, uses laid-out row heights for interaction and drawing, disables horizontal panning, and draws wrapped code and highlights.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Appearance
  participant NativeStyle
  participant ReviewDiffView
  participant DiffRenderer
  Appearance->>NativeStyle: provide codeWordBreak
  NativeStyle->>ReviewDiffView: provide wordWrap
  ReviewDiffView->>ReviewDiffView: calculate wrapped row layout
  ReviewDiffView->>DiffRenderer: draw wrapped code and diff highlights
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 44 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: mobile diffs now respect word wrapping.
Description check ✅ Passed The description includes What Changed, Why, UI Changes, validation details, screenshots, and a completed checklist. It clearly explains the implementation and testing scope.
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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

jakeleventhal and others added 2 commits September 19, 2026 09:56
Wrap breaks now move back to a composed character boundary so emoji and
combining marks never split across visual lines. Each wrapped row's line
starts are computed once during layout and shared by the row height, the
drawn segments, and the word diff highlights.

On Android, code text drawing moves into ReviewDiffCanvasDrawing, which
keeps drawLineRow and DiffCanvasView within detekt's size limits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeWrapLayout owns the wrap columns, line height, and per-row line
starts, built by ReviewDiffCanvasDrawing. DiffCanvasView keeps a single
field, which brings it back under detekt's LargeClass threshold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant