// Pure formatters for "copy to clipboard": a single case (Q + answer + evidence) or a whole thread. // No DOM/clipboard here (that is the caller's side effect) so this stays testable. import type { AdvisorResponse, EvidenceUnit } from './contract' import { resolveRegulation } from './regulation-display' export interface CopyableCase { question: string response: AdvisorResponse | null } function evidenceLine(e: EvidenceUnit): string { const { familyLabel } = resolveRegulation({ code: e.regulation_code || e.document, short: e.document }) const loc = [e.section, e.paragraph].filter(Boolean).join(' ') const ref = [familyLabel, loc].filter(Boolean).join(', ') return e.url ? `- ${ref} (${e.url})` : `- ${ref}` } /** One case as portable Markdown: question, answer (or general definition), and its evidence. */ export function formatCaseForCopy(c: CopyableCase): string { const parts: string[] = [`### Frage`, c.question.trim()] const r = c.response const answer = (r?.answer || r?.general_answer || '').trim() parts.push('', '### Antwort', answer || '(keine Antwort)') if (r && r.evidence.length > 0) { parts.push('', '### Belege', ...r.evidence.map(evidenceLine)) } if (r && r.footnotes.length > 0) { parts.push( '', '### Fußnoten', ...r.footnotes.map((f, i) => { const head = f.ref || `Fußnote ${i + 1}` const src = [f.document, f.section].filter(Boolean).join(' / ') return `- ${[head, src].filter(Boolean).join(' — ')}${f.text ? `: ${f.text}` : ''}` }), ) } return parts.join('\n') } /** A whole topic thread: a title heading followed by every case, separated by rules. */ export function formatThreadForCopy(title: string, cases: CopyableCase[]): string { const header = `# ${title.trim() || 'Compliance-Advisor-Verlauf'}` const body = cases.map(formatCaseForCopy).join('\n\n---\n\n') return `${header}\n\n${body}` }