fix(mobile-web): collapse carriage-return redraws in the console terminal
readline repaints its prompt in place with a lone \r (…└─# \r└─# cmd); the browser turned that \r into a newline, stacking the pre-redraw prompt as a phantom extra line (the mobile-only "extra --#"). Add a collapseCR() mini-vt pass that resolves \r as a column-0 overwrite (ANSI escapes treated as zero-width so colours survive; honours \x1b[K erase-to-EOL) before ansiToHtml, so the real prompt stays and only the duplicate is dropped. TUI was already correct via its real vt emulator. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,40 @@ function ansiToHtml(raw) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ── carriage-return overwrite (mini vt) ──
|
||||
A real terminal treats a lone \\r as "cursor to column 0" so readline's
|
||||
in-place prompt redraw (…└─# \\r└─# cmd) overwrites the old line. The DOM
|
||||
instead turns \\r into \\n, stacking the pre-redraw prompt as a phantom
|
||||
extra line. Resolve overwrites per \\n-delimited line, treating ANSI escapes
|
||||
as zero-width (attached to the next column) so colours survive; honour
|
||||
erase-to-EOL (\\x1b[K) so a shrinking redraw leaves no tail chars. */
|
||||
function collapseCR(raw) {
|
||||
const escRe = /^(?:\\x1b\\[[0-9;?]*[ -\\/]*[@-~]|\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x1b[@-Z\\\\-_])/;
|
||||
const elRe = /^\\x1b\\[[02]?K$/;
|
||||
const lines = [];
|
||||
let cells = [], col = 0, pending = "";
|
||||
function flush() {
|
||||
let s = "";
|
||||
for (let k = 0; k < cells.length; k++) s += cells[k] ? (cells[k].pre + cells[k].ch) : "";
|
||||
lines.push(s + pending); pending = ""; cells = []; col = 0;
|
||||
}
|
||||
for (let i = 0; i < raw.length; ) {
|
||||
const m = escRe.exec(raw.slice(i));
|
||||
if (m) {
|
||||
const seq = m[0];
|
||||
if (elRe.test(seq)) cells.length = col; // erase to end of line
|
||||
else pending += seq; // zero-width; ride the next char
|
||||
i += seq.length; continue;
|
||||
}
|
||||
const ch = raw[i++];
|
||||
if (ch === "\\n") flush();
|
||||
else if (ch === "\\r") col = 0; // overwrite from column 0
|
||||
else { cells[col] = {pre: pending, ch: ch}; pending = ""; col++; }
|
||||
}
|
||||
flush();
|
||||
return lines.join("\\n");
|
||||
}
|
||||
|
||||
/* ── terminal poll ── */
|
||||
async function pollScreen() {
|
||||
for (;;) {
|
||||
@@ -204,7 +238,7 @@ async function pollScreen() {
|
||||
termSeq = d.seq;
|
||||
const t = $("term");
|
||||
const bottom = t.scrollHeight - t.scrollTop - t.clientHeight < 40;
|
||||
if (d.text && d.text.length) t.innerHTML = ansiToHtml(d.text);
|
||||
if (d.text && d.text.length) t.innerHTML = ansiToHtml(collapseCR(d.text));
|
||||
if (bottom) t.scrollTop = t.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user