All files / src/plugins/thumbnails data-url.ts

100% Statements 12/12
100% Branches 4/4
100% Functions 3/3
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49                    3x                       4x   4x 4x 4x 4x   9x   1x   4x 3x                       1x    
/**
 * Turning a PNG on disk into the data URL a Clay settings page can carry.
 *
 * A Clay builder runs in a sandboxed config webview that cannot reach local files, so every picture
 * has to travel inline as a data URL. Both generators in this plugin encode the same way through
 * here, so a panel thumbnail and a theme preview come out byte for byte alike.
 */
import fs from 'node:fs';
 
/** The eight bytes every PNG opens with. */
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
 
/**
 * Whether a file really is a PNG, by its signature rather than its name.
 *
 * A manifest can point at any path, so a file renamed to .png or a stray JPEG would otherwise land
 * in the page as a broken image. Reading the first eight bytes catches it before it ships.
 *
 * @param file The file to check.
 * @return True when the file opens with the PNG signature.
 */
export function isPng(file: string): boolean {
  let handle: number | null = null;
 
  try {
    handle = fs.openSync(file, 'r');
    const head = Buffer.alloc(PNG_SIGNATURE.length);
    const read = fs.readSync(handle, head, 0, head.length, 0);
 
    return read === PNG_SIGNATURE.length && PNG_SIGNATURE.every((byte, index) => head[index] === byte);
  } catch {
    return false;
  } finally {
    if (handle !== null) {
      fs.closeSync(handle);
    }
  }
}
 
/**
 * Reads a PNG and encodes it as a base64 data URL.
 *
 * @param file The PNG to encode.
 * @return The data URL, ready to drop into a Clay page.
 */
export function pngDataUrl(file: string): string {
  return 'data:image/png;base64,' + fs.readFileSync(file).toString('base64');
}