/** * Text shaping for the call reader. Model summaries arrive as dense * paragraphs; on a wide pane they are a wall. Splitting them into one point * per sentence and setting the figures in bold is what makes them scannable. */ /** * Sentence boundaries, minus the periods inside abbreviations ("U.S.", * "Inc.", "vs.") and figures ("$27.8 million"). */ const SENTENCE_BOUNDARY = /(? { if (current.length > 0) paragraphs.push(current.join(" ")); current = []; words = 0; }; for (const sentence of splitSentences(text)) { const count = sentence.split(/\s+/).length; if (words >= targetWords * 0.4 && TOPIC_SHIFT.test(sentence)) flush(); if (words > 0 && words + count > targetWords * 1.25) flush(); current.push(sentence); words += count; if (words >= targetWords) flush(); } flush(); return paragraphs; } export function splitSentences(text: string): string[] { const out: string[] = []; let rest = text.trim(); while (rest.length > 0) { const match = SENTENCE_BOUNDARY.exec(rest); if (!match) { out.push(rest); break; } // Keep the closing punctuation, drop the whitespace that followed it. const cut = match.index + match[0].trimEnd().length; out.push(rest.slice(0, cut).trim()); rest = rest.slice(match.index + match[0].length); } return out.filter(Boolean); }