mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(tui): prevent jump-to-end label from shifting when scrollbar hides (#9842)
- center the label independently of scrollbar visibility - clip drawing and click bounds before the scrollbar - fixes #9136
This commit is contained in:
parent
aef5fc429b
commit
8c72793785
2 changed files with 52 additions and 4 deletions
|
|
@ -1630,11 +1630,14 @@ export class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|||
const row = clip.y + clip.height - 1;
|
||||
if (row >= screen.length || isImageLine(screen[row] ?? "")) return screen;
|
||||
const scrollbarColumn = box ? getScrollbarGeometry(box)?.column : undefined;
|
||||
const availableWidth = Math.max(0, (scrollbarColumn ?? clip.x + clip.width) - clip.x);
|
||||
const text = truncateToWidth(this.scrollToEndIndicator(), availableWidth, "");
|
||||
const label = truncateToWidth(this.scrollToEndIndicator(), clip.width, "");
|
||||
const labelWidth = visibleWidth(label);
|
||||
const column = clip.x + Math.floor((clip.width - labelWidth) / 2);
|
||||
const rightEdge = scrollbarColumn ?? clip.x + clip.width;
|
||||
const availableWidth = Math.max(0, rightEdge - column);
|
||||
const text = truncateToWidth(label, availableWidth, "");
|
||||
const textWidth = visibleWidth(text);
|
||||
if (textWidth === 0) return screen;
|
||||
const column = clip.x + Math.floor((availableWidth - textWidth) / 2);
|
||||
const result = [...screen];
|
||||
result[row] = compositeTuiLine(result[row] ?? "", text, column, textWidth, width);
|
||||
this.scrollToEndIndicatorRect = { row, column, width: textWidth };
|
||||
|
|
|
|||
|
|
@ -138,7 +138,51 @@ describe("TuiAltScreen", () => {
|
|||
tui.stop();
|
||||
});
|
||||
|
||||
it("leaves the scrollbar clickable when the jump-to-end indicator spans the transcript", async () => {
|
||||
it("keeps the jump-to-end indicator centered as the auto scrollbar hides and reappears", async () => {
|
||||
// Regression test for #9136: auto scrollbar visibility must not move the indicator.
|
||||
const terminal = new VirtualTerminal(80, 6);
|
||||
const label = " ↓ Jump to latest message · End ";
|
||||
const tui = new TuiAltScreen(terminal, undefined, undefined, {
|
||||
scrollToEndIndicator: () => label,
|
||||
});
|
||||
const transcript = new ScrollView(
|
||||
new Text(Array.from({ length: 20 }, (_, index) => `line ${index + 1}`).join("\n"), 0, 0),
|
||||
{ follow: "end", primary: true, scrollbar: "auto", scrollbarHideDelayMs: 0 },
|
||||
);
|
||||
tui.setLayoutRoot(transcript);
|
||||
tui.start();
|
||||
try {
|
||||
await terminal.waitForRender();
|
||||
|
||||
// Scrolling over the track keeps the scrollbar visible until the pointer leaves.
|
||||
terminal.sendInput("\x1b[<64;80;1M");
|
||||
await terminal.waitForRender();
|
||||
assert.strictEqual(transcript.isScrollbarVisible, true);
|
||||
assert.strictEqual(transcript.isFollowingEnd, false);
|
||||
const scrollTop = transcript.scrollTop;
|
||||
const visibleColumn = terminal.getViewport()[5].indexOf(label);
|
||||
|
||||
// Leaving the track lets the auto-hide timer expire without changing the content.
|
||||
terminal.sendInput("\x1b[<35;79;1M");
|
||||
await terminal.waitForRender();
|
||||
assert.strictEqual(transcript.isScrollbarVisible, false);
|
||||
assert.strictEqual(transcript.scrollTop, scrollTop);
|
||||
const hiddenColumn = terminal.getViewport()[5].indexOf(label);
|
||||
|
||||
terminal.sendInput("\x1b[<35;80;1M");
|
||||
await terminal.waitForRender();
|
||||
assert.strictEqual(transcript.isScrollbarVisible, true);
|
||||
assert.strictEqual(transcript.scrollTop, scrollTop);
|
||||
const revealedColumn = terminal.getViewport()[5].indexOf(label);
|
||||
|
||||
assert.deepStrictEqual([visibleColumn, hiddenColumn, revealedColumn], [24, 24, 24]);
|
||||
} finally {
|
||||
tui.stop();
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves the scrollbar visible and clickable when the jump-to-end indicator spans the transcript", async () => {
|
||||
// Regression coverage for #9136: centering must not paint or capture clicks over the scrollbar.
|
||||
const terminal = new VirtualTerminal(30, 6);
|
||||
const tui = new TuiAltScreen(terminal, undefined, undefined, {
|
||||
scrollToEndIndicator: () => "↓".repeat(30),
|
||||
|
|
@ -161,6 +205,7 @@ describe("TuiAltScreen", () => {
|
|||
assert.strictEqual(transcript.isFollowingEnd, false);
|
||||
|
||||
// The indicator must not intercept a press on the scrollbar's last column.
|
||||
assert.strictEqual(terminal.getViewport()[3], `${"↓".repeat(29)}┃`);
|
||||
terminal.sendInput("\x1b[<0;30;4M");
|
||||
terminal.sendInput("\x1b[<0;30;4m");
|
||||
await terminal.waitForRender();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue