Compare commits
5 Commits
900223d261
...
85d17defda
| Author | SHA1 | Date |
|---|---|---|
|
|
85d17defda | |
|
|
f7cc56823c | |
|
|
2bdee2076e | |
|
|
eb7f1ed0c7 | |
|
|
0c71bbeab4 |
|
|
@ -38,7 +38,9 @@
|
||||||
"mcp__chrome-devtools__wait_for",
|
"mcp__chrome-devtools__wait_for",
|
||||||
"WebFetch(domain:banatie.app)",
|
"WebFetch(domain:banatie.app)",
|
||||||
"Bash(npx skills:*)",
|
"Bash(npx skills:*)",
|
||||||
"Bash(identify:*)"
|
"Bash(identify:*)",
|
||||||
|
"Bash(pnpm pdf -- output/html/collecting-asteroids-1.html)",
|
||||||
|
"Bash(pnpm pdf output/html/collecting-asteroids-1.html)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 938 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 785 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 852 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1010 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 927 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 999 KiB |
|
|
@ -7,7 +7,7 @@ const PROJECT_ROOT = resolve(fileURLToPath(import.meta.url), '../../..');
|
||||||
const DEFAULT_INPUT = resolve(PROJECT_ROOT, 'assets/icons/pack2');
|
const DEFAULT_INPUT = resolve(PROJECT_ROOT, 'assets/icons/pack2');
|
||||||
const DEFAULT_THRESHOLD = 230;
|
const DEFAULT_THRESHOLD = 230;
|
||||||
const DEFAULT_FUZZ = 0;
|
const DEFAULT_FUZZ = 0;
|
||||||
const PNG_EXTENSION = /\.png$/i;
|
const IMAGE_EXTENSION = /\.(png|jpe?g|webp)$/i;
|
||||||
|
|
||||||
function parseArgs(args) {
|
function parseArgs(args) {
|
||||||
const result = { input: DEFAULT_INPUT, threshold: DEFAULT_THRESHOLD, fuzz: DEFAULT_FUZZ };
|
const result = { input: DEFAULT_INPUT, threshold: DEFAULT_THRESHOLD, fuzz: DEFAULT_FUZZ };
|
||||||
|
|
@ -22,7 +22,7 @@ function parseArgs(args) {
|
||||||
function collectFiles(input) {
|
function collectFiles(input) {
|
||||||
if (statSync(input).isFile()) return [input];
|
if (statSync(input).isFile()) return [input];
|
||||||
return readdirSync(input)
|
return readdirSync(input)
|
||||||
.filter(f => PNG_EXTENSION.test(f))
|
.filter(f => IMAGE_EXTENSION.test(f))
|
||||||
.sort()
|
.sort()
|
||||||
.map(f => join(input, f));
|
.map(f => join(input, f));
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +135,12 @@ function applyAntiAlias(data, width, height, transparent, fuzz) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function outputPath(filePath) {
|
||||||
|
const ext = extname(filePath).toLowerCase();
|
||||||
|
if (ext === '.png') return filePath;
|
||||||
|
return join(resolve(filePath, '..'), basename(filePath, extname(filePath)) + '.png');
|
||||||
|
}
|
||||||
|
|
||||||
async function removeBackground(filePath, threshold, fuzz) {
|
async function removeBackground(filePath, threshold, fuzz) {
|
||||||
const { data, info } = await sharp(filePath)
|
const { data, info } = await sharp(filePath)
|
||||||
.ensureAlpha()
|
.ensureAlpha()
|
||||||
|
|
@ -145,11 +151,12 @@ async function removeBackground(filePath, threshold, fuzz) {
|
||||||
const { transparent, count } = floodFillFromEdges(data, width, height, threshold);
|
const { transparent, count } = floodFillFromEdges(data, width, height, threshold);
|
||||||
applyAntiAlias(data, width, height, transparent, fuzz);
|
applyAntiAlias(data, width, height, transparent, fuzz);
|
||||||
|
|
||||||
|
const outFile = outputPath(filePath);
|
||||||
await sharp(data, { raw: { width, height, channels: 4 } })
|
await sharp(data, { raw: { width, height, channels: 4 } })
|
||||||
.png()
|
.png()
|
||||||
.toFile(filePath);
|
.toFile(outFile);
|
||||||
|
|
||||||
return count;
|
return { count, outFile };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
@ -163,7 +170,7 @@ async function main() {
|
||||||
const files = collectFiles(input);
|
const files = collectFiles(input);
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
console.error(`No PNG files found in: ${input}`);
|
console.error(`No image files found in: ${input}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,9 +178,10 @@ async function main() {
|
||||||
|
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
files.map(async (filePath) => {
|
files.map(async (filePath) => {
|
||||||
const removed = await removeBackground(filePath, threshold, fuzz);
|
const { count, outFile } = await removeBackground(filePath, threshold, fuzz);
|
||||||
console.log(` ${basename(filePath)} -> ${removed} pixels made transparent`);
|
const suffix = outFile !== filePath ? ` (saved as ${basename(outFile)})` : '';
|
||||||
return removed;
|
console.log(` ${basename(filePath)} -> ${count} pixels made transparent${suffix}`);
|
||||||
|
return count;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"id": "collecting-asteroids-1",
|
||||||
|
"title": "Собери Астероиды",
|
||||||
|
"description": "Match asteroids to cargo ships by weight sum — 3 pages with increasing difficulty",
|
||||||
|
"labels": {
|
||||||
|
"title": "Собери Астероиды",
|
||||||
|
"subtitle": "Загрузи трюмы кораблей!",
|
||||||
|
"footerBubble": "Итого загружено:"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"style": "space-asteroids",
|
||||||
|
"template": "space-base",
|
||||||
|
"asteroids": "assets/icons/pack3-asteroids/"
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "asteroid-matching",
|
||||||
|
"shipsPerPage": 3
|
||||||
|
},
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"heroImage": "assets/hero-images/spaceship2.jpeg",
|
||||||
|
"footerImage": "assets/footers/planet3.jpeg",
|
||||||
|
"heroDirection": "row-reverse",
|
||||||
|
"overlapLevel": 30,
|
||||||
|
"ships": [
|
||||||
|
{ "capacity": 9, "asteroids": [3, 2, 4, 5] },
|
||||||
|
{ "capacity": 11, "asteroids": [5, 4, 2, 6] },
|
||||||
|
{ "capacity": 8, "asteroids": [1, 3, 4, 2] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"heroImage": "assets/hero-images/spaceship5.jpeg",
|
||||||
|
"footerImage": "assets/footers/planet5.jpeg",
|
||||||
|
"heroDirection": "row",
|
||||||
|
"overlapLevel": 50,
|
||||||
|
"ships": [
|
||||||
|
{ "capacity": 10, "asteroids": [6, 1, 3, 4] },
|
||||||
|
{ "capacity": 12, "asteroids": [5, 4, 3, 6] },
|
||||||
|
{ "capacity": 14, "asteroids": [6, 5, 2, 1, 3] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"heroImage": "assets/hero-images/spaceship7.jpeg",
|
||||||
|
"footerImage": "assets/footers/planet8.jpeg",
|
||||||
|
"heroDirection": "row-reverse",
|
||||||
|
"overlapLevel": 70,
|
||||||
|
"ships": [
|
||||||
|
{ "capacity": 13, "asteroids": [5, 4, 3, 1, 6] },
|
||||||
|
{ "capacity": 10, "asteroids": [4, 4, 2, 3] },
|
||||||
|
{ "capacity": 15, "asteroids": [6, 5, 3, 1, 4] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||