/** * @file * * Adds a caption band to an already-captured screenshot. * * A store listing shows screenshots ONE AT A TIME, in a carousel, with no * caption of its own — so an image has to say what it is showing. Without that, * a shot of the state a plugin removes reads as a shot of the state the plugin * causes, which is the opposite of the message. * * The band is drawn over the BOTTOM of the frame, for two reasons: the top is * where the content being demonstrated usually starts, and the bottom of an * Obsidian frame is chrome — status bar, word count, sync indicator — which the * band then covers rather than competing with. * * This is post-processing, deliberately. The capture stays an untouched device * frame, and rewording a label needs no re-shoot. * * A caption that is too long for its frame is MEASURED and rejected rather than * drawn. An SVG `` is clipped by its viewport at both ends with no * ellipsis and no error, so an overlong caption ships as a sentence fragment * that looks deliberate — `nabled in Settings - a listening plugin is told` — * and the frame is still exactly the size it should be, so every dimension * assertion downstream still passes. The only feedback anyone ever got was * looking at the PNG. See {@link measureLabelCaption}. */ /** * Parameters for {@link computeLabelBand}. */ export interface ComputeLabelBandParams { /** * Height of the image being labeled, in pixels. */ readonly imageHeightInPixels: number; /** * Width of the image being labeled, in pixels. */ readonly imageWidthInPixels: number; } /** * The geometry of the caption band. */ export interface LabelBandGeometry { /** * How much horizontal room the caption has, in pixels: the image width less * the margin held clear at each end, so a caption that fits does not read as * though it were about to touch the edge of the frame. */ readonly captionRoomInPixels: number; /** * Font size for the caption, in pixels. */ readonly fontSizeInPixels: number; /** * Height of the band, in pixels. */ readonly heightInPixels: number; /** * Distance from the top of the image to the top of the band, in pixels. */ readonly topInPixels: number; } /** * What a caption measures against the frame it is destined for. */ export interface LabelCaptionMeasurement { /** * The room available, in pixels — {@link LabelBandGeometry.captionRoomInPixels}. */ readonly captionRoomInPixels: number; /** * Whether the caption fits that room. */ readonly doesFit: boolean; /** * How wide the caption actually renders, in pixels, at the size the band * would draw it. */ readonly textWidthInPixels: number; } /** * Options for {@link labelScreenshot}. */ export interface LabelScreenshotOptions { /** * The caption. Keep it to a handful of words: it is read at listing-thumbnail * size, and it is neither wrapped nor shrunk to fit — one that does not fit * is REJECTED, with the measured width and the room available. Check a * candidate with {@link measureLabelCaption} before committing to it. */ readonly text: string; } /** * Parameters for {@link measureLabelCaption}. */ export interface MeasureLabelCaptionParams { /** * Height of the image the caption is destined for, in pixels. */ readonly imageHeightInPixels: number; /** * Width of the image the caption is destined for, in pixels. */ readonly imageWidthInPixels: number; /** * The caption to measure. */ readonly text: string; } /** * Builds the SVG for the caption band. * * @param text - The caption. * @param geometry - The band geometry. * @param imageWidthInPixels - Width of the image, so the band spans it. * @returns The SVG markup. */ export declare function buildLabelSvg(text: string, geometry: LabelBandGeometry, imageWidthInPixels: number): string; /** * Computes the caption band's size and position for a given image. * * @param params - The image dimensions. * @returns The band geometry. * @throws Error if either dimension is not a positive number. */ export declare function computeLabelBand(params: ComputeLabelBandParams): LabelBandGeometry; /** * Escapes text for inclusion in SVG character data. * * A caption is authored per screenshot and can legitimately contain `&` or the * angle brackets Obsidian uses in link syntax; unescaped, those make the SVG * impossible to parse and `sharp` fails on a caption rather than on an image. * * @param text - The raw caption. * @returns The caption, safe to embed in SVG. */ export declare function escapeSvgText(text: string): string; /** * Draws a caption band across the bottom of a screenshot. * * The image keeps its dimensions exactly: the band is composited OVER the * frame, never appended to it, because the store expects a specific size. * * @param bytes - The captured PNG. * @param options - The caption. * @returns A {@link Promise} that resolves to the labeled PNG, the same size as the input. * @throws Error if `sharp` is not installed, the image dimensions cannot be read, or the caption is too wide for the frame. */ export declare function labelScreenshot(bytes: Uint8Array, options: LabelScreenshotOptions): Promise; /** * Measures a caption against the frame it is destined for, without drawing * anything. * * This is what {@link labelScreenshot} rejects an overlong caption with, exposed * so a caption can be chosen with the number in hand instead of by capturing a * frame and looking at it. * * @param params - The caption and the dimensions of the image it is for. * @returns A {@link Promise} that resolves to the measurement. * @throws Error if `sharp` is not installed, or either dimension is not a positive number. */ export declare function measureLabelCaption(params: MeasureLabelCaptionParams): Promise;