Measured wrapping is the best next experiment. Large-diff setup still needs work.

All five alternatives were prototyped locally, plus the original PR baseline. The measured version kept the existing renderer, used cheap column arithmetic for printable ASCII, and measured other text in one pass with boundary validation. Nothing was pushed.

Option1,000 rows10,000 rowsCached drawObserved result
Current PR12.7 ms122 ms2.53 msClips CJK
Measured + ASCII shortcut39.0 ms399 ms2.49 ms81/81 fitting and cluster checks passed
Unicode width estimate73.6 ms739 ms2.39 msClips emoji and combining text
Native Android layout85.3 ms835 ms2.40 msText fit checked; tab highlights need integration
ASCII wrap + scroll fallback9.4 ms87 ms6.13 msWorks as a compromise; Unicode stays unwrapped
Wrapping off1.2 ms12 ms8.63 msFast setup; loses wrapping

Layout medians on Android 15 x86_64 emulator: 80% ASCII, 10% CJK, 10% emoji; 36px monospace, 840px width. Three warm-ups, 15 shuffled samples for 1,000 rows, five for 10,000. Cached draw is software Canvas CPU time for a viewport over a separate Unicode-heavy dataset, not GPU frame completion or phone performance.

Recommendation: continue with measured wrapping and the ASCII shortcut, then address large initial layouts and reuse unchanged row layouts. Keep horizontal scrolling disabled in full-wrap mode. Do not merge these prototypes as-is.

Scope: the actual PR Android canvas code was copied into a standalone local test app at f95e22ae670edd48bce3e840e47f44a412097f80. Nine fixtures × three fonts × three widths gave 81 cases per option; 36 native selection/pan checks passed. Native layout had two advance-width warnings; wider-bitmap checks found zero ink outside the viewport. This is not a full T3 integration pass. iOS, physical devices, syntax-token/font-style interactions, complete bidirectional text correctness, allocations, and the real comment editor remain unverified. The disabled helper's ~1 ms cost is experiment overhead. Source, APK, and raw evidence remain in /tmp/t3-wrap-lab.

Original PR: CJK runs extend beyond the right edge.
Baseline canvas with clipped CJK text
Measured prototype: the same CJK text fits, with line 2 preserved.
Measured wrapping with complete CJK lines
Other prototypes and raw measurements
Unicode estimate still clips emoji.
Native layout changes tabs and spacing.
Fallback after panning: ASCII stays fixed.
Wrapping disabled.
{
  "layout": [
    {
      "mode": "PR baseline",
      "1000_p50_ms": 12.678303,
      "1000_p95_ms": 15.958839,
      "10000_p50_ms": 122.331615,
      "raw_1000_ms": [
        14.164762,
        13.580035,
        11.774156,
        12.678303,
        15.958839,
        13.628967,
        12.681609,
        10.999251,
        12.911161,
        12.114294,
        12.295835,
        10.233685,
        12.385233,
        12.529644,
        12.700745
      ]
    },
    {
      "mode": "Measured + ASCII fast path",
      "1000_p50_ms": 39.002875,
      "1000_p95_ms": 44.686531,
      "10000_p50_ms": 399.472407,
      "raw_1000_ms": [
        44.686531,
        39.282781,
        39.002875,
        38.473982,
        40.732562,
        38.157569,
        37.598309,
        39.659547,
        38.105761,
        39.589597,
        38.710426,
        40.806841,
        38.659581,
        37.87018,
        39.409529
      ]
    },
    {
      "mode": "Unicode columns",
      "1000_p50_ms": 73.58309,
      "1000_p95_ms": 79.041595,
      "10000_p50_ms": 738.567565,
      "raw_1000_ms": [
        78.415159,
        73.346567,
        77.204898,
        74.131781,
        79.041595,
        72.418264,
        71.483611,
        71.434659,
        72.768192,
        72.137438,
        75.297078,
        75.776678,
        73.58309,
        74.156677,
        72.536277
      ]
    },
    {
      "mode": "Native layout",
      "1000_p50_ms": 85.335856,
      "1000_p95_ms": 88.682096,
      "10000_p50_ms": 834.888357,
      "raw_1000_ms": [
        84.388099,
        86.814332,
        88.567562,
        84.360667,
        87.820279,
        85.461714,
        85.720109,
        85.335856,
        82.995565,
        85.4495,
        83.290699,
        88.682096,
        84.277381,
        83.652938,
        83.667547
      ]
    },
    {
      "mode": "ASCII wrap + pan",
      "1000_p50_ms": 9.448221,
      "1000_p95_ms": 13.034402,
      "10000_p50_ms": 86.817147,
      "raw_1000_ms": [
        8.848105,
        10.810378,
        11.781711,
        9.978977,
        9.779362,
        8.246386,
        7.975818,
        9.370485,
        9.091732,
        9.18656,
        9.448221,
        13.034402,
        9.462518,
        7.581828,
        9.705774
      ]
    },
    {
      "mode": "Wrap disabled",
      "1000_p50_ms": 1.232023,
      "1000_p95_ms": 3.481063,
      "10000_p50_ms": 12.295395,
      "raw_1000_ms": [
        1.563224,
        1.558996,
        1.565127,
        1.303777,
        1.221954,
        1.213928,
        1.24143,
        1.212786,
        1.215781,
        1.222194,
        1.226181,
        2.438988,
        1.21524,
        3.481063,
        1.232023
      ]
    }
  ],
  "long_lines": [
    {
      "mode": "PR baseline",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 0.371236
    },
    {
      "mode": "PR baseline",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 0.369573
    },
    {
      "mode": "PR baseline",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 1.635398
    },
    {
      "mode": "Measured + ASCII fast path",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 0.196459
    },
    {
      "mode": "Measured + ASCII fast path",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 13.091498
    },
    {
      "mode": "Measured + ASCII fast path",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 10.794678
    },
    {
      "mode": "Unicode columns",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 8.231117
    },
    {
      "mode": "Unicode columns",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 8.010684
    },
    {
      "mode": "Unicode columns",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 1.882593
    },
    {
      "mode": "Native layout",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 43.880369
    },
    {
      "mode": "Native layout",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 7.059127
    },
    {
      "mode": "Native layout",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 17.474113
    },
    {
      "mode": "ASCII wrap + pan",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 0.56483
    },
    {
      "mode": "ASCII wrap + pan",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 0.001793
    },
    {
      "mode": "ASCII wrap + pan",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 0.001753
    },
    {
      "mode": "Wrap disabled",
      "fixture": "long",
      "utf16_length": 20000,
      "p50_ms": 0.001803
    },
    {
      "mode": "Wrap disabled",
      "fixture": "long-CJK",
      "utf16_length": 20000,
      "p50_ms": 0.001733
    },
    {
      "mode": "Wrap disabled",
      "fixture": "long-emoji",
      "utf16_length": 19800,
      "p50_ms": 0.001734
    }
  ],
  "frames": [
    {
      "mode": "PR baseline",
      "10000_rows_setup_ms": 236.773044,
      "cached_software_draw_p50_ms": 2.529627,
      "cached_software_draw_p95_ms": 6.210245,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    },
    {
      "mode": "Measured + ASCII fast path",
      "10000_rows_setup_ms": 1722.128602,
      "cached_software_draw_p50_ms": 2.493399,
      "cached_software_draw_p95_ms": 3.412464,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    },
    {
      "mode": "Unicode columns",
      "10000_rows_setup_ms": 1242.44234,
      "cached_software_draw_p50_ms": 2.394945,
      "cached_software_draw_p95_ms": 3.840727,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    },
    {
      "mode": "Native layout",
      "10000_rows_setup_ms": 1653.967996,
      "cached_software_draw_p50_ms": 2.401376,
      "cached_software_draw_p95_ms": 3.304762,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    },
    {
      "mode": "ASCII wrap + pan",
      "10000_rows_setup_ms": 57.663405,
      "cached_software_draw_p50_ms": 6.125215,
      "cached_software_draw_p95_ms": 7.308055,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    },
    {
      "mode": "Wrap disabled",
      "10000_rows_setup_ms": 17.181143,
      "cached_software_draw_p50_ms": 8.629104,
      "cached_software_draw_p95_ms": 10.528198,
      "frame_interval_p50_ms": 16.666666,
      "frame_interval_p95_ms": 16.666666,
      "frames_over_25ms": 0
    }
  ]
}