chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const root = process.cwd();
|
||||
const logoSize = 62;
|
||||
const badgeHeight = 126;
|
||||
const gap = 20;
|
||||
const out = path.join(root, "assets/sponsors/emil-sponsor-row.webp");
|
||||
|
||||
const logo = await sharp(path.join(root, "assets/sponsors/animations-dev.webp"))
|
||||
.resize(logoSize, logoSize)
|
||||
.toBuffer();
|
||||
|
||||
const badge = await sharp(path.join(root, "assets/sponsors/emil-animations-dev.webp"))
|
||||
.resize({ height: badgeHeight })
|
||||
.toBuffer();
|
||||
|
||||
const badgeMeta = await sharp(badge).metadata();
|
||||
const width = logoSize + gap + badgeMeta.width;
|
||||
const height = badgeHeight;
|
||||
|
||||
await sharp({
|
||||
create: {
|
||||
width,
|
||||
height,
|
||||
channels: 4,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
},
|
||||
})
|
||||
.composite([
|
||||
{ input: logo, left: 0, top: Math.floor((height - logoSize) / 2) },
|
||||
{ input: badge, left: logoSize + gap, top: 0 },
|
||||
])
|
||||
.webp({ quality: 94, effort: 6, alphaQuality: 100 })
|
||||
.toFile(out);
|
||||
|
||||
const meta = await sharp(out).metadata();
|
||||
console.log(
|
||||
`${path.basename(out)} -> ${meta.width}x${meta.height}, ${fs.statSync(out).size} bytes`
|
||||
);
|
||||
@@ -0,0 +1,142 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const root = process.cwd();
|
||||
|
||||
const pngToWebp = [
|
||||
"assets/readme-banner.png",
|
||||
"assets/readme-buttons/btn-site.png",
|
||||
"assets/readme-buttons/btn-mit.png",
|
||||
"assets/readme-buttons/btn-agent-skills.png",
|
||||
"assets/readme-buttons/btn-tools.png",
|
||||
"assets/readme-buttons/btn-changelog.png",
|
||||
];
|
||||
|
||||
function isBackground(r, g, b, a, threshold = 22) {
|
||||
if (a < 8) return true;
|
||||
return r <= threshold && g <= threshold && b <= threshold;
|
||||
}
|
||||
|
||||
function removeOuterBackground(rgba, width, height) {
|
||||
const visited = new Uint8Array(width * height);
|
||||
const queue = [];
|
||||
|
||||
for (let x = 0; x < width; x++) {
|
||||
queue.push(x, 0, x, height - 1);
|
||||
}
|
||||
for (let y = 1; y < height - 1; y++) {
|
||||
queue.push(0, y, width - 1, y);
|
||||
}
|
||||
|
||||
while (queue.length) {
|
||||
const y = queue.pop();
|
||||
const x = queue.pop();
|
||||
const idx = y * width + x;
|
||||
if (x < 0 || y < 0 || x >= width || y >= height || visited[idx]) continue;
|
||||
|
||||
const i = idx * 4;
|
||||
if (!isBackground(rgba[i], rgba[i + 1], rgba[i + 2], rgba[i + 3])) continue;
|
||||
|
||||
visited[idx] = 1;
|
||||
rgba[i + 3] = 0;
|
||||
queue.push(x + 1, y, x - 1, y, x, y + 1, x, y - 1);
|
||||
}
|
||||
|
||||
return rgba;
|
||||
}
|
||||
|
||||
function getBounds(rgba, width, height) {
|
||||
let minX = width;
|
||||
let minY = height;
|
||||
let maxX = 0;
|
||||
let maxY = 0;
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const a = rgba[(y * width + x) * 4 + 3];
|
||||
if (a > 8) {
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pad = 2;
|
||||
return {
|
||||
left: Math.max(0, minX - pad),
|
||||
top: Math.max(0, minY - pad),
|
||||
width: Math.min(width, maxX - minX + 1 + pad * 2),
|
||||
height: Math.min(height, maxY - minY + 1 + pad * 2),
|
||||
};
|
||||
}
|
||||
|
||||
async function pngFileToWebp(inputRel, { maxWidth } = {}) {
|
||||
const input = path.join(root, inputRel);
|
||||
const output = input.replace(/\.png$/i, ".webp");
|
||||
let pipeline = sharp(input);
|
||||
const meta = await pipeline.metadata();
|
||||
|
||||
if (maxWidth && meta.width > maxWidth) {
|
||||
pipeline = pipeline.resize({ width: maxWidth, withoutEnlargement: true });
|
||||
}
|
||||
|
||||
await pipeline.webp({ quality: 92, effort: 6, alphaQuality: 100 }).toFile(output);
|
||||
const outMeta = await sharp(output).metadata();
|
||||
console.log(
|
||||
`${path.basename(output)} -> ${outMeta.width}x${outMeta.height}, ${fs.statSync(output).size} bytes`
|
||||
);
|
||||
}
|
||||
|
||||
async function emilBadgeToWebp() {
|
||||
const src =
|
||||
"C:/Users/User/Downloads/c4f8c4a7-2566-4644-b752-b652e0c103f5.png";
|
||||
const output = path.join(root, "assets/sponsors/emil-animations-dev.webp");
|
||||
const exportHeight = 240;
|
||||
|
||||
const { data, info } = await sharp(src).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
||||
const rgba = Buffer.from(data);
|
||||
removeOuterBackground(rgba, info.width, info.height);
|
||||
const bounds = getBounds(rgba, info.width, info.height);
|
||||
|
||||
await sharp(rgba, { raw: { width: info.width, height: info.height, channels: 4 } })
|
||||
.extract(bounds)
|
||||
.resize({ height: exportHeight, withoutEnlargement: true })
|
||||
.webp({ quality: 94, effort: 6, alphaQuality: 100 })
|
||||
.toFile(output);
|
||||
|
||||
const outMeta = await sharp(output).metadata();
|
||||
console.log(
|
||||
`${path.basename(output)} -> ${outMeta.width}x${outMeta.height}, ${fs.statSync(output).size} bytes`
|
||||
);
|
||||
}
|
||||
|
||||
async function sponsorLogoToWebp() {
|
||||
const jfif = "C:/Users/User/Downloads/6b610a0c-8889-49fc-9684-e172d7172ea0.jfif";
|
||||
const output = path.join(root, "assets/sponsors/animations-dev.webp");
|
||||
const source = fs.existsSync(jfif)
|
||||
? jfif
|
||||
: path.join(root, "assets/sponsors/animations-dev.png");
|
||||
|
||||
await sharp(source)
|
||||
.resize(192, 192, { fit: "cover" })
|
||||
.webp({ quality: 92, effort: 6, alphaQuality: 100 })
|
||||
.toFile(`${output}.tmp`);
|
||||
fs.renameSync(`${output}.tmp`, output);
|
||||
|
||||
const outMeta = await sharp(output).metadata();
|
||||
console.log(
|
||||
`${path.basename(output)} -> ${outMeta.width}x${outMeta.height}, ${fs.statSync(output).size} bytes`
|
||||
);
|
||||
}
|
||||
|
||||
for (const file of pngToWebp) {
|
||||
await pngFileToWebp(file, {
|
||||
maxWidth: file.includes("readme-buttons") ? 1400 : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
await sponsorLogoToWebp();
|
||||
await emilBadgeToWebp();
|
||||
@@ -0,0 +1,108 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const srcDir = "C:/Users/User/Downloads";
|
||||
const outDir = path.join(process.cwd(), "assets/readme-buttons");
|
||||
|
||||
const mapping = [
|
||||
{ src: "ChatGPT Image Jun 17, 2026, 04_04_16 PM (1).png", out: "btn-site.png" },
|
||||
{ src: "ChatGPT Image Jun 17, 2026, 04_04_16 PM (2).png", out: "btn-mit.png" },
|
||||
{ src: "ChatGPT Image Jun 17, 2026, 04_04_17 PM (3).png", out: "btn-agent-skills.png" },
|
||||
{ src: "ChatGPT Image Jun 17, 2026, 04_04_17 PM (4).png", out: "btn-tools.png" },
|
||||
{ src: "ChatGPT Image Jun 17, 2026, 04_04_20 PM (5).png", out: "btn-changelog.png" },
|
||||
];
|
||||
|
||||
function isBackground(r, g, b, a, threshold = 28) {
|
||||
if (a < 8) return true;
|
||||
return r <= threshold && g <= threshold && b <= threshold;
|
||||
}
|
||||
|
||||
function removeOuterBackground(rgba, width, height) {
|
||||
const visited = new Uint8Array(width * height);
|
||||
const queue = [];
|
||||
|
||||
for (let x = 0; x < width; x++) {
|
||||
queue.push(x, 0, x, height - 1);
|
||||
}
|
||||
for (let y = 1; y < height - 1; y++) {
|
||||
queue.push(0, y, width - 1, y);
|
||||
}
|
||||
|
||||
while (queue.length) {
|
||||
const y = queue.pop();
|
||||
const x = queue.pop();
|
||||
const idx = y * width + x;
|
||||
if (x < 0 || y < 0 || x >= width || y >= height || visited[idx]) continue;
|
||||
|
||||
const i = idx * 4;
|
||||
const r = rgba[i];
|
||||
const g = rgba[i + 1];
|
||||
const b = rgba[i + 2];
|
||||
const a = rgba[i + 3];
|
||||
if (!isBackground(r, g, b, a)) continue;
|
||||
|
||||
visited[idx] = 1;
|
||||
rgba[i + 3] = 0;
|
||||
|
||||
queue.push(x + 1, y, x - 1, y, x, y + 1, x, y - 1);
|
||||
}
|
||||
|
||||
return rgba;
|
||||
}
|
||||
|
||||
function getBounds(rgba, width, height) {
|
||||
let minX = width;
|
||||
let minY = height;
|
||||
let maxX = 0;
|
||||
let maxY = 0;
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const a = rgba[(y * width + x) * 4 + 3];
|
||||
if (a > 8) {
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pad = 2;
|
||||
return {
|
||||
left: Math.max(0, minX - pad),
|
||||
top: Math.max(0, minY - pad),
|
||||
width: Math.min(width, maxX - minX + 1 + pad * 2),
|
||||
height: Math.min(height, maxY - minY + 1 + pad * 2),
|
||||
};
|
||||
}
|
||||
|
||||
async function processOne(srcPath, outPath) {
|
||||
const { data, info } = await sharp(srcPath)
|
||||
.ensureAlpha()
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
|
||||
const rgba = Buffer.from(data);
|
||||
removeOuterBackground(rgba, info.width, info.height);
|
||||
const bounds = getBounds(rgba, info.width, info.height);
|
||||
|
||||
await sharp(rgba, {
|
||||
raw: { width: info.width, height: info.height, channels: 4 },
|
||||
})
|
||||
.extract(bounds)
|
||||
.png({ compressionLevel: 9, adaptiveFiltering: true })
|
||||
.toFile(outPath);
|
||||
|
||||
const meta = await sharp(outPath).metadata();
|
||||
console.log(
|
||||
`${path.basename(outPath)} -> ${meta.width}x${meta.height}, ${fs.statSync(outPath).size} bytes`
|
||||
);
|
||||
}
|
||||
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
|
||||
for (const { src, out } of mapping) {
|
||||
await processOne(path.join(srcDir, src), path.join(outDir, out));
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const src =
|
||||
"C:/Users/User/Downloads/c4f8c4a7-2566-4644-b752-b652e0c103f5.png";
|
||||
const out = path.join(process.cwd(), "assets/sponsors/emil-animations-dev.png");
|
||||
|
||||
function isBackground(r, g, b, a, threshold = 22) {
|
||||
if (a < 8) return true;
|
||||
return r <= threshold && g <= threshold && b <= threshold;
|
||||
}
|
||||
|
||||
function removeOuterBackground(rgba, width, height) {
|
||||
const visited = new Uint8Array(width * height);
|
||||
const queue = [];
|
||||
|
||||
for (let x = 0; x < width; x++) {
|
||||
queue.push(x, 0, x, height - 1);
|
||||
}
|
||||
for (let y = 1; y < height - 1; y++) {
|
||||
queue.push(0, y, width - 1, y);
|
||||
}
|
||||
|
||||
while (queue.length) {
|
||||
const y = queue.pop();
|
||||
const x = queue.pop();
|
||||
const idx = y * width + x;
|
||||
if (x < 0 || y < 0 || x >= width || y >= height || visited[idx]) continue;
|
||||
|
||||
const i = idx * 4;
|
||||
if (!isBackground(rgba[i], rgba[i + 1], rgba[i + 2], rgba[i + 3])) continue;
|
||||
|
||||
visited[idx] = 1;
|
||||
rgba[i + 3] = 0;
|
||||
queue.push(x + 1, y, x - 1, y, x, y + 1, x, y - 1);
|
||||
}
|
||||
|
||||
return rgba;
|
||||
}
|
||||
|
||||
function getBounds(rgba, width, height) {
|
||||
let minX = width;
|
||||
let minY = height;
|
||||
let maxX = 0;
|
||||
let maxY = 0;
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const a = rgba[(y * width + x) * 4 + 3];
|
||||
if (a > 8) {
|
||||
minX = Math.min(minX, x);
|
||||
minY = Math.min(minY, y);
|
||||
maxX = Math.max(maxX, x);
|
||||
maxY = Math.max(maxY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pad = 2;
|
||||
return {
|
||||
left: Math.max(0, minX - pad),
|
||||
top: Math.max(0, minY - pad),
|
||||
width: Math.min(width, maxX - minX + 1 + pad * 2),
|
||||
height: Math.min(height, maxY - minY + 1 + pad * 2),
|
||||
};
|
||||
}
|
||||
|
||||
const { data, info } = await sharp(src).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
||||
const rgba = Buffer.from(data);
|
||||
removeOuterBackground(rgba, info.width, info.height);
|
||||
const bounds = getBounds(rgba, info.width, info.height);
|
||||
|
||||
const tmp = `${out}.tmp.png`;
|
||||
await sharp(rgba, { raw: { width: info.width, height: info.height, channels: 4 } })
|
||||
.extract(bounds)
|
||||
.resize({ height: 36, withoutEnlargement: true })
|
||||
.png({ compressionLevel: 9, palette: true, quality: 80, effort: 10 })
|
||||
.toFile(tmp);
|
||||
|
||||
fs.renameSync(tmp, out);
|
||||
const meta = await sharp(out).metadata();
|
||||
console.log(`${path.basename(out)} -> ${meta.width}x${meta.height}, ${fs.statSync(out).size} bytes`);
|
||||
Reference in New Issue
Block a user