aahook
Version:
A CLI tool that displays ASCII art when commands succeed or fail
299 lines • 10.8 kB
JavaScript
/**
* Color Engine for applying themes to ASCII art
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ColorEngine = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const ansi_helper_1 = require("./ansi-helper");
class ColorEngine {
constructor(themesDir) {
this.cachedThemes = new Map();
this.themesDir = themesDir || path.join(process.env.HOME || '', '.aahook', 'themes');
}
/**
* Apply a color theme to ASCII art
*/
applyTheme(art, theme) {
// Strip existing colors first
const cleanArt = (0, ansi_helper_1.stripAnsi)(art);
switch (theme.colors.mode) {
case 'line':
return this.applyLineColors(cleanArt, theme.colors.rules);
case 'pattern':
return this.applyPatternColors(cleanArt, theme.colors.rules);
case 'character':
return this.applyCharacterColors(cleanArt, theme.colors.rules);
case 'region':
return this.applyRegionColors(cleanArt, theme.colors.rules);
default:
return cleanArt;
}
}
/**
* Parse and validate a theme file
*/
async parseTheme(themePath) {
try {
const content = fs.readFileSync(themePath, 'utf8');
const theme = JSON.parse(content);
if (this.validateTheme(theme)) {
// Convert string patterns to RegExp if they look like regex patterns
if (theme.colors.mode === 'pattern') {
for (const rule of theme.colors.rules) {
if (typeof rule.match === 'string' && rule.match.startsWith('[') && rule.match.endsWith(']')) {
// Convert regex pattern string to RegExp
rule.match = new RegExp(rule.match, 'g');
}
}
}
return theme;
}
else {
throw new Error('Invalid theme format');
}
}
catch (error) {
throw new Error(`Failed to parse theme: ${error.message}`);
}
}
/**
* Load a predefined or custom theme
*/
async loadTheme(name) {
// Check cache first
if (this.cachedThemes.has(name)) {
return this.cachedThemes.get(name);
}
// Try to load from themes directory
const themePath = path.join(this.themesDir, `${name}.json`);
if (fs.existsSync(themePath)) {
const theme = await this.parseTheme(themePath);
this.cachedThemes.set(name, theme);
return theme;
}
// Try to load from built-in themes (in src directory during development)
const srcPath = path.join(__dirname, '..', '..', '..', 'src', 'lib', 'color', 'themes', `${name}.json`);
if (fs.existsSync(srcPath)) {
const theme = await this.parseTheme(srcPath);
this.cachedThemes.set(name, theme);
return theme;
}
// Try to load from built-in themes (in dist directory for production)
const builtInPath = path.join(__dirname, 'themes', `${name}.json`);
if (fs.existsSync(builtInPath)) {
const theme = await this.parseTheme(builtInPath);
this.cachedThemes.set(name, theme);
return theme;
}
throw new Error(`Theme not found: ${name}`);
}
/**
* Validate theme structure
*/
validateTheme(theme) {
if (!theme || typeof theme !== 'object')
return false;
if (!theme.name || !theme.version || !theme.colors)
return false;
if (!theme.colors.mode || !Array.isArray(theme.colors.rules))
return false;
const validModes = ['pattern', 'line', 'character', 'region'];
if (!validModes.includes(theme.colors.mode))
return false;
return true;
}
/**
* Strip all color codes from ASCII art
*/
stripColors(art) {
return (0, ansi_helper_1.stripAnsi)(art);
}
/**
* Apply colors based on line numbers
*/
applyLineColors(art, rules) {
const lines = art.split('\n');
const coloredLines = [];
for (let i = 0; i < lines.length; i++) {
let colored = false;
for (const rule of rules) {
if (this.isLineRange(rule.match)) {
const range = rule.match;
if (i >= range.start && i <= range.end) {
coloredLines.push((0, ansi_helper_1.colorize)(lines[i], rule.color));
colored = true;
break;
}
}
}
if (!colored) {
coloredLines.push(lines[i]);
}
}
return coloredLines.join('\n');
}
/**
* Apply colors based on patterns
*/
applyPatternColors(art, rules) {
// Process character by character to avoid overlapping replacements
let result = '';
let skipNext = 0;
for (let i = 0; i < art.length; i++) {
if (skipNext > 0) {
skipNext--;
continue;
}
const char = art[i];
let matched = false;
for (const rule of rules) {
if (rule.match instanceof RegExp) {
// Test if this character matches the pattern
if (rule.match.test(char)) {
result += (0, ansi_helper_1.colorize)(char, rule.color);
matched = true;
break;
}
}
else if (typeof rule.match === 'string') {
// Check if string starts at current position
if (art.substr(i, rule.match.length) === rule.match) {
result += (0, ansi_helper_1.colorize)(rule.match, rule.color);
skipNext = rule.match.length - 1;
matched = true;
break;
}
}
}
if (!matched) {
result += char;
}
}
return result;
}
/**
* Apply colors character by character
*/
applyCharacterColors(art, rules) {
let result = '';
for (let i = 0; i < art.length; i++) {
const char = art[i];
let colored = false;
for (const rule of rules) {
if (typeof rule.match === 'string' && char === rule.match) {
result += (0, ansi_helper_1.colorize)(char, rule.color);
colored = true;
break;
}
}
if (!colored) {
result += char;
}
}
return result;
}
/**
* Apply colors to specific regions
*/
applyRegionColors(art, rules) {
const lines = art.split('\n');
const grid = lines.map(line => line.split(''));
for (const rule of rules) {
if (this.isRegion(rule.match)) {
const region = rule.match;
for (let y = region.y; y < Math.min(region.y + region.height, grid.length); y++) {
for (let x = region.x; x < Math.min(region.x + region.width, grid[y].length); x++) {
if (grid[y][x]) {
grid[y][x] = (0, ansi_helper_1.colorize)(grid[y][x], rule.color);
}
}
}
}
}
return grid.map(row => row.join('')).join('\n');
}
/**
* Type guards
*/
isLineRange(match) {
return match && typeof match === 'object' &&
'start' in match && 'end' in match;
}
isRegion(match) {
return match && typeof match === 'object' &&
'x' in match && 'y' in match &&
'width' in match && 'height' in match;
}
/**
* List available themes
*/
async listThemes() {
const themes = [];
// List from themes directory
if (fs.existsSync(this.themesDir)) {
const files = fs.readdirSync(this.themesDir);
for (const file of files) {
if (file.endsWith('.json')) {
themes.push(file.replace('.json', ''));
}
}
}
// Add built-in themes
const builtInThemes = ['rainbow', 'neon', 'ocean', 'fire', 'retro'];
for (const theme of builtInThemes) {
if (!themes.includes(theme)) {
themes.push(theme);
}
}
return themes;
}
/**
* Save a colored ASCII art
*/
async saveColoredArt(art, name) {
const outputDir = path.join(process.env.HOME || '', '.aahook', 'arts', 'colored');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const outputPath = path.join(outputDir, `${name}.txt`);
fs.writeFileSync(outputPath, art, 'utf8');
return outputPath;
}
}
exports.ColorEngine = ColorEngine;
//# sourceMappingURL=color-engine.js.map
;