fix(tui): place image correctly when viewport height < image height (#4461)
fixes #4415
This commit is contained in:
@@ -82,19 +82,28 @@ export class Image implements Component {
|
||||
this.imageId = result.imageId;
|
||||
}
|
||||
|
||||
// Return `rows` lines so TUI accounts for image height.
|
||||
// First (rows-1) lines are empty and cleared before the image is drawn.
|
||||
// Last line: move cursor back up, draw the image, then move back down
|
||||
// for Kitty (this component disables Kitty's terminal-side cursor movement)
|
||||
// so TUI cursor accounting stays inside the scroll area.
|
||||
lines = [];
|
||||
for (let i = 0; i < result.rows - 1; i++) {
|
||||
lines.push("");
|
||||
if (caps.images === "kitty") {
|
||||
// For Kitty: C=1 prevents cursor movement.
|
||||
// Don't need the cursor movement.
|
||||
lines = [result.sequence];
|
||||
|
||||
// Return `rows` lines so TUI accounts for image height.
|
||||
for (let i = 0; i < result.rows - 1; i++) {
|
||||
lines.push("");
|
||||
}
|
||||
} else {
|
||||
// Return `rows` lines so TUI accounts for image height.
|
||||
// First (rows-1) lines are empty and cleared before the image is drawn.
|
||||
// Last line: move cursor back up, draw the image, then move back down
|
||||
// so TUI cursor accounting stays inside the scroll area.
|
||||
lines = [];
|
||||
for (let i = 0; i < result.rows - 1; i++) {
|
||||
lines.push("");
|
||||
}
|
||||
const rowOffset = result.rows - 1;
|
||||
const moveUp = rowOffset > 0 ? `\x1b[${rowOffset}A` : "";
|
||||
lines.push(moveUp + result.sequence);
|
||||
}
|
||||
const rowOffset = result.rows - 1;
|
||||
const moveUp = rowOffset > 0 ? `\x1b[${rowOffset}A` : "";
|
||||
const moveDown = caps.images === "kitty" && rowOffset > 0 ? `\x1b[${rowOffset}B` : "";
|
||||
lines.push(moveUp + result.sequence + moveDown);
|
||||
} else {
|
||||
const fallback = imageFallback(this.mimeType, this.dimensions, this.options.filename);
|
||||
lines = [this.theme.fallbackColor(fallback)];
|
||||
|
||||
@@ -312,7 +312,7 @@ describe("Kitty image cursor movement", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("restores the cursor to the reserved image row after Kitty rendering", () => {
|
||||
it("places image sequence on first line with empty padding rows", () => {
|
||||
setCapabilities({ images: "kitty", trueColor: true, hyperlinks: true });
|
||||
setCellDimensions({ widthPx: 10, heightPx: 10 });
|
||||
try {
|
||||
@@ -326,11 +326,11 @@ describe("Kitty image cursor movement", () => {
|
||||
const lines = image.render(4);
|
||||
const imageId = image.getImageId();
|
||||
assert.strictEqual(typeof imageId, "number");
|
||||
assert.deepStrictEqual(lines.slice(0, -1), [""]);
|
||||
assert.ok(lines[1].startsWith("\x1b[1A\x1b_G"));
|
||||
assert.ok(lines[1].includes(",C=1,"));
|
||||
assert.ok(lines[1].includes(`,i=${imageId}`));
|
||||
assert.ok(lines[1].endsWith("\x1b[1B"));
|
||||
assert.ok(lines[0].startsWith("\x1b_G"));
|
||||
assert.ok(lines[0].includes(",C=1,"));
|
||||
assert.ok(lines[0].includes(`,i=${imageId}`));
|
||||
assert.ok(lines[0].endsWith("\x1b\\"));
|
||||
assert.deepStrictEqual(lines.slice(1, lines.length), [""]);
|
||||
} finally {
|
||||
resetCapabilitiesCache();
|
||||
setCellDimensions({ widthPx: 9, heightPx: 18 });
|
||||
|
||||
Reference in New Issue
Block a user