diff --git a/suite-pages.js b/suite-pages.js index f34734d..3fdd9d5 100644 --- a/suite-pages.js +++ b/suite-pages.js @@ -46,19 +46,31 @@ function buildPrompt(input) { ); } +// Locate each label wherever it appears and slice between them. Deliberately +// forgiving: the colon is optional (models drop it — "STORY" alone broke the +// first build), markdown emphasis is tolerated, order is taken from the text. function parseSections(raw) { const out = { headline: '', subhead: '', story: [], bullets: [], closing: '' }; const text = String(raw || '').replace(/\r/g, ''); - const grab = function (label, next) { - const re = new RegExp(label + ':\\s*([\\s\\S]*?)(?=\\n(?:' + next + '):|$)', 'i'); - const m = text.match(re); - return m ? m[1].trim() : ''; - }; - out.headline = grab('HEADLINE', 'SUBHEAD|STORY|BULLETS|CLOSING').split('\n')[0].trim().slice(0, 90); - out.subhead = grab('SUBHEAD', 'STORY|BULLETS|CLOSING').split('\n')[0].trim().slice(0, 200); - out.story = grab('STORY', 'BULLETS|CLOSING').split(/\n{2,}/).map(function (p) { return p.replace(/\s+/g, ' ').trim(); }).filter(Boolean).slice(0, 4); - out.bullets = grab('BULLETS', 'CLOSING').split('\n').map(function (b) { return b.replace(/^[-•*]\s*/, '').trim(); }).filter(Boolean).slice(0, 4); - out.closing = grab('CLOSING', 'ZZZZ').replace(/\s+/g, ' ').trim().slice(0, 400); + const LABELS = ['HEADLINE', 'SUBHEAD', 'STORY', 'BULLETS', 'CLOSING']; + const found = []; + LABELS.forEach(function (L) { + const m = text.match(new RegExp('(?:^|\\n)[ \\t]*[*#>\\s]*' + L + '[ \\t]*[*#]*[ \\t]*:?[ \\t]*[*#]*[ \\t]*', 'i')); + if (m) found.push({ label: L, at: m.index, from: m.index + m[0].length }); + }); + if (!found.length) return out; + found.sort(function (a, b) { return a.at - b.at; }); + const seg = {}; + found.forEach(function (f, i) { + const end = i + 1 < found.length ? found[i + 1].at : text.length; + // strip any leftover markdown emphasis the label pattern didn't absorb + seg[f.label] = text.slice(f.from, end).replace(/^[*#\s]+/, '').replace(/[*#\s]+$/, '').trim(); + }); + out.headline = (seg.HEADLINE || '').split('\n')[0].replace(/^["“]|["”]$/g, '').trim().slice(0, 90); + out.subhead = (seg.SUBHEAD || '').split('\n')[0].trim().slice(0, 200); + out.story = (seg.STORY || '').split(/\n{2,}/).map(function (p) { return p.replace(/\s+/g, ' ').trim(); }).filter(Boolean).slice(0, 4); + out.bullets = (seg.BULLETS || '').split('\n').map(function (b) { return b.replace(/^[-•*]\s*/, '').trim(); }).filter(Boolean).slice(0, 4); + out.closing = (seg.CLOSING || '').replace(/\s+/g, ' ').trim().slice(0, 400); return out; }