From eb4b713e9d5c66bd05e99d3dc6ff5e1b7df93e7c Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:13:56 -0400 Subject: [PATCH] =?UTF-8?q?Fix=20docx=20import=20missing=20lesson=20conten?= =?UTF-8?q?t=20=E2=80=94=20include=20h1-h6=20block=20elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mammoth outputs week/quiz headings as

/

, not

. The block-element regex only matched

, so all headings were skipped and no lessons were ever created. Expand regex to h[1-6]|p|ul|ol and treat any h-tag as a heading unconditionally. Co-Authored-By: Claude Sonnet 4.6 --- server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server.js b/server.js index 342ab7b..598f182 100644 --- a/server.js +++ b/server.js @@ -44,15 +44,15 @@ async function parseUnitDocxBuffer(buffer, filename) { // Walk the HTML and collect typed elements const elements = []; - for (const m of stripped.matchAll(/<(p|ul|ol)(?:\s[^>]*)?>[\s\S]*?<\/\1>/gi)) { + for (const m of stripped.matchAll(/<(h[1-6]|p|ul|ol)(?:\s[^>]*)?>[\s\S]*?<\/\1>/gi)) { const tag = m[0].match(/^<(\w+)/)[1].toLowerCase(); const inner = m[0].replace(/^<[^>]+>/, '').replace(/<\/\w+>\s*$/, ''); - if (tag === 'p') { + if (tag === 'p' || /^h[1-6]$/.test(tag)) { const text = htmlToText(inner); if (!text) continue; - // A paragraph whose only non-whitespace content is wrapped in is a heading - const isHeading = /^[^<]+<\/strong>\s*$/.test(inner.trim()); + // h1-h6 are always headings;

is a heading only if its sole content is + const isHeading = /^h[1-6]$/.test(tag) || /^[^<]+<\/strong>\s*$/.test(inner.trim()); elements.push({ kind: isHeading ? 'heading' : 'para', text }); } else { const items = [...inner.matchAll(/]*>([\s\S]*?)<\/li>/gi)]