Something went wrong. Try again.
mobile bluesky & blacksky app made with flutter lazurite.stormlightlabs.org
mobile bluesky blacksky flutter android ios
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188#!/usr/bin/env bun
import { createCanvas, loadImage } from "canvas";import chalk from "chalk";import { mkdir } from "fs/promises";import { parseArgs } from "util";import inquirer from "inquirer";import sharp from "sharp";import { readFile } from "fs/promises";import path from "path";import type { ThemeColors } from "./types";import { ANDROID_SIZES, IOS_SIZES, THEMES, getThemeFromKey } from "./constants";
async function generateSplashScreen( theme: ThemeColors, width: number, height: number, logoSvgPath: string, outputPath: string,): Promise<void> { const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d");
ctx.fillStyle = theme.background; ctx.fillRect(0, 0, width, height);
const minDim = Math.min(width, height); const logoSize = minDim * 0.22; const gap = minDim * 0.04; const fontSize = minDim * 0.07;
const totalGroupHeight = logoSize + gap + fontSize; const groupCenterY = height / 2; const logoY = groupCenterY - totalGroupHeight / 2; const textY = logoY + logoSize + gap + fontSize / 2;
try { let svgContent = await readFile(logoSvgPath, "utf-8"); svgContent = svgContent.replace('fill="currentColor"', `fill="${theme.primary}"`);
const svgBuffer = Buffer.from(svgContent); const logoPngBuffer = await sharp(svgBuffer).resize(Math.round(logoSize), Math.round(logoSize)).png().toBuffer(); const logo = await loadImage(logoPngBuffer); const logoX = (width - logoSize) / 2;
ctx.drawImage(logo, logoX, logoY, logoSize, logoSize); } catch (error) { console.warn(chalk.yellow("⚠ Warning: Could not load logo, drawing placeholder")); ctx.beginPath(); ctx.arc(width / 2, logoY + logoSize / 2, logoSize / 2, 0, Math.PI * 2); ctx.fillStyle = theme.primary; ctx.fill(); }
ctx.font = `${fontSize}px serif`; ctx.fillStyle = theme.text; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText("Lazurite", width / 2, textY);
const buffer = canvas.toBuffer("image/png"); await Bun.write(outputPath, buffer);}
async function main() { console.log(chalk.cyan.bold("🎨 Lazurite Splash Screen Generator\n"));
const { values, positionals } = parseArgs({ args: Bun.argv.slice(2), options: { theme: { type: "string", short: "t", help: "Theme name (lazurite, oxocarbon, catppuccin, nord, rosePine) or with -light suffix", }, variant: { type: "string", short: "v", help: "Theme variant (light or dark)" }, output: { type: "string", short: "o", default: "./dist", help: "Output directory for generated images" }, all: { type: "boolean", short: "a", default: false, help: "Generate for all themes" }, help: { type: "boolean", short: "h", default: false, help: "Show help" }, }, strict: true, allowPositionals: true, });
if (values.help) { console.log(chalk.bold("Usage:")); console.log(" bun run index.ts [options]\n"); console.log(chalk.bold("Options:")); console.log(" -t, --theme <name> Theme name (lazurite, oxocarbon, catppuccin, nord, rosePine)"); console.log(" -v, --variant <type> Theme variant (light or dark)"); console.log(" -o, --output <dir> Output directory (default: ./dist)"); console.log(" -a, --all Generate for all themes"); console.log(" -h, --help Show this help message\n"); console.log(chalk.bold("Examples:")); console.log(" bun run index.ts -t oxocarbon -v dark"); console.log(" bun run index.ts --all"); console.log(" bun run index.ts -t nord -o ./splash\n"); process.exit(0); }
let themesToGenerate: ThemeColors[] = [];
if (values.all) { themesToGenerate = Object.values(THEMES); } else if (values.theme) { const themeKey = values.variant === "light" ? `${values.theme}-light` : values.theme; const theme = getThemeFromKey(themeKey); themesToGenerate = [theme]; } else { const answers = await inquirer.prompt([ { type: "list", name: "palette", message: "Select theme palette:", choices: [ { name: "Oxocarbon", value: "oxocarbon" }, { name: "Lazurite", value: "lazurite" }, { name: "Catppuccin", value: "catppuccin" }, { name: "Nord", value: "nord" }, { name: "Rosé Pine", value: "rosePine" }, ], }, { type: "list", name: "variant", message: "Select theme variant:", choices: [ { name: "Dark", value: "dark" }, { name: "Light", value: "light" }, ], }, { type: "confirm", name: "generateAll", message: "Generate for all themes instead?", default: false }, ]);
if (answers.generateAll) { themesToGenerate = Object.values(THEMES); } else { const themeKey = answers.variant === "light" ? `${answers.palette}-light` : answers.palette; themesToGenerate = [getThemeFromKey(themeKey)]; } }
const outputDir = values.output || "./dist"; const logoPath = path.resolve("../assets/logo.svg");
console.log(chalk.blue(`\n📁 Output directory: ${outputDir}`)); console.log(chalk.blue(`🎨 Themes to generate: ${themesToGenerate.length}`)); console.log();
for (const theme of themesToGenerate) { console.log(chalk.magenta(`\n🖼 Generating for ${theme.name} (${theme.variant})...`));
const themeDir = path.join(outputDir, theme.name.toLowerCase().replace(" ", "-")); const androidDir = path.join(themeDir, "android"); const iosDir = path.join(themeDir, "ios");
await mkdir(androidDir, { recursive: true }); await mkdir(iosDir, { recursive: true });
for (const size of ANDROID_SIZES) { const fileName = `splash_${size.name}.png`; const outputPath = path.join(androidDir, fileName);
await generateSplashScreen(theme, size.size, Math.round(size.size * 1.5), logoPath, outputPath);
console.log(chalk.green(` ✓ ${fileName}`)); }
for (const size of IOS_SIZES) { const width = size.size; const height = size.height || Math.round(width * 1.5); const fileName = `${size.name}.png`; const outputPath = path.join(iosDir, fileName);
await generateSplashScreen(theme, width, height, logoPath, outputPath);
console.log(chalk.green(` ✓ ${fileName}`)); } }
console.log(chalk.cyan.bold("\n✅ Splash screen generation complete!\n"));}
main().catch((error) => { console.error(chalk.red("✗ Error:"), error.message); process.exit(1);});