/** * dashaFormatter handles the transformation of raw JHora Dasha text * into structured Markdown with hierarchical headings. * It uses a date-aware parsing strategy to ensure complex dasha names * (like in Kalachakra) are preserved correctly. */ const PLANET_NAMES = ['Sun', 'Moon', 'Mars', 'Rah', 'Jup', 'Sat', 'Merc', 'Ket', 'Ven']; const RASI_NAMES = ['Ar', 'Ta', 'Ge', 'Cn', 'Le', 'Vi', 'Li', 'Sc', 'Sg', 'Cp', 'Aq', 'Pi']; /** * Transforms JHora Dasha plaintext into Markdown. */ export function formatDasha(text: string): string { if (!text || !text.trim()) return ''; const lines = text.split('\n'); const result: string[] = []; let currentMD = ''; for (let line of lines) { const trimmed = line.trim(); if (!trimmed) { if (result.length > 0 && result[result.length - 1] !== '') { result.push(''); } continue; } // 1. Detect Section Headers: e.g., "Vimsottari Dasa (paramayush 120yr):" const headerMatch = line.match(/^([A-Za-z0-9\-/\s]+ Dasa)\s*(?:\([^)]*\))?:/); if (headerMatch) { result.push(`### ${headerMatch[1]}:`); result.push(''); currentMD = ''; continue; } // 2. Identify if this line starts a new Mahadasha (MD) const leadingSpaces = line.match(/^\s*/)?.[0].length || 0; const isNewMDLine = leadingSpaces < 3 && trimmed.length > 0; const tokens = trimmed.split(/\s+/); let startIndex = 0; if (isNewMDLine) { const potentialMD = tokens[0]; const isKnownName = [...PLANET_NAMES, ...RASI_NAMES].some(name => potentialMD.toLowerCase().startsWith(name.toLowerCase()) ); // For Kalachakra, MD might be "Vi (Push2)" - group the first two tokens if they look like a pair if (isKnownName || tokens.length >= 3) { let mdName = tokens[0]; // If next token is in parentheses, it's part of the MD name if (tokens[1] && tokens[1].startsWith('(')) { mdName += ` ${tokens[1]}`; startIndex = 2; } else { startIndex = 1; } currentMD = mdName; result.push(`#### ${currentMD}:`); } } // 3. Process remaining tokens using Date-Aware Grouping // A Dasa Item is [Name tokens...] + [Date token (optional at start)] const items: string[] = []; let currentItemParts: string[] = []; for (let i = startIndex; i < tokens.length; i++) { const token = tokens[i]; const isDate = /^\d{4}-\d{2}-\d{2}$/.test(token); if (isDate) { // Group current parts with this date const itemName = currentItemParts.join(' '); items.push(`${itemName} ${token}`.trim()); currentItemParts = []; } else { currentItemParts.push(token); } } // Any remaining tokens without a date (could be the final dangling item or labels) if (currentItemParts.length > 0) { items.push(currentItemParts.join(' ')); } // 4. Output items in a compact horizontal grid (3 items per line) for (let i = 0; i < items.length; i += 3) { const chunk = items.slice(i, i + 3); result.push(chunk.join(' ')); } } return result.join('\n').replace(/\n{3,}/g, '\n\n').trim(); }