Back to Body Journal
SOURCE / PINNED RELEASE

Made of little things.

Body Journal

Release
e8049f8da4e2…
Author-recorded commit
61c04bf0e8aa…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7

Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/report-view.ts
import type { Report } from './ai-contract.js';
import { escape as esc } from './case.js';
export function reportMarkup(report: Report, caseText?: string): string {
  const list = (items: string[]) => `<ul>${items.map(item => `<li>${esc(item)}</li>`).join('')}</ul>`;
  const section = (title: string, content: string) => `<section class="report-section"><h4>${title}</h4>${content}</section>`;
  let names: { id: number; locationOrSymptomName: string }[] = [];
  try {
    const candidates = caseText ? JSON.parse(caseText).concerns : [];
    if (Array.isArray(candidates)) names = candidates.filter(c => c && Number.isInteger(c.id) && typeof c.locationOrSymptomName === 'string');
  } catch { /* Older reports can lack context. */ }
  const name = (id: number) => esc(names.find(c => c.id === id)?.locationOrSymptomName || `Observation ${id}`);
  const p = report.plainLanguage;
  return `<h3>Preliminary assessment</h3><p class="report-caveat">AI-generated from the information you chose to share. Not a clinician’s diagnosis or examination. It may be wrong or miss important problems; discuss it with a qualified healthcare professional.</p><div class="urgency-note" data-urgency="${report.urgency.level}"><strong>Suggested urgency: ${report.urgency.level}</strong><p>${esc(report.urgency.reason)}</p></div>
    ${p ? `<section class="plain-language"><span class="section-caption">IN SIMPLE LANGUAGE</span><h4>What this means for you</h4><p>${esc(p.gist)}</p><h5>What to do next</h5>${list(p.nextSteps)}<h5>When to get help</h5>${list(p.seekHelp)}</section>` : '<p class="field-hint">This is an older report. Generate a new assessment for the detailed and simple-language sections.</p>'}
    ${section('Assessment overview', `<p>${esc(report.summary)}</p>`)}
    ${report.history ? section('History and timeline', `<p>${esc(report.history)}</p>`) : ''}
    ${report.evidence ? section('Evidence and important unknowns', `<h5>Reported by you</h5>${list(report.evidence.reported)}<h5>Visible in supplied patient photos</h5>${report.evidence.visual.length ? list(report.evidence.visual) : '<p>No visual findings from patient photos were recorded.</p>'}<h5>Missing information</h5>${list(report.evidence.missing)}`) : ''}
    ${report.assessments ? section('Assessment of each observation', report.assessments.map(a => `<div class="possible-explanation"><strong>${name(a.concernId)}</strong><p>${esc(a.assessment)}</p></div>`).join('')) : ''}
    ${section('Possible explanations · differential assessment', report.possibilities.length ? report.possibilities.map(p => `<div class="possible-explanation"><strong>${esc(p.name)}</strong><p><b>Supporting evidence:</b> ${esc(p.rationale)}</p>${p.against ? `<p><b>Conflicting evidence:</b> ${esc(p.against)}</p>` : ''}<p><b>Uncertainty:</b> ${esc(p.uncertainty)}</p></div>`).join('') : '<p>Insufficient evidence to suggest specific explanations.</p>')}
    ${report.evaluation?.length ? section('Evaluation to discuss with a clinician', report.evaluation.map(e => `<p><strong>${esc(e.item)}</strong> — ${esc(e.rationale)}</p>`).join('')) : ''}
    ${section('Suggested next steps', list(report.nextSteps))}
    ${section('Who to speak with', `<p><strong>${esc(report.clinician.type)}</strong> — ${esc(report.clinician.reason)}</p>`)}
    ${report.safetyNet ? section('Changes that should prompt further help', list(report.safetyNet)) : ''}
    ${report.questions.length ? section('Questions to clarify', list(report.questions)) : ''}
    ${section('Limits of this assessment', list(report.limitations))}`;
}