diff --git a/app/src/lib/xtermLineJoin.test.ts b/app/src/lib/xtermLineJoin.test.ts index b034d41..b8cf530 100644 --- a/app/src/lib/xtermLineJoin.test.ts +++ b/app/src/lib/xtermLineJoin.test.ts @@ -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"); diff --git a/app/src/lib/xtermLineJoin.ts b/app/src/lib/xtermLineJoin.ts index 28fc63b..0e8ac1a 100644 --- a/app/src/lib/xtermLineJoin.ts +++ b/app/src/lib/xtermLineJoin.ts @@ -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-/…` 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;