/* 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 += '
| ${inline(escapeHtml(h))} | `).join('') + '
|---|
| ${inline(escapeHtml(c))} | `).join('') + '