# fix\(web\): keep wrapped file edits at the caret · PR \#24582 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/24582)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: pingdotgg/t3code

Source branch: fix/issue-7907

Target branch: main

Head commit: 484d9a98b7c5e46f6984567a0d78aa847dc21787

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/24582)

[Commits](https://git.cafe/pingdotgg/t3code/pull/24582/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/24582/diffs?format=markdown)

## Description

---

## Problem

With word wrap enabled in Files, pressing Enter at the end of a 999-line file jumps the viewport away from the caret. A narrow editor makes the failure visible: line 1000 is added, but the view moves back to roughly lines 874–880.

## Change

Keep the known changed-line boundary when the line-number gutter gains or loses a digit. The existing Pierre dependency patch now preserves measurements for unchanged wrapped rows; rendered rows are still remeasured. Update the patch hash and add a real Chromium regression for the EOF edit.

## Scope and approval

Fixes https://github.com/pingdotgg/t3code/issues/7907
GitCafe: https://git.cafe/pingdotgg/t3code/issues/7907
Triaged bug via the automated issue flow.

## Verification

Both independent review models returned MERGE in round 2; proof audit marked all four rows sufficient. Captures are from the pre-main-merge implementation commit; the merge adds unrelated route-authentication changes from main; the editor implementation is unchanged. A-before’s narrated wall-clock span is 19.633 seconds but the encoded clip is 16.8573 seconds: its visible sequence is verified, exact timestamp mapping is not.

- Regression fails on the baseline with visible indexes 873–880 instead of the expected new row 999; passes three times with the fix.
- Existing file-editor virtualization tests: 9 passed.
- Targeted lint, web package typecheck, diff whitespace, and frozen dependency install passed.
- Real app: forward/reverse digit-boundary edits, repeated cycles, Page Up/return to EOF, same-digit and character-edit controls, and wrap-off control. Before/after files agree byte-for-byte for each scenario.
- Desktop execution was not verified; it shares the web editor. Native mobile does not use this DOM editor. Hosted/relay/tunnel execution was not verified. The reported exception was not reproduced; this fixes the reproduced scroll jump.

[Fixture matrix](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a053f511d62fa55e/issue-7907-matrix.md) · [Evidence index and uploaded fixture/driver scripts](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e465b46a9fa0b7a9/index.md)

### Evidence

Diff0 is absolute per-channel RGB difference at gain 1 (zero amplification). DiffT uses per-channel absolute difference >16/255 =>255, otherwise 0. No masks, alignment, normalization, cropping or amplification. Both are whole-capture comparisons including incidental UI differences. Stable1 and Stable2 captures were separated by at least 500 ms after a 2500-ms settlement.

| Row | Conditions (CSS px) | Before | Before SHA | After | After SHA | Diff0 | DiffT (>16) |
|---|---|---|---|---|---|---|---|
| A | 1280×800; editor 539×708; dark, wrap on | ![Before still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/2b780df83b444769/A-before-enter-stable1.png) | [909e4c9482](https://git.cafe/pingdotgg/t3code/commit/909e4c948278afc2b3e1e665075fb1755a13f894) | ![After still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f4c092500e169d5b/A-after-enter-stable1.png) | [8a66d607f9](https://git.cafe/pingdotgg/t3code/commit/8a66d607f9ade09f546767eeda8baf59d67cd35f) | ![Diff0](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/08e4b067bf28cef6/A-diff0.png) | ![DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c8109c2d7a9fcf0b/A-diffT16.png) |
| B | 1440×900; editor 539×808; light, wrap on | ![Before still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1d6c8b9256736d91/B-before-enter-stable1.png) | [909e4c9482](https://git.cafe/pingdotgg/t3code/commit/909e4c948278afc2b3e1e665075fb1755a13f894) | ![After still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b5f72a5882609a3e/B-after-enter-stable1.png) | [8a66d607f9](https://git.cafe/pingdotgg/t3code/commit/8a66d607f9ade09f546767eeda8baf59d67cd35f) | ![Diff0](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d97e6f8c1ca3a86b/B-diff0.png) | ![DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7f4ca572afa4f7f2/B-diffT16.png) |
| C | 390×844; editor 342.195×752; dark, wrap on; narrow web | ![Before still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/8ae87c7aec657b2d/C-before-enter-stable1.png) | [909e4c9482](https://git.cafe/pingdotgg/t3code/commit/909e4c948278afc2b3e1e665075fb1755a13f894) | ![After still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e181fce5e32b9230/C-after-enter-stable1.png) | [8a66d607f9](https://git.cafe/pingdotgg/t3code/commit/8a66d607f9ade09f546767eeda8baf59d67cd35f) | ![Diff0](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/6880a2ce8987584e/C-diff0.png) | ![DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/573b99bf1f6f42f1/C-diffT16.png) |
| D | 1280×800; editor 539×708; dark, wrap off | ![Before still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/de23e1bc07b5fe94/D-before-enter-stable1.png) | [909e4c9482](https://git.cafe/pingdotgg/t3code/commit/909e4c948278afc2b3e1e665075fb1755a13f894) | ![After still](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f467527aac8b7a30/D-after-enter-stable1.png) | [8a66d607f9](https://git.cafe/pingdotgg/t3code/commit/8a66d607f9ade09f546767eeda8baf59d67cd35f) | ![Diff0](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/720093d14282c412/D-diff0.png) | ![DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d0c48cfeedbb486b/D-diffT16.png) |

## Recordings and narrated sequence

**A:** [Before recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/66da8d0acd6d5910/A-before.mp4) · [After recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f542c4b69f146d4d/A-after.mp4)

**B:** [Before recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/785c1361db7254f2/B-before.mp4) · [After recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d28755c80e28a681/B-after.mp4)

**C:** [Before recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/9284ff0ed841ba07/C-before.mp4) · [After recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/2d0df7938ff28ca9/C-after.mp4)

**D:** [Before recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/87f9cb638183ea09/D-before.mp4) · [After recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/41bd7a71d6cc3268/D-after.mp4)

All start with focused real EOF 999 (selection offset 240). Ctrl+End was repeated until actual EOF was rendered and measured. Unique fixture names avoid cache/save races. One second initial hold, then Enter 999→1000, hold 2.5 s, Backspace 1000→999, hold 2.5 s, repeat the cycle. Page Up, then Ctrl+End and a 2.5 s hold close each recording. A used a 1 s Page Up hold; B–D used 0.7 s. A's 500-ms still pair is from a supplementary identical Enter/settle/Backspace cycle after the clip. B–D include their still pairs inside the clips.

B additionally enters twice after the boundary round-trip, ending at 1001 (same-digit 1000→1001 control). C additionally types x after the boundary round-trip, ending at 999 with x appended. A and D end byte-for-byte equal to the initial fixture. Saved B files have exactly two extra LF bytes; C files have exactly one extra x byte. Both revisions' final file bytes agree per row.

| Row | Before recording UTC | After recording UTC | Observed after first Enter |
|---|---|---|---|
| A | 22:43:19.433–22:43:39.066 | 22:43:43.171–22:44:01.128 | Before shows about 874–880; after shows EOF/1000. |
| B | approximately 22:45:44–22:46:07.488 | approximately 22:46:09–22:46:32.017 | Before shows about 873–880; after shows EOF/1000; same-digit control ends at 1001. |
| C | approximately 22:47:27–22:47:46.851 | approximately 22:47:47–22:48:06.746 | Before shows about 865–869; after shows EOF/1000; final x visible at 999. |
| D | approximately 22:50:33–22:50:51.609 | approximately 22:50:52–22:51:11.114 | Both show EOF/1000, coherent return to 999 and horizontal line end. |

Exact B–D key, screenshot, recording timestamps are retained in the action logs (UTC, not video-relative). Contact sheets sample each video every two seconds; the original clips retain the full captured sequence. Every original saved still was opened directly or in a labeled review sheet; all eight video contact sheets and every diff were opened and inspected. No isolated harness was used.

## Environment and limits

Linux x86_64, Chrome/154.0.0.0; DPR 2; default browser zoom. Computed editor font: 13px / 20px ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace. Font status loaded; document.fonts.ready awaited on final metadata read. A/D desktop PNGs are 1280×800; B PNGs are scaled by the preview capture to 1280×800 despite the measured 1440×900 CSS viewport. C PNGs are 780×1688 (DPR 2). Pixel statistics refer to stored PNG pixels, not CSS pixels.

Actual prefers-reduced-motion was false; the exposed preview appearance tool does not provide reduced-motion emulation. App panelAnimationDurationMs was 0. Strict network-idle was not asserted: the real app continues observability POSTs. Editor layout settlement and 500-ms frame-pair stability were checked instead. These two capture-environment preferences remain unverified, not silently claimed satisfied.

Before origin localhost:7554 serves the old digit-count conditional; after localhost:6767 serves change.startLine unconditionally. This was confirmed by fetching each running origin's optimized @pierre_diffs_edit.js through its owned preview tab, in addition to checking the checkout SHAs. Desktop execution, native mobile, hosted/relay/tunnel modes remain untested as scoped by the matrix.

## WHAT ELSE CHANGED

Across before/after pairs: unique filenames differ in active tab labels and file breadcrumbs; earlier opened fixture tabs also differ by before/after suffix. Thread age labels differ because isolated environments have different creation times. A/B differ in editor visible logical rows and caret/highlight state because base loses EOF; candidate keeps line 1000 visible. The extra digit expands the gutter and shifts text start in both revisions. Narrow C has clipped/ellipsized tab and breadcrumb labels, the normal right-side sheet with a small chat sliver at left, and no explorer obstruction; these are shared layout conditions, not introduced by the patch. D text/gutter geometry matches; its diff is limited to filenames, age label, caret phase and tiny capture edges.

Within stable pairs: A before and C before are pixel identical. A/C after and both D pairs change only the blinking caret (60 stored pixels). B before changes a one-pixel sidebar edge outside the editor (455 stored pixels); B after has a sidebar-edge/caret phase difference (224 stored pixels). No text reflow or editor scroll change is visible between the paired settled captures. No other visible functional change was identified. An existing narrow tab strip can show an earlier tab label while the active file breadcrumb names C; captured honestly, not changed or investigated beyond scope.


GPT-6.1-Sol and Claude Opus 5.5 via Codex and Claude Agent harnesses in T3 Code.

