How do I have so many partners??
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 103 lines 3.9 kB view raw
1import { Command } from 'commander'; 2import { writeFileSync, mkdirSync } from 'fs'; 3import { resolve, join, basename, extname } from 'path'; 4import { parseConfig } from './parser.js'; 5import { simulate } from './simulate.js'; 6import { generateSVG } from './exporters/svg.js'; 7import { generatePNG } from './exporters/png.js'; 8import { generateStandaloneHTML, generateEmbedJS } from './exporters/html.js'; 9 10const program = new Command(); 11 12program 13 .name('polymap') 14 .description('Generate interactive polycule relationship maps from YAML') 15 .version('0.1.0'); 16 17program 18 .command('generate <input>') 19 .description('Generate map from a YAML config file') 20 .option('-o, --output <dir>', 'output directory', '.') 21 .option( 22 '-f, --format <formats>', 23 'comma-separated output formats: html,svg,png', 24 'html,svg,png' 25 ) 26 .option('-n, --name <prefix>', 'output filename prefix (default: input filename without extension)') 27 .option('--width <pixels>', 'PNG output width in pixels', '1400') 28 .option('--title <title>', 'HTML page title', 'Polycule Map') 29 .option('--legend', 'render relationship legend on SVG/PNG exports', false) 30 .option('--no-labels', 'hide edge label text on SVG/PNG exports') 31 .option('--no-names', 'hide node name labels on SVG/PNG exports') 32 .action(async (input: string, opts) => { 33 const inputPath = resolve(input); 34 const outputDir = resolve(opts.output as string); 35 const formats = (opts.format as string).split(',').map(s => s.trim().toLowerCase()); 36 const prefix = (opts.name as string | undefined) ?? basename(inputPath, extname(inputPath)); 37 const pngWidth = parseInt(opts.width as string, 10) || 1400; 38 const title = opts.title as string; 39 const showLegend = Boolean(opts.legend); 40 const showEdgeLabels = opts.labels !== false; 41 const showNames = opts.names !== false; 42 43 mkdirSync(outputDir, { recursive: true }); 44 45 let data; 46 try { 47 data = parseConfig(inputPath); 48 } catch (err) { 49 console.error(`Error parsing config: ${(err as Error).message}`); 50 process.exit(1); 51 } 52 53 console.log(`Loaded ${data.people.length} people, ${data.relationships.length} relationships`); 54 55 // Run force simulation for static exports 56 const needsSimulation = formats.includes('svg') || formats.includes('png'); 57 let simResult: ReturnType<typeof simulate> | null = null; 58 if (needsSimulation) { 59 process.stdout.write('Running force simulation...'); 60 simResult = simulate(data); 61 process.stdout.write(' done\n'); 62 } 63 64 for (const fmt of formats) { 65 switch (fmt) { 66 case 'html': { 67 const htmlPath = join(outputDir, `${prefix}.html`); 68 const embedPath = join(outputDir, `${prefix}-embed.js`); 69 writeFileSync(htmlPath, generateStandaloneHTML(data, { title }), 'utf8'); 70 writeFileSync(embedPath, generateEmbedJS(data), 'utf8'); 71 console.log(`HTML → ${htmlPath}`); 72 console.log(`Embed → ${embedPath}`); 73 break; 74 } 75 76 case 'svg': { 77 const svgPath = join(outputDir, `${prefix}.svg`); 78 const svgStr = await generateSVG(data, simResult!.nodes, simResult!.links, { showLegend, showEdgeLabels, showNames }); 79 writeFileSync(svgPath, svgStr, 'utf8'); 80 console.log(`SVG → ${svgPath}`); 81 break; 82 } 83 84 case 'png': { 85 const pngPath = join(outputDir, `${prefix}.png`); 86 const pngBuf = await generatePNG(data, simResult!.nodes, simResult!.links, pngWidth, showLegend, showEdgeLabels, showNames); 87 writeFileSync(pngPath, pngBuf); 88 console.log(`PNG → ${pngPath}`); 89 break; 90 } 91 92 default: 93 console.warn(`Unknown format "${fmt}" — skipping`); 94 } 95 } 96 97 console.log('Done.'); 98 }); 99 100program.parseAsync(process.argv).catch(err => { 101 console.error(err); 102 process.exit(1); 103});