fix(web): gate ANSI filtering behind resumeParam and reuse TextDecoder

Addresses Copilot review:
- Reuse a single TextDecoder instance instead of allocating per message
- Only filter erase codes during session resume (resumeParam != null)
- Extract filter chain into named helper sanitizeResumeOutput()

Refs #47313
This commit is contained in:
灵越羽毛 2026-06-17 18:04:29 +08:00 committed by Austin Pickett
parent 4b6e1e440e
commit 5e37c94906
1 changed files with 17 additions and 11 deletions

View File

@ -989,18 +989,24 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) {
}
};
// Session resume: strip ANSI codes that Ink's two-pass virtual
// scroll unmount emits into the PTY stream (blank-line bursts,
// erase-line, erase-char). Gate on resumeParam so short/new
// sessions never see the filter.
const decoder = new TextDecoder();
const sanitizeResumeOutput = (raw: string): string =>
resumeParam
? raw.replace(/\n{3,}/g, "\n\n")
.replace(/\x1b\[\d*K/g, "")
.replace(/\x1b\[\d*X/g, "")
: raw;
ws.onmessage = (ev) => {
let text: string;
if (typeof ev.data === "string") {
text = ev.data;
} else {
text = new TextDecoder().decode(new Uint8Array(ev.data as ArrayBuffer));
}
term.write(
text.replace(/\n{3,}/g, "\n\n")
.replace(/\x1b\[\d*K/g, "")
.replace(/\x1b\[\d*X/g, "")
);
const text =
typeof ev.data === "string"
? ev.data
: decoder.decode(new Uint8Array(ev.data as ArrayBuffer));
term.write(sanitizeResumeOutput(text));
};
ws.onclose = (ev) => {