UNPKG

aahook

Version:

A CLI tool that displays ASCII art when commands succeed or fail

299 lines 10.8 kB
"use strict"; /** * 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