Compare commits

...
Author SHA1 Message Date
Mohamed Boudra 0d88211636 fix(terminal): preserve adjacent wide characters
Terminal snapshots converted zero-width continuation cells into spaces, which altered captured and restored output.
2026-07-22 12:13:27 +00:00
4 changed files with 43 additions and 3 deletions
@@ -7,6 +7,20 @@ function cells(text: string): TerminalState["grid"][number] {
}
describe("renderTerminalSnapshotToAnsi", () => {
it("preserves adjacent wide characters", () => {
const state: TerminalState = {
rows: 1,
cols: 4,
scrollback: [],
grid: [[{ char: "\u4e2d" }, { char: "" }, { char: "\u6587" }, { char: "" }]],
cursor: { row: 0, col: 4 },
};
const ansi = renderTerminalSnapshotToAnsi(state);
expect(ansi).toContain("\u4e2d\u6587");
});
it("renders soft-wrapped rows as one contiguous logical line when wrap flags are present", () => {
// The server soft-wrapped one logical line "ABCDEFGHIJKLMNOP" at 10 cols into
// two grid rows. gridWrapped[0] = true marks row 0 as continuing into row 1.
+4 -1
View File
@@ -87,12 +87,15 @@ function renderTerminalRow(row: TerminalCell[], padToCols?: number): string {
for (let index = 0; index < length; index += 1) {
const cell = row[index] ?? { char: " " };
if (cell.char === "") {
continue;
}
const nextStyle = getTerminalStyle(cell);
if (!terminalStylesEqual(previousStyle, nextStyle)) {
output.push(styleToAnsi(nextStyle));
previousStyle = nextStyle;
}
output.push(cell.char || " ");
output.push(cell.char);
}
if (!terminalStylesEqual(previousStyle, DEFAULT_STYLE)) {
@@ -1626,6 +1626,29 @@ describe("capture", () => {
expect(capture.totalLines).toBeGreaterThan(0);
}, 15000);
test("preserves adjacent CJK characters in captured output", async () => {
const cwd = tmpCwd();
tempDirs.push(cwd);
const created = await createTerminalInWorkspace(ctx.client, { cwd });
const terminalId = created.terminal!.id;
await ctx.client.subscribeTerminal(terminalId);
ctx.client.sendTerminalInput(terminalId, {
type: "input",
data: `node -e "process.stdout.write('START\\u4e2d\\u6587END\\n')"\r`,
});
await waitForTerminalOutput(
ctx.client,
terminalId,
(text) => text.includes("START\u4e2d\u6587END"),
15000,
);
const capture = await ctx.client.captureTerminal(terminalId);
expect(capture.lines).toContain("START\u4e2d\u6587END");
}, 15000);
test("captures with start/end line range", async () => {
const cwd = tmpCwd();
tempDirs.push(cwd);
+2 -2
View File
@@ -494,7 +494,7 @@ function extractCell(terminal: TerminalType, row: number, col: number): Terminal
const bg = bgMode !== 0 ? cell.getBgColor() : undefined;
return {
char: cell.getChars() || " ",
char: cell.getWidth() === 0 ? "" : cell.getChars() || " ",
fg,
bg,
fgMode: fgMode !== 0 ? fgMode : undefined,
@@ -553,7 +553,7 @@ function extractScrollback(
const fg = fgMode !== 0 ? cell.getFgColor() : undefined;
const bg = bgMode !== 0 ? cell.getBgColor() : undefined;
rowCells.push({
char: cell.getChars() || " ",
char: cell.getWidth() === 0 ? "" : cell.getChars() || " ",
fg,
bg,
fgMode: fgMode !== 0 ? fgMode : undefined,