chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
alexion committed 2026-10-15 17:58:48 -04:00
1 parent 719370c8f7
commit 6081e3e325
1743 files changed
+205459 -131704

No files matched your search

+80 -13
View File
@@ -2,6 +2,7 @@ import { execSync } from "node:child_process";
import { homedir } from "node:os";
import { isAbsolute } from "node:path";
import { pathToFileURL } from "node:url";
import type { TerminalColorMode } from "./colors.ts";
export type ImageProtocol = "kitty" | "iterm2" | null;
@@ -71,7 +72,7 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink: () => boolean)
const terminalEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || "";
const term = process.env.TERM?.toLowerCase() || "";
const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit";
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit" || term.endsWith("-direct");
const isWindowsConsole = process.platform === "win32";
// Emit OSC 8 hyperlinks only when tmux confirms it forwards.
@@ -85,6 +86,12 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink: () => boolean)
return { images: null, trueColor: hasTrueColorHint, hyperlinks: false };
}
// Herdr forwards OSC 8 hyperlinks and supports the Kitty graphics protocol. Check it before
// outer-terminal detection because variables like KITTY_WINDOW_ID can leak into its panes.
if (termProgram === "herdr") {
return { images: "kitty", trueColor: hasTrueColorHint, hyperlinks: true };
}
if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
return { images: "kitty", trueColor: true, hyperlinks: true };
}
@@ -168,6 +175,10 @@ export function getCapabilities(): TerminalCapabilities {
return cachedCapabilities;
}
export function getTerminalColorMode(capabilities: TerminalCapabilities = getCapabilities()): TerminalColorMode {
return capabilities.trueColor ? "truecolor" : "256color";
}
export function resetCapabilitiesCache(): void {
cachedCapabilities = null;
}
@@ -327,6 +338,7 @@ export interface KittyImagePlacement {
transmissionGeneration: number;
transmissionBytes: number;
estimatedDecodedBytes: number;
rows: number;
sequence: string;
replacementLine: string;
}
@@ -344,13 +356,27 @@ export function registerKittyImageMetadata(metadata: KittyImageMetadata): void {
}
}
function getRegisteredKittyImageMetadata(line: string): RegisteredKittyImageMetadata | undefined {
const controls = /\x1b_G([^;]*);/.exec(line)?.[1];
if (!controls) return undefined;
function getRegisteredKittyImageMetadataFromControls(controls: string): RegisteredKittyImageMetadata | undefined {
const imageId = /(?:^|,)i=(\d+)(?:,|$)/.exec(controls)?.[1];
return imageId === undefined ? undefined : kittyImageMetadata.get(Number.parseInt(imageId, 10));
}
function getRegisteredKittyImageMetadata(line: string): RegisteredKittyImageMetadata | undefined {
const controls = /\x1b_G([^;]*);/.exec(line)?.[1];
return controls === undefined ? undefined : getRegisteredKittyImageMetadataFromControls(controls);
}
function getExplicitKittyImageRows(controls: string): number | undefined {
const value = /(?:^|,)r=(\d+)(?:,|$)/.exec(controls)?.[1];
if (value === undefined) return undefined;
const rows = Number.parseInt(value, 10);
return rows > 0 ? rows : undefined;
}
function getKittyImageRowsFromControls(controls: string, fallbackRows: number): number {
return getExplicitKittyImageRows(controls) ?? fallbackRows;
}
export function getKittyImageMetadata(line: string): KittyImageMetadata | undefined {
const metadata = getRegisteredKittyImageMetadata(line);
if (!metadata) return undefined;
@@ -383,11 +409,21 @@ const KITTY_PLACEMENT_CONTROL_KEYS = new Set([
"V",
]);
/** Read the number of rows covered by an image placement without scanning its payload. */
export function getKittyImagePlacementRows(line: string): number | undefined {
const controls = /\x1b_G([^;]*);/.exec(line)?.[1];
if (controls === undefined) return undefined;
const explicitRows = getExplicitKittyImageRows(controls);
if (explicitRows !== undefined) return explicitRows;
return getRegisteredKittyImageMetadataFromControls(controls)?.rows;
}
/** Build a placement-only command for an image line emitted by {@link renderImage}. */
export function getKittyImagePlacement(line: string): KittyImagePlacement | undefined {
const match = /\x1b_G([^;]*);/.exec(line);
const metadata = getRegisteredKittyImageMetadata(line);
if (!match || !metadata) return undefined;
if (!match) return undefined;
const metadata = getRegisteredKittyImageMetadataFromControls(match[1]);
if (!metadata) return undefined;
let commandStart = match.index;
let commandControls = match[1];
@@ -413,6 +449,7 @@ export function getKittyImagePlacement(line: string): KittyImagePlacement | unde
transmissionGeneration: metadata.transmissionGeneration,
transmissionBytes: transmissionEnd - match.index,
estimatedDecodedBytes: metadata.widthPx * metadata.heightPx * 4,
rows: getKittyImageRowsFromControls(match[1], metadata.rows),
sequence,
replacementLine: `${line.slice(0, match.index)}${sequence}${line.slice(transmissionEnd)}`,
};
@@ -432,11 +469,21 @@ export function cropKittyImageLine(line: string, hiddenRows: number, visibleRows
return `${line.slice(0, match.index)}\x1b_G${controls.join(",")};${line.slice(match.index + match[0].length)}`;
}
function chooseLessDistortedCellCount(upperCount: number, idealCount: number): number {
if (upperCount <= 1) return upperCount;
const lowerCount = upperCount - 1;
const upperDistortion = Math.max(upperCount / idealCount, idealCount / upperCount);
const lowerDistortion = Math.max(lowerCount / idealCount, idealCount / lowerCount);
return lowerDistortion < upperDistortion ? lowerCount : upperCount;
}
export function calculateImageCellSize(
imageDimensions: ImageDimensions,
maxWidthCells: number,
maxHeightCells?: number,
cellDimensions: CellDimensions = { widthPx: 9, heightPx: 18 },
optimizeAspectRatio = false,
): ImageCellSize {
const maxWidth = Math.max(1, Math.floor(maxWidthCells));
const maxHeight = maxHeightCells === undefined ? undefined : Math.max(1, Math.floor(maxHeightCells));
@@ -449,13 +496,26 @@ export function calculateImageCellSize(
const scaledWidthPx = imageWidth * scale;
const scaledHeightPx = imageHeight * scale;
const columns = Math.ceil(scaledWidthPx / cellDimensions.widthPx);
const rows = Math.ceil(scaledHeightPx / cellDimensions.heightPx);
let columns = Math.max(1, Math.min(maxWidth, Math.ceil(scaledWidthPx / cellDimensions.widthPx)));
const heightRows = scaledHeightPx / cellDimensions.heightPx;
let rows = Math.max(1, Math.ceil(heightRows));
if (maxHeight !== undefined) {
rows = Math.min(maxHeight, rows);
}
return {
columns: Math.max(1, Math.min(maxWidth, columns)),
rows: Math.max(1, maxHeight === undefined ? rows : Math.min(maxHeight, rows)),
};
if (!optimizeAspectRatio) {
return { columns, rows };
}
if (widthScale <= heightScale) {
const idealRows = (columns * cellDimensions.widthPx * imageHeight) / (imageWidth * cellDimensions.heightPx);
rows = chooseLessDistortedCellCount(rows, idealRows);
} else {
const idealColumns = (rows * cellDimensions.heightPx * imageWidth) / (imageHeight * cellDimensions.widthPx);
columns = chooseLessDistortedCellCount(columns, idealColumns);
}
return { columns, rows };
}
export function calculateImageRows(
@@ -619,7 +679,14 @@ export function renderImage(
}
const maxWidth = options.maxWidthCells ?? 80;
const size = calculateImageCellSize(imageDimensions, maxWidth, options.maxHeightCells, getCellDimensions());
// Reduce Kitty's cell-aligned distortion without shrinking iTerm2 reservations.
const size = calculateImageCellSize(
imageDimensions,
maxWidth,
options.maxHeightCells,
getCellDimensions(),
caps.images === "kitty",
);
if (caps.images === "kitty") {
if (options.imageId !== undefined) {