@visualizevalue/img-grid 0.1.2 → 0.1.3

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.
package/README.md CHANGED
@@ -30,6 +30,8 @@ const highlightedBuffer = await grid(images, {
30
30
  highlight: ['img2', 'img3'], // Images with these IDs will be 2×2
31
31
  maxWidth: 1920, // Maximum width of output image
32
32
  concurrency: 10, // Maximum concurrent downloads
33
+ background: '#000', // Background and letterbox color
34
+ pixelated: true, // Nearest-neighbour resize — keeps pixel art crisp
33
35
  format: 'webp', // Output as png (default), jpeg, or webp
34
36
  quality: 80, // Quality for jpeg/webp
35
37
  onError: (img, err) => console.warn(`failed: ${img.url}`, err),
@@ -43,6 +45,7 @@ writeFileSync('grid.png', buffer)
43
45
 
44
46
  - Packs images into a compact, near-square grid
45
47
  - Supports highlighting specific images (makes them 2×2)
48
+ - Optional nearest-neighbour scaling to keep pixel art crisp (`pixelated`)
46
49
  - Downloads images concurrently (with a configurable limit)
47
50
  - Leaves a blank cell for failed downloads instead of failing the grid
48
51
  - Output as PNG, JPEG, or WebP
@@ -56,6 +59,7 @@ writeFileSync('grid.png', buffer)
56
59
  | `maxWidth` | `1920` | Maximum width of output image in pixels |
57
60
  | `concurrency` | `10` | Maximum concurrent image downloads |
58
61
  | `background` | `'#000'` | Background and letterbox color (any sharp color) |
62
+ | `pixelated` | `false` | Nearest-neighbour resize; keeps pixel art crisp |
59
63
  | `format` | `'png'` | Output format: `'png'`, `'jpeg'`, or `'webp'` |
60
64
  | `quality` | sharp's | Quality (1–100) for `jpeg`/`webp`; ignored for png |
61
65
  | `onError` | `undefined` | `(img, error) => void` called when an image fails |
package/dist/index.d.ts CHANGED
@@ -12,6 +12,12 @@ export interface GridOptions {
12
12
  concurrency?: number;
13
13
  /** Background and letterbox color (any sharp-compatible color). */
14
14
  background?: string;
15
+ /**
16
+ * Resize with nearest-neighbour instead of the default smooth (lanczos)
17
+ * kernel. Keeps pixel art (e.g. NFTs) crisp instead of blurring it when
18
+ * scaled up. Like CSS `image-rendering: pixelated`.
19
+ */
20
+ pixelated?: boolean;
15
21
  /** Output image format. */
16
22
  format?: ImageFormat;
17
23
  /** Quality (1–100) for lossy formats (`jpeg`, `webp`); ignored for `png`. */
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ const HIGHLIGHT_SPAN = 2;
19
19
  * `onError`) instead of failing the whole grid.
20
20
  */
21
21
  async function grid(images, opts = {}) {
22
- const { highlight = [], maxWidth = DEFAULT_MAX_WIDTH, concurrency = DEFAULT_CONCURRENCY, background = DEFAULT_BACKGROUND, format = DEFAULT_FORMAT, quality, onError, } = opts;
22
+ const { highlight = [], maxWidth = DEFAULT_MAX_WIDTH, concurrency = DEFAULT_CONCURRENCY, background = DEFAULT_BACKGROUND, pixelated = false, format = DEFAULT_FORMAT, quality, onError, } = opts;
23
23
  const highlightIds = new Set(highlight);
24
24
  const isHighlighted = (img) => img.id !== undefined && highlightIds.has(img.id);
25
25
  const highlighted = images.filter(isHighlighted);
@@ -31,7 +31,7 @@ async function grid(images, opts = {}) {
31
31
  const cellSize = Math.max(1, Math.floor(maxWidth / columns));
32
32
  const layers = await mapWithConcurrency(placements, concurrency, async (placement) => {
33
33
  const size = cellSize * placement.span;
34
- const cell = await renderCell(placement.img, size, background, onError);
34
+ const cell = await renderCell(placement.img, size, background, pixelated, onError);
35
35
  return {
36
36
  input: cell,
37
37
  left: placement.col * cellSize,
@@ -117,7 +117,7 @@ function pack(highlighted, normal, columns) {
117
117
  return { placements, rows: occupied.length };
118
118
  }
119
119
  /** Fetch and resize a single image; fall back to a blank cell on any failure. */
120
- async function renderCell(img, size, background, onError) {
120
+ async function renderCell(img, size, background, pixelated, onError) {
121
121
  try {
122
122
  const response = await fetch(img.url, {
123
123
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
@@ -127,7 +127,11 @@ async function renderCell(img, size, background, onError) {
127
127
  throw new Error(`HTTP ${response.status} for ${img.url}`);
128
128
  const data = Buffer.from(await response.arrayBuffer());
129
129
  return await (0, sharp_1.default)(data)
130
- .resize(size, size, { fit: 'contain', background })
130
+ .resize(size, size, {
131
+ fit: 'contain',
132
+ background,
133
+ ...(pixelated ? { kernel: 'nearest' } : {}),
134
+ })
131
135
  .png()
132
136
  .toBuffer();
133
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visualizevalue/img-grid",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Generate an image grid (PNG, JPEG, or WebP) from image URLs (and highlight images).",
5
5
  "license": "MIT",
6
6
  "author": "Visualize Value",