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 3c390a1f5c..ecd2b44ffa 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,48 @@ function rowWithText( } describe("native terminal selection", () => { + it.each([ + { startCol: 1, endCol: 8, expected: "한글 문장" }, + { startCol: 3, endCol: 8, expected: "글 문장" }, + { startCol: 1, endCol: 1, expected: "한" }, + { startCol: 0, endCol: 0, expected: "한" }, + { startCol: 0, endCol: 1, expected: "한" }, + { startCol: 4, endCol: 6, expected: " 문" }, + ])( + "copies complete wide characters for selected columns $startCol through $endCol", + async ({ startCol, endCol, expected }) => { + const terminal = createNativeHeadlessTerminal({ rows: 3, cols: 30 }); + const copied: string[] = []; + try { + await terminal.write("한글 문장"); + const bounds = terminal.getBufferBounds(); + const selection = createTerminalSelectionModel(); + selection.begin({ + coordinate: { row: bounds.cursorRow, col: endCol }, + bounds, + }); + selection.update({ + coordinate: { row: bounds.cursorRow, col: startCol }, + bounds, + }); + + await copyTerminalSelection({ + terminal, + selection: selection.getSnapshot().range, + clipboard: { + writeText: async (text) => { + copied.push(text); + }, + }, + }); + + expect(copied).toEqual([expected]); + } finally { + terminal.dispose(); + } + }, + ); + it("copies Korean output without adding spaces for continuation cells", async () => { const terminal = createNativeHeadlessTerminal({ rows: 3, cols: 30 }); const copied: string[] = []; diff --git a/packages/app/src/terminal/native-renderer/terminal-selection.ts b/packages/app/src/terminal/native-renderer/terminal-selection.ts index 02fb415aac..d222a4cc90 100644 --- a/packages/app/src/terminal/native-renderer/terminal-selection.ts +++ b/packages/app/src/terminal/native-renderer/terminal-selection.ts @@ -324,9 +324,13 @@ export function extractTerminalSelectedText(input: TerminalSelectedTextInput): s const cells = window.rows[offset] ?? []; const startsOnThisRow = row === selection.start.row; const endsOnThisRow = row === selection.end.row; - const startCol = startsOnThisRow ? selection.start.col : 0; + let startCol = Math.max(0, startsOnThisRow ? selection.start.col : 0); + // Selecting either half highlights the whole wide character, so copy its leading cell too. + if (startCol > 0 && cells[startCol]?.width === 0) { + startCol -= 1; + } const endCol = endsOnThisRow ? selection.end.col : cells.length - 1; - const selectedCells = cells.slice(Math.max(0, startCol), Math.max(0, endCol) + 1); + const selectedCells = cells.slice(startCol, Math.max(0, endCol) + 1); const line = selectedCells .filter((cell) => cell.width !== 0) .map((cell) => cell.char)