import { escapeAttr, escapeHtml } from './citations.js'; export function createAssistantExporter(options) { options = options || {}; var exportCacheKey = ''; var exportCacheItems = null; function invalidate() { exportCacheKey = ''; exportCacheItems = null; } function exportAnswerPdf(state) { state = state || {}; if (!state.lastAnswer) { if (typeof options.showToast === 'function') options.showToast('No answer to export', 'error'); return; } var exportItems = collectExportItems(state.messages || [], state.lastAnswer, state.lastSources || []); var cacheKey = buildExportCacheKey(exportItems, state.lastGeneratedImageSrc || ''); var doc = openExportWindow(); if (!doc) { if (typeof options.showToast === 'function') options.showToast('Allow popups to export PDF', 'error'); return; } if (exportCacheKey === cacheKey && exportCacheItems) { writePrintableChatExport(doc, exportCacheItems, state.lastGeneratedImageSrc || ''); return; } exportCacheKey = cacheKey; exportCacheItems = exportItems; writePrintableChatExport(doc, exportItems, state.lastGeneratedImageSrc || ''); } function openExportWindow() { var doc = window.open('', '_blank', 'width=900,height=1100'); if (!doc) return null; doc.document.open(); doc.document.write('Preparing Clinical Assistant Export

Preparing PDF export...

'); doc.document.close(); return doc; } function writePrintableChatExport(doc, items, imageSrc) { if (!doc || doc.closed) return; items = Array.isArray(items) && items.length ? items : []; var imageHtml = imageSrc ? '

Generated Image

Generated clinical visual
' : ''; var sections = items.map(function (item, idx) { var sources = Array.isArray(item.sources) ? item.sources : []; var heading = item.heading || deriveExportHeading(item.question, idx); var summary = item.summary || ''; var answer = item.answer || ''; var citedNumbers = extractCitedSourceNumbers([summary, answer].filter(Boolean).join('\n\n')); var refs = renderExportRefs(sources, idx + 1, citedNumbers); return '
' + '

' + escapeHtml(heading) + '

' + '
Question: ' + escapeHtml(item.question || '') + '
' + (summary ? '

Summary

' + renderMarkdown(summary, sources, { citationLabel: 'number' }) + '
' : '') + '

Full Generated Answer

' + renderMarkdown(answer, sources, { citationLabel: 'number' }) + '
' + (refs ? '

References

    ' + refs + '
' : '') + '
'; }).join(''); var html = 'Clinical Assistant Export' + '' + '

Clinical Assistant Export

' + '
Export generated ' + escapeHtml(new Date().toLocaleString()) + '
' + imageHtml + sections + ''; doc.document.open(); doc.document.write(html); doc.document.close(); try { var printBtn = doc.document.getElementById('assistant-export-print'); if (printBtn) printBtn.addEventListener('click', function () { doc.focus(); doc.print(); }); } catch (e) {} setTimeout(function () { try { doc.focus(); doc.print(); } catch (e) {} }, 500); } function renderMarkdown(md, sources, renderOptions) { if (typeof options.renderMarkdown === 'function') return options.renderMarkdown(md, sources, renderOptions); return escapeHtml(md); } return { exportAnswerPdf: exportAnswerPdf, invalidate: invalidate }; } function collectExportItems(messages, lastAnswer, lastSources) { var items = []; var pendingQuestion = ''; (messages || []).forEach(function (m) { if (m.role === 'user') { pendingQuestion = m.content || pendingQuestion; return; } if (m.role !== 'assistant' || !m.content) return; if (isUtilityAssistantMessage(m.content)) return; items.push({ question: pendingQuestion || 'Clinical question', heading: deriveExportHeading(pendingQuestion, items.length), summary: '', answer: m.content, sources: Array.isArray(m.sources) && m.sources.length ? m.sources : lastSources }); pendingQuestion = ''; }); if (!items.length && lastAnswer) { items.push({ question: 'Clinical question', heading: 'Clinical Answer', summary: '', answer: lastAnswer, sources: lastSources }); } return items; } function renderExportRefs(sources, sectionNumber, citedNumbers) { var cited = citedNumbers && citedNumbers.length ? new Set(citedNumbers.map(String)) : null; return (sources || []).filter(function (s, idx) { var n = s.number || idx + 1; return !cited || cited.has(String(n)); }).map(function (s, idx) { var n = s.number || idx + 1; var title = s.title || s.resource || 'Untitled source'; var page = s.page || s.page_number || s.pageNumber; return '
  • [' + n + '] ' + escapeHtml(title) + (page ? ', page ' + escapeHtml(page) : '') + '.
  • '; }).join(''); } function extractCitedSourceNumbers(text) { var found = new Set(); String(text || '').replace(/\[(\d+(?:\s*,\s*\d+)*)\]/g, function (_, nums) { nums.split(',').forEach(function (n) { n = String(n || '').trim(); if (n) found.add(n); }); return _; }); return Array.from(found).sort(function (a, b) { return Number(a) - Number(b); }); } function deriveExportHeading(question, idx) { var text = String(question || '').replace(/\s+/g, ' ').trim(); if (!text || /^(what|which|when|why|how|and|also|what dose\??|dose\??)$/i.test(text)) return 'Clinical Question ' + (idx + 1); return text.replace(/[?!.]+$/, '').slice(0, 90); } function isUtilityAssistantMessage(content) { return /^I prepared the image prompt in the \*\*Image \/ Graph\*\* box/i.test(String(content || '')); } export function buildExportCacheKey(items, imageSrc) { return JSON.stringify({ items: (items || []).map(function (item) { return { q: item.question, a: item.answer, s: (item.sources || []).map(function (s) { return [s.number, s.title, s.page]; }) }; }), image: imageSrc ? '1' : '' }); }