chenbhao's picture
feat: pass pixelWidth/pixelHeight from Image props to Kitty renderer
862182b
Raw
History Blame Contribute Delete
3.65 kB
import { Box, useIsScreenReaderEnabled, useStdout } from "src/ink";
import React, { useMemo } from "react";
import { useMeasuredSize } from "../../hooks/useMeasuredSize.js";
import usePosition from "../../hooks/usePosition.js";
import useProtocol from "../../hooks/useProtocol.js";
import type { GetVisibility, Visibility } from "../../hooks/useVisibility.js";
import { useVisibility } from "../../hooks/useVisibility.js";
import { useTerminalInfo } from "../../InkPictureProvider.js";
import AsciiImage from "./Ascii.js";
import BrailleImage from "./Braille.js";
import HalfBlockImage from "./HalfBlock.js";
import ITerm2Image from "./ITerm2.js";
import KittyImage from "./Kitty.js";
import type { ImageProps } from "./protocol.js";
import SixelImage from "./Sixel.js";
const imageProtocols = {
ascii: AsciiImage,
braille: BrailleImage,
halfBlock: HalfBlockImage,
iterm2: ITerm2Image,
kitty: KittyImage,
sixel: SixelImage,
};
export type ImageProtocolName = keyof typeof imageProtocols;
export type ImageProtocolHint = Partial<Record<Visibility, ImageProtocolName>>;
const ImageRenderer = (props: ImageProps & { protocol: ImageProtocolName }) => {
const ProtocolComponent = imageProtocols[props.protocol];
return <ProtocolComponent {...props} />;
};
type ImageComponentProps = Omit<ImageProps, "width" | "height"> & {
width?: number | string;
height?: number | string;
protocol?: ImageProtocolName | ImageProtocolHint;
getVisibility?: GetVisibility;
};
function Image({
protocol: specifiedProtocol,
getVisibility,
width = "100%",
height = "100%",
pixelWidth,
pixelHeight,
...props
}: ImageComponentProps) {
const isScreenReaderEnabled = useIsScreenReaderEnabled();
const terminalInfo = useTerminalInfo();
const { stdout } = useStdout();
const protocol = useProtocol(
typeof specifiedProtocol === "string" ? specifiedProtocol : undefined,
);
const { containerRef, resolvedWidth, resolvedHeight } = useMeasuredSize(
width,
height,
);
const position = usePosition(containerRef);
const visibility = useVisibility(
position,
stdout.rows,
stdout.columns,
getVisibility,
);
const effectiveProtocol = useMemo((): ImageProtocolName => {
if (typeof specifiedProtocol === "string") return specifiedProtocol;
const hints: ImageProtocolHint = specifiedProtocol ?? {};
const hint = hints[visibility];
if (hint) return hint;
if (visibility === "full") return protocol;
if (
protocol === "ascii" ||
protocol === "braille" ||
protocol === "halfBlock"
)
return protocol;
if (terminalInfo.supportsUnicode && terminalInfo.supportsColor)
return "halfBlock";
if (terminalInfo.supportsUnicode) return "braille";
return "ascii";
}, [
visibility,
protocol,
specifiedProtocol,
terminalInfo.supportsUnicode,
terminalInfo.supportsColor,
]);
if (isScreenReaderEnabled) {
const { src, alt } = props;
const label = `image: ${alt || (typeof src === "string" ? src : "binary image data")}`;
return (
<Box
ref={containerRef}
width={width}
height={height}
aria-label={label}
flexDirection="column"
/>
);
}
return (
<Box
ref={containerRef}
width={width}
height={height}
flexDirection="column"
>
<ImageRenderer
protocol={effectiveProtocol}
key={effectiveProtocol}
width={resolvedWidth}
height={resolvedHeight}
pixelWidth={pixelWidth}
pixelHeight={pixelHeight}
{...props}
/>
</Box>
);
}
export default Image;