feat(cockpit): the owner flags load at startup from one generated 15 KB sheet; the loading status names the place packs while they load on first lookup

This commit is contained in:
Sameh Khamis committed 2026-11-08 19:35:30 -08:00
1 parent 63aa45c520
commit 5a6cf22be1
35 files changed
+643 -216

No files matched your search

+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env node
/**
* Build the owner flag sheet the orbital Cockpit marks a satellite's owner
* country with (`src/ui/ownerFlag.js`): every flag in OWNER_COUNTRY_CODES
* (`src/layers/satellites/classes.js`), drawn from the flag-icons 4:3 SVGs
* at twice the readout's 20 × 15 px, in one PNG the app loads at startup.
*
* src/data/local_data/owner_flags/owner_flags.png the sheet
* src/ui/ownerFlagSheet.js its layout (generated)
*
* Flag i (in the sorted code list) sits at column i % COLUMNS, row
* floor(i / COLUMNS). A rerun with the same flag-icons and sharp versions
* reproduces both files.
*
* Usage: node scripts/build-owner-flags.mjs
*/
import { readFile, writeFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as prettier from 'prettier';
import sharp from 'sharp';
import { OWNER_COUNTRY_CODES } from '../src/layers/satellites/classes.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const require = createRequire(import.meta.url);
const FLAGS_DIR = path.join(
path.dirname(require.resolve('flag-icons/package.json')),
'flags/4x3',
);
const FLAG_ICONS_VERSION = require('flag-icons/package.json').version;
/** One flag's cell, CSS pixels × 2. */
export const CELL = Object.freeze({ width: 40, height: 30 });
export const COLUMNS = 16;
const SHEET = path.join(
ROOT,
'src/data/local_data/owner_flags/owner_flags.png',
);
const LAYOUT = path.join(ROOT, 'src/ui/ownerFlagSheet.js');
async function flagCell(code) {
const svg = await readFile(path.join(FLAGS_DIR, `${code.toLowerCase()}.svg`));
return sharp(svg, { density: 300 })
.resize(CELL.width, CELL.height, { fit: 'fill' })
.png()
.toBuffer();
}
const codes = [...OWNER_COUNTRY_CODES];
const rows = Math.ceil(codes.length / COLUMNS);
const cells = await Promise.all(codes.map(flagCell));
const sheet = await sharp({
create: {
width: COLUMNS * CELL.width,
height: rows * CELL.height,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 },
},
})
.composite(
cells.map((input, i) => ({
input,
left: (i % COLUMNS) * CELL.width,
top: Math.floor(i / COLUMNS) * CELL.height,
})),
)
.png({ compressionLevel: 9, palette: true, quality: 100, effort: 10 })
.toBuffer();
await writeFile(SHEET, sheet);
const layout = `// Generated by scripts/build-owner-flags.mjs from flag-icons ${FLAG_ICONS_VERSION}; do not edit.
/** The owner flag sheet's layout: flag i at column i % columns, row i / columns. */
export const OWNER_FLAG_SHEET = Object.freeze({
cellWidthPx: ${CELL.width},
cellHeightPx: ${CELL.height},
columns: ${COLUMNS},
rows: ${rows},
codes: Object.freeze(${JSON.stringify(codes)}),
});
`;
await writeFile(
LAYOUT,
await prettier.format(layout, {
...(await prettier.resolveConfig(LAYOUT)),
filepath: LAYOUT,
}),
);
console.log(
`Wrote ${codes.length} flags (${COLUMNS} × ${rows}), ${(sheet.length / 1024).toFixed(1)} KB.`,
);