mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(tui): clip Kitty images to layout boundaries
This commit is contained in:
parent
b3ed27b3fe
commit
af187eee4b
5 changed files with 33 additions and 4 deletions
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
- Fixed Kitty image previews in fullscreen mode overlapping the sticky editor and footer dock while scrolling.
|
||||
- Fixed spaces in `/settings` searches toggling the highlighted setting while typing multi-word queries such as **UI mode** or **Quiet startup**.
|
||||
- Fixed custom editors not inheriting the default editor's autocomplete dropdown item limit ([#7333](https://github.com/earendil-works/pi/issues/7333)).
|
||||
- Fixed malformed resource arrays in package manifests crashing session startup ([#7187](https://github.com/earendil-works/pi/issues/7187)).
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
- Fixed terminal width accounting for Indic conjunct grapheme clusters ([#6124](https://github.com/earendil-works/pi/issues/6124) by [@petrroll](https://github.com/petrroll)).
|
||||
- Fixed phantom alternate-screen text selection from unmatched mouse events when changing terminal pane focus.
|
||||
- Fixed spaces in searchable settings queries changing the selected value instead of filtering multi-word labels.
|
||||
- Fixed alternate-screen Kitty images crossing vertical layout clip boundaries and overlapping sticky regions while scrolling.
|
||||
|
||||
### Added
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,13 @@ function paintBox(box: LayoutBox, screen: string[], totalWidth: number): void {
|
|||
if (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length) continue;
|
||||
const sourceLine = box.lines[offset + localRow];
|
||||
if (sourceLine === undefined) continue;
|
||||
const line = sourceLine.replace(OSC133_ZONE_PREFIX, "");
|
||||
let line = sourceLine.replace(OSC133_ZONE_PREFIX, "");
|
||||
const imageMetadata = getKittyImageMetadata(line);
|
||||
if (imageMetadata) {
|
||||
const clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);
|
||||
const visibleRows = Math.min(imageMetadata.rows, clipBottom - row);
|
||||
if (visibleRows < imageMetadata.rows) line = cropKittyImageLine(line, 0, visibleRows);
|
||||
}
|
||||
if (isImageLine(line) && box.rect.x === 0 && box.rect.width >= totalWidth) screen[row] = line;
|
||||
else screen[row] = compositeTuiLine(screen[row] ?? "", line, box.rect.x, box.rect.width, totalWidth);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -284,12 +284,14 @@ export function getKittyImageMetadata(line: string): KittyImageMetadata | undefi
|
|||
export function cropKittyImageLine(line: string, hiddenRows: number, visibleRows: number): string {
|
||||
const metadata = getKittyImageMetadata(line);
|
||||
const match = /\x1b_G([^;]*);/.exec(line);
|
||||
if (!metadata || !match || hiddenRows <= 0 || visibleRows <= 0) return line;
|
||||
if (!metadata || !match || hiddenRows < 0 || hiddenRows >= metadata.rows || visibleRows <= 0) return line;
|
||||
const croppedRows = Math.min(visibleRows, metadata.rows - hiddenRows);
|
||||
if (hiddenRows === 0 && croppedRows === metadata.rows) return line;
|
||||
const sourceY = Math.floor((metadata.heightPx * hiddenRows) / metadata.rows);
|
||||
const sourceEnd = Math.ceil((metadata.heightPx * (hiddenRows + visibleRows)) / metadata.rows);
|
||||
const sourceEnd = Math.ceil((metadata.heightPx * (hiddenRows + croppedRows)) / metadata.rows);
|
||||
const sourceHeight = Math.max(1, Math.min(metadata.heightPx, sourceEnd) - sourceY);
|
||||
const controls = match[1].split(",").filter((control) => !/^[yhr]=/.test(control));
|
||||
controls.push(`y=${sourceY}`, `h=${sourceHeight}`, `r=${visibleRows}`);
|
||||
controls.push(`y=${sourceY}`, `h=${sourceHeight}`, `r=${croppedRows}`);
|
||||
return `${line.slice(0, match.index)}\x1b_G${controls.join(",")};${line.slice(match.index + match[0].length)}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { ScrollView } from "../src/components/scroll-view.ts";
|
|||
import { Text } from "../src/components/text.ts";
|
||||
import { VStack } from "../src/components/v-stack.ts";
|
||||
import { renderLayoutFrame } from "../src/layout.ts";
|
||||
import { encodeKitty, registerKittyImageMetadata } from "../src/terminal-image.ts";
|
||||
import { stripTerminalSequences } from "../src/utils.ts";
|
||||
|
||||
function visibleLines(lines: string[]): string[] {
|
||||
|
|
@ -59,6 +60,24 @@ describe("viewport layout", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("crops Kitty images at a scroll view's lower boundary", () => {
|
||||
const imageId = 124;
|
||||
const imageLine = encodeKitty("AAAA", { columns: 2, rows: 3, imageId, moveCursor: false });
|
||||
registerKittyImageMetadata({ imageId, columns: 2, rows: 3, widthPx: 100, heightPx: 100 });
|
||||
const transcript = new ScrollView({
|
||||
render: () => ["one", "two", imageLine, "", ""],
|
||||
invalidate: () => {},
|
||||
});
|
||||
const frame = renderLayoutFrame(
|
||||
new VStack([{ component: transcript, basis: 0, grow: 1 }, new Text("dock", 0, 0)]),
|
||||
20,
|
||||
4,
|
||||
() => {},
|
||||
);
|
||||
|
||||
assert.ok(frame.lines[2]?.includes("y=0,h=34,r=1"));
|
||||
});
|
||||
|
||||
it("composes horizontal children at allocated widths", () => {
|
||||
const frame = renderLayoutFrame(
|
||||
new HStack([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue