@earendil-works/pi-tui 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import { getKeybindings } from "./keybindings.js";
5
5
  import { isKeyRelease } from "./keys.js";
6
6
  import { getLayoutBoxesAt, getScrollbarGeometry, getScrollViewBox, getScrollViewsAt, renderLayoutFrame, } from "./layout.js";
7
7
  import { getLayoutNode } from "./layout-node.js";
8
- import { deleteAllKittyImages, deleteAllKittyPlacements, deleteKittyImage, getCapabilities, getKittyImagePlacement, isImageLine, setCapabilities, } from "./terminal-image.js";
8
+ import { deleteAllKittyImages, deleteAllKittyPlacements, deleteKittyImage, getCapabilities, getKittyImagePlacement, getKittyImagePlacementRows, isImageLine, setCapabilities, } from "./terminal-image.js";
9
9
  import { Container, CURSOR_MARKER, compositeTuiLine, dispatchMouseEvent, retargetMouseEvent, TuiBase, VIEWPORT_TUI, } from "./tui.js";
10
10
  import { extractAnsiCode, getGraphemeCellRange, getOsc8LinkAtColumn, getWordSegmenter, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, } from "./utils.js";
11
11
  import { WheelScrollAccelerator } from "./wheel-scroll.js";
@@ -1458,7 +1458,24 @@ export class TuiAltScreen extends TuiBase {
1458
1458
  return sliceByColumn(line, 0, width, true);
1459
1459
  });
1460
1460
  const fullRedraw = this.previousScreen.length === 0 || this.previousScreenWidth !== width || this.previousScreenHeight !== height;
1461
- const imagesNeedRedraw = screen.some((line, row) => line !== this.previousScreen[row] && (isImageLine(line) || isImageLine(this.previousScreen[row] ?? "")));
1461
+ const changedRows = screen.map((line, row) => line !== this.previousScreen[row]);
1462
+ const imageAnchorsNeedRedraw = screen.some((line, row) => changedRows[row] && (isImageLine(line) || isImageLine(this.previousScreen[row] ?? "")));
1463
+ const isWezTerm = Boolean(process.env.WEZTERM_PANE) || process.env.TERM_PROGRAM?.toLowerCase() === "wezterm";
1464
+ const imageCellsNeedRedraw = !imageAnchorsNeedRedraw &&
1465
+ isWezTerm &&
1466
+ this.imageProtocol === "kitty" &&
1467
+ changedRows.some(Boolean) &&
1468
+ screen.some((line, row) => {
1469
+ const placementRows = getKittyImagePlacementRows(line);
1470
+ if (placementRows === undefined)
1471
+ return false;
1472
+ for (let coveredRow = row; coveredRow < row + placementRows; coveredRow++) {
1473
+ if (changedRows[coveredRow])
1474
+ return true;
1475
+ }
1476
+ return false;
1477
+ });
1478
+ const imagesNeedRedraw = imageAnchorsNeedRedraw || imageCellsNeedRedraw;
1462
1479
  const redrawImages = fullRedraw || imagesNeedRedraw;
1463
1480
  const hadUploadedKittyImages = this.uploadedKittyImages.size > 0;
1464
1481
  const preparedKittyScreen = redrawImages && this.imageProtocol === "kitty"
@@ -1479,24 +1496,37 @@ export class TuiAltScreen extends TuiBase {
1479
1496
  buffer += deleteAllKittyPlacements();
1480
1497
  }
1481
1498
  buffer += preparedKittyScreen.evictedImageDeletion;
1482
- // WezTerm erases intersecting Kitty image cells when a later EL clears a covered row.
1483
- // Only separate clearing from drawing for WezTerm frames that place images; preserve the
1484
- // existing interleaved output for text-only frames and every other terminal.
1485
- const clearRowsBeforeKittyImages = redrawImages &&
1486
- this.imageProtocol === "kitty" &&
1487
- screen.some(isImageLine) &&
1488
- (Boolean(process.env.WEZTERM_PANE) || process.env.TERM_PROGRAM?.toLowerCase() === "wezterm");
1489
- if (clearRowsBeforeKittyImages) {
1499
+ // WezTerm erases intersecting Kitty image cells when a later row write touches a covered row.
1500
+ // Draw image placements after every clear and text write so nothing later intersects them; preserve
1501
+ // the existing interleaved output for text-only frames and every other terminal.
1502
+ const drawKittyImagesLast = redrawImages && this.imageProtocol === "kitty" && screen.some(isImageLine) && isWezTerm;
1503
+ if (drawKittyImagesLast) {
1490
1504
  for (let row = 0; row < height; row++) {
1491
1505
  if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1492
1506
  continue;
1493
1507
  buffer += `\x1b[${row + 1};1H\x1b[2K`;
1494
1508
  }
1509
+ for (let row = 0; row < height; row++) {
1510
+ if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1511
+ continue;
1512
+ if (isImageLine(preparedKittyScreen.lines[row] ?? ""))
1513
+ continue;
1514
+ buffer += `\x1b[${row + 1};1H${preparedKittyScreen.lines[row] ?? ""}`;
1515
+ }
1516
+ for (let row = 0; row < height; row++) {
1517
+ if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1518
+ continue;
1519
+ if (!isImageLine(preparedKittyScreen.lines[row] ?? ""))
1520
+ continue;
1521
+ buffer += `\x1b[${row + 1};1H${preparedKittyScreen.lines[row] ?? ""}`;
1522
+ }
1495
1523
  }
1496
- for (let row = 0; row < height; row++) {
1497
- if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1498
- continue;
1499
- buffer += `\x1b[${row + 1};1H${clearRowsBeforeKittyImages ? "" : "\x1b[2K"}${preparedKittyScreen.lines[row] ?? ""}`;
1524
+ else {
1525
+ for (let row = 0; row < height; row++) {
1526
+ if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1527
+ continue;
1528
+ buffer += `\x1b[${row + 1};1H\x1b[2K${preparedKittyScreen.lines[row] ?? ""}`;
1529
+ }
1500
1530
  }
1501
1531
  if (cursorPos) {
1502
1532
  buffer += `\x1b[${cursorPos.row + 1};${Math.min(width, cursorPos.col) + 1}H`;