From 9bd58f66b2dfc195898bf1178e84f02933c0d41e Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:57:04 +0200 Subject: [PATCH] Copy Korean terminal text without extra spaces (#7254) * fix(terminal): preserve Korean text when copying native terminal cells * fix(terminal): omit continuation cells from copied text Native terminal buffers already retain authoritative cell widths. Skip zero-width continuation cells when serializing a selection so Korean text copies with only its real spaces, while preserving the buffer representation. Co-authored-by: helloimdevman <333048235+helloimdevman@users.noreply.github.com> --------- Co-authored-by: helloimdevman <333048235+helloimdevman@users.noreply.github.com> Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> --- .../terminal-selection.test.ts | 45 +++++++++++++++++++ .../native-renderer/terminal-selection.ts | 5 ++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/app/src/terminal/native-renderer/terminal-selection.test.ts b/packages/app/src/terminal/native-renderer/terminal-selection.test.ts index 5f2a4bb593..3c390a1f5c 100644 --- a/packages/app/src/terminal/native-renderer/terminal-selection.test.ts +++ b/packages/app/src/terminal/native-renderer/terminal-selection.test.ts @@ -41,6 +41,51 @@ function rowWithText( } describe("native terminal selection", () => { + it("copies Korean output without adding spaces for continuation cells", async () => { + const terminal = createNativeHeadlessTerminal({ rows: 3, cols: 30 }); + const copied: string[] = []; + try { + await terminal.write("한글 문장"); + const bounds = terminal.getBufferBounds(); + await copyTerminalSelection({ + terminal, + selection: { + start: { row: bounds.cursorRow, col: 0 }, + end: { row: bounds.cursorRow, col: bounds.cursorCol - 1 }, + coordinateEpoch: bounds.coordinateEpoch, + }, + clipboard: { + writeText: async (text) => { + copied.push(text); + }, + }, + }); + expect(copied).toEqual(["한글 문장"]); + } finally { + terminal.dispose(); + } + }); + + it("copies Korean text and real spaces without wide-character continuation spaces", async () => { + const terminal = createNativeHeadlessTerminal({ rows: 3, cols: 30 }); + try { + await terminal.write("한글 문장 | 성공"); + const bounds = terminal.getBufferBounds(); + expect( + extractTerminalSelectedText({ + terminal, + selection: { + start: { row: bounds.cursorRow, col: 0 }, + end: { row: bounds.cursorRow, col: bounds.cursorCol - 1 }, + coordinateEpoch: bounds.coordinateEpoch, + }, + }), + ).toBe("한글 문장 | 성공"); + } finally { + terminal.dispose(); + } + }); + it("expands a long-press coordinate to the whole terminal word", async () => { const terminal = createNativeHeadlessTerminal({ rows: 4, cols: 32, scrollbackLines: 20 }); await terminal.write("before PASEO_TARGET after\r\n"); diff --git a/packages/app/src/terminal/native-renderer/terminal-selection.ts b/packages/app/src/terminal/native-renderer/terminal-selection.ts index 1bda469e42..02fb415aac 100644 --- a/packages/app/src/terminal/native-renderer/terminal-selection.ts +++ b/packages/app/src/terminal/native-renderer/terminal-selection.ts @@ -327,7 +327,10 @@ export function extractTerminalSelectedText(input: TerminalSelectedTextInput): s const startCol = startsOnThisRow ? selection.start.col : 0; const endCol = endsOnThisRow ? selection.end.col : cells.length - 1; const selectedCells = cells.slice(Math.max(0, startCol), Math.max(0, endCol) + 1); - const line = selectedCells.map((cell) => cell.char || " ").join(""); + const line = selectedCells + .filter((cell) => cell.width !== 0) + .map((cell) => cell.char) + .join(""); if (offset > 0 && window.wrappedRows[offset] !== true) { text += "\n"; }