fix(links): require an indented continuation row before bridging a hard wrap

Two column-0 lines of shell output where the first happens to fill the
row (a 40-char path at 40 cols, then src/main.rs) were glued into one
bogus path. Claude Code draws continuation rows at least 2 cells in, so
require that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Opus 5.5 committed 2026-11-02 10:42:06 -08:00
1 parent a1cc9713a9
commit c992a7959e
2 files changed
+15 -2

No files matched your search

+9
View File
@@ -88,6 +88,15 @@ describe("joinWrappedRows across Claude Code's hard wraps", () => {
expect(joinWrappedRows(b, 1, { cols }).text).toBe(" src/main.rs for details");
});
it("does not glue two column-0 lines together (continuations are indented)", () => {
const cols = 40;
const row0 = "/var/log/some/very/long/dir/file-aaa.txt";
expect(row0.length).toBe(cols);
const b = buffer([[row0, false], ["src/main.rs", false]]);
expect(joinWrappedRows(b, 0, { cols }).text).toBe(row0);
expect(joinWrappedRows(b, 1, { cols }).text).toBe("src/main.rs");
});
it("does not bridge a row that stops short of the edge", () => {
const b = buffer([["/tmp/claude-1000/-workspace/abc", false], ["def/notes.md", false]]);
expect(joinWrappedRows(b, 1, { cols: 70 }).text).toBe("def/notes.md");
+6 -2
View File
@@ -51,6 +51,8 @@ const TRAILING_PATH_RUN = /[A-Za-z0-9_.\-~+@/]+$/;
const LEADING_PATH_RUN = /^[A-Za-z0-9_.\-~+@/]+/;
/** Cells a full row may stop short of the right edge (Ink boxes pad by one or two). */
const EDGE_SLACK = 2;
/** Claude Code draws its transcript (and so every continuation row) at least this far in. */
const MIN_CONTINUATION_INDENT = 2;
/**
* Is `next` the rest of a word that Claude Code broke at the end of `prev`?
@@ -62,7 +64,9 @@ const EDGE_SLACK = 2;
* It breaks *inside* a word only when the word is longer than the line, which
* is exactly what a long `/tmp/claude-<uid>/…` path is. So bridge only when
* `prev` runs to (within {@link EDGE_SLACK} of) the right edge and ends in a
* path character, `next` starts (after its indent) with one, and the two runs
* path character, `next` starts (after an indent of at least
* {@link MIN_CONTINUATION_INDENT} cells — so two column-0 lines of shell
* output are never glued) with one, and the two runs
* together would not have fit on one line — i.e. word wrap could not have
* moved the word down whole. That last test is what keeps ordinary prose (a
* word that happens to end at the edge above `src/main.rs`) from being glued.
@@ -71,7 +75,7 @@ function hardWrapIndent(prev: string, next: string, cols: number): number | null
if (prev.length < cols - EDGE_SLACK || prev.length > cols) return null;
if (!PATH_CHAR.test(prev[prev.length - 1])) return null;
const indent = next.search(/\S/);
if (indent < 0 || !PATH_CHAR.test(next[indent])) return null;
if (indent < MIN_CONTINUATION_INDENT || !PATH_CHAR.test(next[indent])) return null;
const tail = TRAILING_PATH_RUN.exec(prev)?.[0].length ?? 0;
const head = LEADING_PATH_RUN.exec(next.slice(indent))?.[0].length ?? 0;
if (tail + head <= cols - indent - EDGE_SLACK) return null;