/* global React */ /* Tiny markdown -> HTML for copilot/briefing/scenario text, plus ```chart``` extraction. */ function escapeHtml(s) { return s.replace(/&/g, '&').replace(//g, '>'); } // Pull ```chart {json}``` blocks out; return { text, charts:[spec,...] } function splitCharts(raw) { const charts = []; const text = raw.replace(/```chart\s*([\s\S]*?)```/g, (_, body) => { try { charts.push(JSON.parse(body.trim())); } catch (e) { /* ignore bad spec */ } return ''; }).replace(/```[\s\S]*?```/g, m => m); // leave other fences alone return { text: text.trim(), charts }; } function inline(s) { return s .replace(/`([^`]+)`/g, (_, c) => `${c}`) .replace(/\*\*([^*]+)\*\*/g, '$1') .replace(/(^|[^*])\*([^*]+)\*/g, '$1$2') .replace(/_([^_]+)_/g, '$1'); } // Convert a (chart-free) markdown string to an HTML string. function mdToHtml(md) { const lines = md.split('\n'); let html = '', i = 0, inUl = false, inOl = false; const closeLists = () => { if (inUl) { html += ''; inUl = false; } if (inOl) { html += ''; inOl = false; } }; while (i < lines.length) { let ln = lines[i]; // table block if (/^\s*\|.*\|\s*$/.test(ln) && i + 1 < lines.length && /^\s*\|[\s:|-]+\|\s*$/.test(lines[i + 1])) { closeLists(); const header = ln.trim().replace(/^\||\|$/g, '').split('|').map(c => c.trim()); i += 2; let rows = []; while (i < lines.length && /^\s*\|.*\|\s*$/.test(lines[i])) { rows.push(lines[i].trim().replace(/^\||\|$/g, '').split('|').map(c => c.trim())); i++; } html += '' + header.map(h => ``).join('') + ''; html += rows.map(r => '' + r.map(c => ``).join('') + '').join(''); html += '
${inline(escapeHtml(h))}
${inline(escapeHtml(c))}
'; continue; } const esc = escapeHtml(ln); if (/^###\s+/.test(ln)) { closeLists(); html += `

${inline(esc.replace(/^###\s+/, ''))}

`; } else if (/^##\s+/.test(ln)) { closeLists(); html += `

${inline(esc.replace(/^##\s+/, ''))}

`; } else if (/^\s*[-*]\s+/.test(ln)) { if (!inUl) { closeLists(); html += '