Files
everforest-resources/scripts/generate-themes.mjs
Ismo Vuorinen 11baabe545 feat: initial scaffold and generator
- Complete project structure with directories for all target platforms
- Template system for CLI tools with color placeholder replacement
- Working generator that processes templates for 6 theme variants
- GitHub workflows for build, snapshots, commitlint, and cli-verify
- Installer and verifier scripts for CLI tool deployment
- Comprehensive documentation and specifications
- Biome 2.x linting and formatting setup
- Husky git hooks for pre-commit validation
2025-09-05 23:06:12 +03:00

187 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Everforest Resources Theme Generator
*
* Generates all theme files from canonical palette definitions.
* Uses template system with color placeholders for CLI tools.
*
* Architecture:
* - Loads palettes from palettes/everforest.(json|yaml)
* - Processes template.txt files with color placeholders
* - Generates all 6 variants (dark/light × hard/medium/soft)
* - Outputs to appropriate directories
*/
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, '..');
/**
* Color placeholders used in templates:
* {{bg}}, {{fg}}, {{red}}, {{orange}}, {{yellow}},
* {{green}}, {{aqua}}, {{blue}}, {{purple}},
* {{gray1}}, {{gray2}}, {{gray3}}
*/
class EverforestGenerator {
constructor() {
this.palette = null;
}
async loadPalette() {
try {
const paletteJson = await fs.readFile(
path.join(rootDir, 'palettes/everforest.json'),
'utf-8'
);
this.palette = JSON.parse(paletteJson);
console.log('✅ Loaded palette from everforest.json');
} catch (error) {
console.error('❌ Failed to load palette:', error.message);
process.exit(1);
}
}
async processTemplate(templatePath, variant, contrast) {
try {
const template = await fs.readFile(templatePath, 'utf-8');
const colors = this.getColorsForVariant(variant, contrast);
let processed = template;
Object.entries(colors).forEach(([key, value]) => {
const placeholder = new RegExp(`{{${key}}}`, 'g');
processed = processed.replace(placeholder, value);
});
return processed;
} catch (error) {
console.error(`❌ Failed to process template ${templatePath}:`, error.message);
return null;
}
}
getColorsForVariant(variant, contrast) {
const variantColors = this.palette.variants[variant][contrast];
const accentColors = this.palette.accents;
const grayColors = this.palette.grays[variant];
return {
bg: variantColors.bg,
bg1: variantColors.bg1,
bg2: variantColors.bg2,
fg: variantColors.fg,
red: accentColors.red,
orange: accentColors.orange,
yellow: accentColors.yellow,
green: accentColors.green,
aqua: accentColors.aqua,
blue: accentColors.blue,
purple: accentColors.purple,
gray1: grayColors.gray1,
gray2: grayColors.gray2,
gray3: grayColors.gray3,
};
}
async generateAll() {
console.log('🎨 Starting Everforest theme generation...');
if (!this.palette) {
await this.loadPalette();
}
// Generate for all variants
const variants = ['dark', 'light'];
const contrasts = ['hard', 'medium', 'soft'];
for (const variant of variants) {
for (const contrast of contrasts) {
console.log(`📝 Generating ${variant}-${contrast} variant...`);
await this.generateVariant(variant, contrast);
}
}
console.log('✨ Theme generation complete!');
}
async generateVariant(variant, contrast) {
console.log(` - Processing ${variant}-${contrast} templates...`);
// Process CLI tool templates
await this.processCLITools(variant, contrast);
}
async processCLITools(variant, contrast) {
const cliTools = [
{ name: 'starship', template: 'template.txt', output: 'starship.toml' },
{ name: 'fzf', template: 'template.txt', output: 'everforest.sh' },
{ name: 'delta', template: 'template.txt', output: 'gitconfig.delta' },
{ name: 'tmux', template: 'template.txt', output: 'everforest.tmux.conf' },
{ name: 'ls_colors', template: 'template.txt', output: 'everforest.sh' },
];
for (const tool of cliTools) {
await this.processToolTemplate(tool, variant, contrast);
}
// Process fish with multiple templates and outputs
await this.processFishTemplates(variant, contrast);
}
async processToolTemplate(tool, variant, contrast) {
const templatePath = path.join(rootDir, 'cli', tool.name, tool.template);
const outputPath = path.join(rootDir, 'cli', tool.name, tool.output);
try {
if (await this.fileExists(templatePath)) {
const processed = await this.processTemplate(templatePath, variant, contrast);
if (processed) {
await fs.writeFile(outputPath, processed);
console.log(` ✅ Generated ${tool.name}/${tool.output}`);
}
}
} catch (error) {
console.error(` ❌ Failed to process ${tool.name}: ${error.message}`);
}
}
async processFishTemplates(variant, contrast) {
const fishPath = path.join(rootDir, 'cli', 'fish');
const colorsTemplate = path.join(fishPath, 'colors-template.txt');
const outputFile = `everforest-${variant}-${contrast}.fish`;
const outputPath = path.join(fishPath, outputFile);
try {
if (await this.fileExists(colorsTemplate)) {
const processed = await this.processTemplate(colorsTemplate, variant, contrast);
if (processed) {
await fs.writeFile(outputPath, processed);
console.log(` ✅ Generated fish/${outputFile}`);
}
}
} catch (error) {
console.error(` ❌ Failed to process fish colors: ${error.message}`);
}
}
async fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
}
// Main execution
if (import.meta.url === `file://${process.argv[1]}`) {
const generator = new EverforestGenerator();
await generator.generateAll();
}