import { execa } from 'execa' const TIMEOUT = 30_000 const SCHEMAS = { chafa: { size: { type: 'value' }, colors: { type: 'value', valid: ['none', '2', '8', '16/8', '16', '240', '256', 'full'] }, symbols: { type: 'value' }, dither: { type: 'value', valid: ['none', 'ordered', 'diffusion', 'noise'] }, threshold: { type: 'value' }, 'font-ratio': { type: 'value' }, }, jp2a: { width: { type: 'value' }, height: { type: 'value' }, chars: { type: 'value' }, background: { type: 'value', valid: ['light', 'dark'] }, }, 'ascii-image-converter': { width: { type: 'value' }, height: { type: 'value' }, color: { type: 'bool' }, braille: { type: 'bool' }, threshold: { type: 'value' }, }, img2txt: { width: { type: 'value' }, height: { type: 'value' }, format: { type: 'value', valid: ['ansi', 'utf8', 'html'] }, dither: { type: 'value' }, gamma: { type: 'value' }, }, } export function buildArgs(tool, flags) { const schema = SCHEMAS[tool] if (!schema) throw new Error(`Unknown tool: ${tool}`) const args = [] for (const [key, value] of Object.entries(flags)) { const def = schema[key] if (!def) throw new Error(`Unknown flag for ${tool}: --${key}`) if (def.valid && !def.valid.includes(String(value))) { throw new Error(`Invalid value for --${key}: ${value}. Expected one of: ${def.valid.join(', ')}`) } if (def.type === 'bool') { if (value) args.push(`--${key}`) } else { args.push(`--${key}`, String(value)) } } return args } export async function runChafa(imagePath, flags) { const { width, height, ...rest } = flags const processed = { ...rest } if (width && height) processed.size = `${width}x${height}` else if (width) processed.size = `${width}x` else if (height) processed.size = `x${height}` const args = buildArgs('chafa', processed) return execa('chafa', [...args, imagePath], { timeout: TIMEOUT }) } export async function runJp2a(imagePath, flags) { const args = buildArgs('jp2a', flags) return execa('jp2a', [...args, imagePath], { timeout: TIMEOUT }) } export async function runAsciiImageConverter(imagePath, flags) { const args = buildArgs('ascii-image-converter', flags) return execa('ascii-image-converter', [...args, imagePath], { timeout: TIMEOUT }) } export async function runImgToTxt(imagePath, flags) { const args = buildArgs('img2txt', flags) return execa('img2txt', [...args, imagePath], { timeout: TIMEOUT }) }