@reddoorla/maintenance 0.74.0 → 0.75.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{blux-OL5HKP26.js → blux-C24OVDDW.js} +95 -1
- package/dist/blux-C24OVDDW.js.map +1 -0
- package/dist/{chunk-KKL3RYFQ.js → chunk-2AUUAJ5X.js} +2 -2
- package/dist/{chunk-PERP7LSS.js → chunk-TDG5Z5OT.js} +18 -5
- package/dist/chunk-TDG5Z5OT.js.map +1 -0
- package/dist/cli/bin.js +3 -3
- package/dist/cli/commands/audit.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -2
- package/dist/{init-7PTP4O2K.js → init-Y2DMKKE7.js} +3 -3
- package/dist/{launch-W33FWWOZ.js → launch-ZDX7UA3Y.js} +2 -2
- package/package.json +1 -1
- package/dist/blux-OL5HKP26.js.map +0 -1
- package/dist/chunk-PERP7LSS.js.map +0 -1
- /package/dist/{chunk-KKL3RYFQ.js.map → chunk-2AUUAJ5X.js.map} +0 -0
- /package/dist/{init-7PTP4O2K.js.map → init-Y2DMKKE7.js.map} +0 -0
- /package/dist/{launch-W33FWWOZ.js.map → launch-ZDX7UA3Y.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/blux.ts","../src/blux/parse.ts","../src/blux/archetype.ts","../src/blux/normalize.ts","../src/blux/collections.ts","../src/blux/assemble.ts","../src/blux/emit/plan.ts","../src/blux/emit/custom-types.ts","../src/blux/emit/coerce-html.ts","../src/blux/emit/slices.ts","../src/blux/emit/flatten.ts","../src/blux/emit/migration-plan.ts","../src/blux/emit/theme.ts","../src/blux/emit/review.ts","../src/blux/validate.ts","../src/blux/grid/parse-grid.ts","../src/blux/grid/token.ts","../src/blux/grid/leaf.ts","../src/blux/grid/classify-band.ts","../src/blux/grid/extract-map.ts","../src/blux/grid/feed-grid.ts","../src/blux/products.ts","../src/blux/emit/block-styles.ts","../src/blux/emit/grid-slice.ts","../src/blux/emit/grid-plan.ts","../src/blux/emit/presentation.ts","../src/blux/emit/convert.ts","../src/blux/emit/site-config.ts","../src/blux/emit/validate-layout.ts","../src/blux/emit/rewrite-manifest.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { assembleIR } from \"../../blux/assemble.js\";\nimport { buildMigrationPlan } from \"../../blux/emit/migration-plan.js\";\nimport { emitThemeCss, emitRolesCss, emitButtonsCss } from \"../../blux/emit/theme.js\";\nimport { buildReviewManifest } from \"../../blux/emit/review.js\";\nimport { validateCoverage } from \"../../blux/validate.js\";\nimport { parseGridBands, extractMapConfig } from \"../../blux/grid/index.js\";\nimport { feedAssetBase, extFor } from \"../../blux/grid/feed-grid.js\";\nimport { materializeProducts, type ProductRecord } from \"../../blux/products.js\";\nimport { convertExport, convertSite, sitePages } from \"../../blux/emit/convert.js\";\nimport { buildSiteConfig, socialHrefResolverFromHtml } from \"../../blux/emit/site-config.js\";\nimport { validateLayout, formatLayoutReport } from \"../../blux/emit/validate-layout.js\";\nimport { rewriteManifestUrls } from \"../../blux/emit/rewrite-manifest.js\";\nimport type { SitePresentation } from \"../../blux/emit/presentation.js\";\nimport type { MigrationPlan } from \"../../blux/emit/plan.js\";\n\nexport type BluxCommandOptions = {\n /** Output directory for emit (default: <exportDir>/blux-out). */\n out?: string;\n /** Base URL of the converted site for the review manifest. */\n convertedBase?: string;\n /** Base URL of the original Blux site (default: https://<site.json domain>). */\n bluxBase?: string;\n /** Reconstruct + HEAD-probe CDN URLs for used assets the HTML scrape missed (network). */\n probe?: boolean;\n /** validate: the converted site's rendered HTML — a file path or http(s) URL. */\n against?: string;\n /** Test seam for --probe; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n cwd?: string;\n verbose?: boolean;\n};\n\n/** `blux <action> [dir]` — emit: Blux export dir → migration plan + custom-type\n * schemas + theme CSS + review manifest, all deterministic and offline.\n * migrate: a previously emitted plan → live Prismic (creds-gated; the runner\n * is imported lazily so emit runs never touch @prismicio).\n * validate: offline layout-fidelity gate (parse+classify index.html, diff the\n * emitted manifest vs the source answer key) — exits non-zero on drift.\n * --against <file|url> additionally runs content coverage of a rendered page.\n * grid: parse rendered index.html → grid-tree.json (layout tree).\n * convert: parse+classify index.html + assemble IR from site.json → the grid\n * migration-plan.json (page doc) + blux-presentation.json (render manifest) +\n * theme.css (+ map-config.json when a map is present), all offline, no creds. */\nexport async function runBluxCommand(\n action: string,\n dir: string | undefined,\n opts: BluxCommandOptions,\n): Promise<{ output: string; code: number }> {\n if (action === \"emit\") {\n if (!dir) return { output: \"blux emit needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return {\n output: `could not read site.json in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const out = opts.out ?? join(dir, \"blux-out\");\n const htmlPaths = (\n await glob([\"**/*.html\"], { cwd: dir, absolute: true, ignore: [\"blux-out/**\"] })\n ).sort();\n const htmls = await Promise.all(htmlPaths.map((p) => readFile(p, \"utf-8\")));\n\n const ir = assembleIR({ siteJson, htmls });\n\n let probeLine = \"\";\n if (opts.probe) {\n const { probeAssetUrls } = await import(\"../../blux/emit/probe.js\");\n // derive \"used\" from the plan's own __asset_id markers — the single\n // source of truth for which assets documents actually reference\n const used = new Set<string>();\n const collect = (v: unknown): void => {\n if (!v || typeof v !== \"object\") return;\n if (\"__asset_id\" in v) used.add((v as { __asset_id: string }).__asset_id);\n else if (Array.isArray(v)) v.forEach(collect);\n else Object.values(v).forEach(collect);\n };\n buildMigrationPlan(ir).documents.forEach((d) => collect(d.data));\n\n const targets = ir.assets.filter((a) => used.has(a.id) && !a.sourceUrl);\n const probed = await probeAssetUrls(targets, ir.meta.bluxSiteId, opts.fetchImpl ?? fetch);\n let hits = 0;\n for (const a of ir.assets) {\n const url = probed.get(a.id);\n if (url) {\n a.sourceUrl = url;\n hits++;\n }\n }\n ir.diagnostics = ir.diagnostics.filter(\n (d) => !(d.kind === \"unresolved-asset\" && probed.get(d.where)),\n );\n probeLine = `probe resolved ${hits}/${targets.length} used assets`;\n }\n\n const plan = buildMigrationPlan(ir);\n const manifest = buildReviewManifest(ir, {\n convertedBase: opts.convertedBase ?? \"http://localhost:5173\",\n bluxBase: opts.bluxBase ?? `https://${ir.meta.domain}`,\n });\n\n await mkdir(join(out, \"customtypes\"), { recursive: true });\n await writeFile(join(out, \"ir.json\"), JSON.stringify(ir, null, 2));\n await writeFile(join(out, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n const rolesCss = emitRolesCss(ir.theme);\n const buttonsCss = emitButtonsCss(ir.theme);\n await writeFile(\n join(out, \"theme.css\"),\n emitThemeCss(ir.theme) +\n (rolesCss ? \"\\n\" + rolesCss : \"\") +\n (buttonsCss ? \"\\n\" + buttonsCss : \"\"),\n );\n await writeFile(join(out, \"review-manifest.json\"), JSON.stringify(manifest, null, 2));\n await writeFile(\n join(out, \"styles-manifest.json\"),\n JSON.stringify(plan.stylesManifest, null, 2),\n );\n for (const ct of plan.customTypes) {\n await writeFile(join(out, \"customtypes\", `${ct.id}.json`), JSON.stringify(ct.json, null, 2));\n }\n\n const resolved = ir.assets.filter((a) => a.sourceUrl !== null).length;\n const diagnostics = [...ir.diagnostics, ...plan.diagnostics];\n const lines = [\n `site: ${ir.meta.name} (${ir.meta.domain})`,\n ...(probeLine ? [probeLine] : []),\n `pages: ${ir.pages.length} | custom types: ${plan.customTypes.length} | documents: ${plan.documents.length} | assets: ${resolved}/${ir.assets.length} resolved`,\n `diagnostics: ${diagnostics.length}`,\n ...diagnostics.map((d) => ` - [${d.kind}] ${d.where}: ${d.message}`),\n `wrote ${out}`,\n ];\n return { output: lines.join(\"\\n\"), code: 0 };\n }\n\n if (action === \"migrate\") {\n if (!dir) {\n return {\n output: \"blux migrate needs an emitted output directory (or a plan .json path).\",\n code: 1,\n };\n }\n if (!process.env.PRISMIC_REPOSITORY_NAME || !process.env.PRISMIC_WRITE_TOKEN) {\n return {\n output: \"Set PRISMIC_REPOSITORY_NAME and PRISMIC_WRITE_TOKEN to run a live migration.\",\n code: 1,\n };\n }\n const planPath = dir.endsWith(\".json\") ? dir : join(dir, \"migration-plan.json\");\n const plan = JSON.parse(await readFile(planPath, \"utf-8\")) as MigrationPlan;\n const { pushCustomTypes, runMigration } = await import(\"../../blux/emit/run-migration.js\");\n const pushed = await pushCustomTypes(plan.customTypes);\n // stream progress to stderr — a throttled run over many assets/docs takes\n // minutes and silence reads as a hang; stdout stays the result summary\n const r = await runMigration(plan, (line) => process.stderr.write(`${line}\\n`));\n const missing = r.missingAssets.length\n ? `\\nWARNING missing assets: ${r.missingAssets.join(\", \")}`\n : \"\";\n // Rewrite the render manifest's media urls from the CDN url the export\n // carries → the durable Prismic url we just uploaded to. Skipped silently\n // when no manifest sits beside the plan (e.g. an archetype-only emit).\n let manifestNote = \"\";\n const manifestPath = join(dirname(planPath), \"blux-presentation.json\");\n let manifestRaw: string | null = null;\n try {\n manifestRaw = await readFile(manifestPath, \"utf-8\");\n } catch {\n /* no manifest beside the plan (e.g. archetype-only emit) — nothing to\n rewrite; not an error. A rewrite failure on a manifest that DOES\n exist must surface, not be swallowed (it silently strands the render\n on Blux CDN urls). */\n }\n if (manifestRaw !== null) {\n const manifest = JSON.parse(manifestRaw) as SitePresentation;\n const rewritten = rewriteManifestUrls(manifest, r.assetUrlByCdn);\n await writeFile(manifestPath, JSON.stringify(rewritten, null, 2) + \"\\n\");\n manifestNote = \"\\nmanifest media rewritten to Prismic urls\";\n }\n return {\n output:\n `custom types pushed: ${pushed.join(\", \") || \"none\"}\\n` +\n `assets: ${r.assetsUploaded} uploaded, ${r.assetsReused} reused | ` +\n `documents: ${r.docsCreated} created, ${r.docsUpdated} updated → ` +\n `${process.env.PRISMIC_REPOSITORY_NAME} (publish the migration release in the dashboard)` +\n missing +\n manifestNote,\n code: 0,\n };\n }\n\n if (action === \"validate\") {\n if (!dir) return { output: \"blux validate needs a Blux export directory.\", code: 1 };\n let exportHtml: string;\n try {\n exportHtml = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return { output: `could not read index.html in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n // Resolve the optional --against render FIRST so a bad target hard-fails\n // before we spend the convert pipeline (and so its error message wins).\n let rendered: string | null = null;\n if (opts.against) {\n try {\n if (/^https?:\\/\\//.test(opts.against)) {\n const res = await (opts.fetchImpl ?? fetch)(opts.against);\n if (!res.ok) {\n return {\n output: `could not fetch --against ${opts.against}: HTTP ${res.status}`,\n code: 1,\n };\n }\n rendered = await res.text();\n } else {\n rendered = await readFile(opts.against, \"utf-8\");\n }\n } catch (err) {\n return {\n output: `could not read --against ${opts.against}: ${(err as Error).message}`,\n code: 1,\n };\n }\n }\n\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read site.json in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n\n const { specs, presentation } = convertExport({ html: exportHtml, siteJson });\n const layout = validateLayout(specs, presentation);\n const lines = [formatLayoutReport(layout)];\n\n // Content coverage is informational only — it names export text the render\n // dropped, but layout fidelity alone gates the exit code below. A coverage\n // gap never flips a faithful layout to a non-zero exit.\n if (rendered !== null) {\n const report = validateCoverage(exportHtml, rendered);\n lines.push(\n \"\",\n `content coverage: ${report.covered}/${report.total} runs (${report.coveragePct}%)`,\n ...(report.missing.length\n ? [\n \"missing runs — export text absent from the render:\",\n ...report.missing.map((m) => ` - ${m}`),\n ]\n : [\"all export text runs present in the render\"]),\n );\n }\n\n return { output: lines.join(\"\\n\"), code: layout.faithful ? 0 : 1 };\n }\n\n if (action === \"grid\") {\n if (!dir) return { output: \"blux grid needs a Blux export directory.\", code: 1 };\n let html: string;\n try {\n html = await readFile(join(dir, \"index.html\"), \"utf-8\");\n } catch (err) {\n return {\n output: `could not read index.html in ${dir}: ${(err as Error).message}`,\n code: 1,\n };\n }\n const bands = parseGridBands(html);\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"grid-tree.json\"), JSON.stringify(bands, null, 2));\n const mapConfig = extractMapConfig(html);\n if (mapConfig) {\n await writeFile(\n join(outDir, \"map-config.json\"),\n JSON.stringify(mapConfig, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n return {\n output:\n `Parsed ${bands.length} bands → ${join(outDir, \"grid-tree.json\")}` +\n (mapConfig ? \", map config extracted\" : \"\"),\n code: 0,\n };\n }\n\n if (action === \"convert\") {\n if (!dir) return { output: \"blux convert needs a Blux export directory.\", code: 1 };\n let siteJson: unknown;\n try {\n siteJson = JSON.parse(await readFile(join(dir, \"site.json\"), \"utf-8\"));\n } catch (err) {\n return { output: `could not read export in ${dir}: ${(err as Error).message}`, code: 1 };\n }\n // Every site page renders to its own index.html: the homepage at the\n // export root, the rest at <path>/index.html. A page dir the export\n // doesn't contain (an unexported draft) is skipped — convertSite records\n // the missing-page-html diagnostic.\n const htmlByUid = new Map<string, string>();\n for (const p of sitePages(siteJson)) {\n const file = p.path ? join(dir, p.path, \"index.html\") : join(dir, \"index.html\");\n try {\n htmlByUid.set(p.uid, await readFile(file, \"utf-8\"));\n } catch {\n /* missing page dir — diagnosed by convertSite */\n }\n }\n if (!htmlByUid.size) {\n return { output: `could not read any page html in ${dir}`, code: 1 };\n }\n const { pages, ir, plan, presentation } = convertSite({ siteJson, htmlByUid });\n\n const outDir = opts.out ?? join(dir, \"blux-out\");\n await mkdir(outDir, { recursive: true });\n await writeFile(join(outDir, \"migration-plan.json\"), JSON.stringify(plan, null, 2));\n await writeFile(\n join(outDir, \"blux-presentation.json\"),\n JSON.stringify(presentation, null, 2) + \"\\n\",\n );\n {\n const buttonsCss = emitButtonsCss(ir.theme);\n await writeFile(\n join(outDir, \"theme.css\"),\n emitThemeCss(ir.theme) +\n \"\\n\" +\n emitRolesCss(ir.theme) +\n (buttonsCss ? \"\\n\" + buttonsCss : \"\"),\n );\n }\n // Map configs are per page now (informational — the presentation manifest\n // co-locates each map on its band).\n const mapConfigs = Object.fromEntries(\n pages.filter((p) => p.mapConfig).map((p) => [p.uid, p.mapConfig]),\n );\n if (Object.keys(mapConfigs).length) {\n await writeFile(join(outDir, \"map-config.json\"), JSON.stringify(mapConfigs, null, 2) + \"\\n\");\n }\n // Site chrome (nav dropdowns + footer socials/copyright) → site-config.json,\n // consumed by the render's Nav/Footer. The nav logo is chrome, not on any\n // page grid, so it isn't in the scraped urlMap — resolve it the scraped url\n // first, else reconstruct the CDN url (base + uuid + ext, like feed media).\n {\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaDict = (siteJson as { media?: Record<string, { type?: string }> }).media ?? {};\n const extByMime: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n };\n const resolveLogo = (uuid: string): string | null => {\n const scraped = sourceUrlById.get(uuid);\n if (scraped) return scraped;\n const ext = extByMime[String(mediaDict[uuid]?.type ?? \"\")];\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n // Footer social profile urls aren't in the export (Blux injects them at\n // render time), but they ride the scraped live footer — recover them by\n // host from the same page html the grid was built from.\n const resolveSocialHref = socialHrefResolverFromHtml([...htmlByUid.values()]);\n const siteConfig = buildSiteConfig(siteJson, resolveLogo, resolveSocialHref);\n await writeFile(join(outDir, \"site-config.json\"), JSON.stringify(siteConfig, null, 2) + \"\\n\");\n }\n // Product catalog → products.json. A Blux \"products\" feed drives a detail\n // page per record (/products/<slug>) from a template the static export\n // drops; rebuild the catalog deterministically with cleaned categories +\n // resolved images. Image urls reconstruct like feed media (base + uuid +\n // ext) — the scraped url map only covers page assets, not feed records.\n {\n const feeds =\n (siteJson as { feeds?: Record<string, { publish?: unknown; items?: unknown }> }).feeds ??\n {};\n const productFeed = Object.values(feeds).find((f) => f?.publish === \"products\");\n if (productFeed && Array.isArray(productFeed.items)) {\n const base = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const mediaMeta =\n (siteJson as { media?: Record<string, { type?: string; name?: string }> }).media ?? {};\n const resolveImage = (uuid: string): string | null => {\n const ext = extFor(mediaMeta[uuid]?.type, mediaMeta[uuid]?.name);\n return ext ? `${base}${uuid}.${ext}` : null;\n };\n const products = materializeProducts(productFeed.items as ProductRecord[], resolveImage);\n await writeFile(join(outDir, \"products.json\"), JSON.stringify(products, null, 2) + \"\\n\");\n }\n }\n // The favicon never rides the migration plan (plan assets get uploaded to\n // Prismic media — the wrong destination), so convert downloads it directly\n // beside the other outputs. This is convert's ONLY network touch and it\n // uses the same injectable fetch seam as --probe, so tests stay offline\n // and convertExport itself stays pure. A fetch failure never fails the\n // command — the {assetId, url} pair is preserved as favicon.json so the\n // download can be re-run by hand.\n let faviconLine = \"\";\n if (ir.meta.favicon?.sourceUrl) {\n const { assetId, sourceUrl } = ir.meta.favicon;\n try {\n const res = await (opts.fetchImpl ?? fetch)(sourceUrl);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n await writeFile(join(outDir, \"favicon.png\"), new Uint8Array(await res.arrayBuffer()));\n faviconLine = `\\nfavicon → ${join(outDir, \"favicon.png\")}`;\n } catch (err) {\n await writeFile(\n join(outDir, \"favicon.json\"),\n JSON.stringify({ assetId, url: sourceUrl }, null, 2) + \"\\n\",\n );\n faviconLine =\n `\\nfavicon fetch failed (${(err as Error).message}) — ` +\n `url preserved in ${join(outDir, \"favicon.json\")}`;\n }\n }\n const layoutByUid: Record<string, ReturnType<typeof validateLayout>> = {};\n const reportLines: string[] = [];\n for (const p of pages) {\n const pagePresentation = presentation.pages[p.uid];\n if (!pagePresentation) continue;\n const layout = validateLayout(p.specs, pagePresentation);\n layoutByUid[p.uid] = layout;\n reportLines.push(`[${p.uid}] ${formatLayoutReport(layout)}`);\n }\n await writeFile(\n join(outDir, \"layout-report.json\"),\n JSON.stringify({ pages: layoutByUid }, null, 2) + \"\\n\",\n );\n const totalBands = pages.reduce((n, p) => n + p.bands.length, 0);\n const missing = ir.diagnostics.filter((d) => d.kind === \"missing-page-html\");\n return {\n output:\n `Converted ${pages.length} pages / ${totalBands} bands → ${outDir} ` +\n `(${plan.documents.length} page documents` +\n (Object.keys(mapConfigs).length\n ? `, map config on ${Object.keys(mapConfigs).join(\", \")}`\n : \"\") +\n \")\" +\n (missing.length ? `\\nskipped (no html): ${missing.map((d) => d.where).join(\", \")}` : \"\") +\n faviconLine +\n \"\\n\" +\n reportLines.join(\"\\n\"),\n code: 0,\n };\n }\n\n return {\n output: `unknown blux action '${action}'. Use: emit, migrate, validate, grid, convert.`,\n code: 1,\n };\n}\n","/** Style config for a block's title/body. `class: \"disable\"` (or the bare\n * string \"disable\") hides that element on the rendered site. */\nexport type BluxTextStyle = string | { class?: string; [key: string]: unknown };\n\nexport type BluxBlock = {\n title?: string;\n _title?: BluxTextStyle;\n body?: string;\n _body?: BluxTextStyle;\n media?: { media?: string };\n backgroundMedia?: { media?: string };\n class?: string;\n ratio?: string;\n loadEffect?: string;\n items?: BluxBlock[];\n styles?: Record<string, unknown>;\n};\n\nconst hasDisable = (cls: unknown) =>\n typeof cls === \"string\" && cls.split(/\\s+/).includes(\"disable\");\n\n/** Display text of a Blux title/body pair, or undefined when the element is\n * hidden. Blux stores the text itself in `title`/`body`; the underscore twin\n * (`_title`/`_body`) is style config whose `class: \"disable\"` hides the\n * element, so its text must not be migrated. */\nexport function visibleText(text: unknown, style: BluxTextStyle | undefined): string | undefined {\n const s = typeof text === \"number\" && Number.isFinite(text) ? String(text) : text;\n if (typeof s !== \"string\" || s.trim() === \"\") return undefined;\n if (hasDisable(style)) return undefined;\n if (typeof style === \"object\" && style !== null && hasDisable(style.class)) return undefined;\n return s.trim();\n}\nexport type BluxPage = {\n title?: string;\n description?: string;\n /** The page's routing slug. Often empty/absent — the slug then derives from\n * the title. The FIRST page is the homepage regardless (Blux is positional). */\n url?: string;\n items?: BluxBlock[];\n};\nexport type BluxFeed = {\n name?: string;\n source?: string;\n publish?: string;\n fields?: { title?: string; field?: string; type?: string }[];\n items?: Record<string, unknown>[];\n};\nexport type BluxMedia = { name?: string; type?: string; size?: unknown; siteID?: string };\nexport type BluxRaw = {\n meta: { name: string; domain: string; bluxSiteId: string };\n pages: BluxPage[];\n feeds: Record<string, BluxFeed>;\n media: Record<string, BluxMedia>;\n styles: {\n colors?: Record<string, string>;\n text?: Record<string, unknown>;\n buttons?: Record<string, unknown>;\n };\n nav: { title?: string; url?: string }[];\n settings: {\n fonts?: { heading?: string; body?: string };\n /** The site favicon, declared as a bare media uuid ({\"media\": \"<uuid>\"}). */\n favicon?: { media?: string };\n };\n};\n\nfunction asObject(v: unknown): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) {\n throw new Error(\"Invalid site.json: expected an object\");\n }\n return v as Record<string, unknown>;\n}\n\nexport function parseBluxSite(input: unknown): BluxRaw {\n const j = asObject(input);\n const content = (j.content ?? {}) as { pages?: BluxPage[] };\n const styles = (j.styles ?? {}) as BluxRaw[\"styles\"];\n const nav = ((j.navigation as { items?: unknown }[] | undefined)?.[0]?.items ?? []) as {\n title?: string;\n url?: string;\n }[];\n return {\n meta: {\n name: String(j.name ?? \"\"),\n domain: String(j.domain ?? \"\"),\n bluxSiteId: String(j.id ?? \"\"),\n },\n pages: Array.isArray(content.pages) ? content.pages : [],\n feeds: (j.feeds ?? {}) as Record<string, BluxFeed>,\n media: (j.media ?? {}) as Record<string, BluxMedia>,\n styles,\n nav,\n settings: (j.settings ?? {}) as BluxRaw[\"settings\"],\n };\n}\n","import { visibleText, type BluxBlock } from \"./parse.js\";\n\nconst nonEmpty = (v: unknown): boolean =>\n v != null &&\n v !== \"\" &&\n !(Array.isArray(v) && v.length === 0) &&\n !(typeof v === \"object\" && !Array.isArray(v) && Object.keys(v as object).length === 0);\n\nexport type ArchetypeResult = {\n sliceType: \"hero\" | \"media_text\" | \"rich_text\" | \"grid\" | \"slider\" | \"collection_list\";\n variation: string;\n confidence: number;\n};\n\nexport function archetype(b: BluxBlock): ArchetypeResult {\n const heading = visibleText(b.title, b._title) !== undefined;\n const text = visibleText(b.body, b._body) !== undefined;\n const media = nonEmpty(b.media?.media);\n const bg = nonEmpty(b.backgroundMedia?.media);\n const kids = Array.isArray(b.items) && b.items.length > 0;\n const cls = nonEmpty(b.class) ? String(b.class) : null;\n\n // Slides keep their grouping even under a background — exploding a\n // carousel into siblings loses more than dropping its backdrop does.\n if (kids && cls === \"slides\")\n return { sliceType: \"slider\", variation: \"default\", confidence: 0.85 };\n // A background image/video makes a hero even with no visible copy — Blux\n // uses text-less full-bleed banners (e.g. a hero video with a disabled label).\n if (bg)\n return { sliceType: \"hero\", variation: \"default\", confidence: heading || text ? 0.9 : 0.7 };\n if (kids)\n return { sliceType: \"grid\", variation: \"default\", confidence: cls === \"grid\" ? 0.9 : 0.7 };\n // Any visible copy next to media is a media_text; media alone still is,\n // just with less certainty.\n if (media && heading && text)\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.9 };\n if (media && (heading || text))\n return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.75 };\n if (media) return { sliceType: \"media_text\", variation: \"imageRight\", confidence: 0.6 };\n if (heading && text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.85 };\n if (heading || text) return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.6 };\n return { sliceType: \"rich_text\", variation: \"default\", confidence: 0.2 };\n}\n","import { visibleText, type BluxBlock, type BluxRaw, type BluxTextStyle } from \"./parse.js\";\nimport { archetype } from \"./archetype.js\";\nimport type {\n ButtonStyleIR,\n FontLoad,\n PageIR,\n SectionIR,\n TextStyleIR,\n ThemeIR,\n Diagnostic,\n} from \"./ir.js\";\n\n/** The styles.text role (\"text5\") a block's _title/_body class points at. */\nfunction textRole(style: BluxTextStyle | undefined): string | undefined {\n const cls = typeof style === \"object\" && style !== null ? style.class : style;\n if (typeof cls !== \"string\") return undefined;\n return cls.split(/\\s+/).find((c) => /^text\\d+$/.test(c));\n}\n\nconst str = (x: unknown): string =>\n typeof x === \"string\" ? x : typeof x === \"number\" ? String(x) : \"\";\n\n/** A cleaned CSS value, or \"\" when the export left a malformed one. Blux emits\n * degenerate placeholders — \"\" and \"px\" for unset lengths, \"0.px\" for a zeroed\n * one — which would poison a Tailwind custom property (an invalid `var()` value\n * collapses the whole declaration). Those are always single tokens, so the\n * numeric-prefix guard runs only on a lone length; multi-value shorthands\n * (\"10px 40px 10px 40px\"), colors, \"0\", and keywords all pass through. */\nexport function cleanCssValue(x: unknown): string {\n const s = str(x).trim();\n if (s === \"\" || s === \"px\") return \"\";\n if (!/\\s/.test(s) && /px$/.test(s) && !/^-?(\\d+(\\.\\d+)?|\\.\\d+)px$/.test(s)) return \"\";\n return s;\n}\n\n/** Per-element inline style overrides on a _title/_body element (color,\n * font-size, margin, …) minus the `class` token, cleaned. undefined when none\n * survive — e.g. a hero title's `{ class: \"text0\", color: \"#fff\" }` white\n * override that a role reference alone would lose. */\nfunction inlineStyle(style: BluxTextStyle | undefined): Record<string, string> | undefined {\n if (typeof style !== \"object\" || style === null) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(style)) {\n if (k === \"class\") continue;\n const cleaned = cleanCssValue(v);\n if (cleaned) out[k] = cleaned;\n }\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Parse `settings.fonts.google` (\"Scope+One:regular|Montserrat:300,500,regular\")\n * into families + numeric weights (\"regular\" → \"400\"). */\nfunction parseGoogleFonts(google: string): FontLoad[] {\n if (!google.trim()) return [];\n return google\n .split(\"|\")\n .map((spec) => {\n const [rawFamily = \"\", rawWeights = \"\"] = spec.split(\":\");\n const family = rawFamily.replace(/\\+/g, \" \").trim();\n const weights = rawWeights\n .split(\",\")\n .map((w) => w.trim())\n .filter(Boolean)\n .map((w) => (w === \"regular\" ? \"400\" : w));\n return { family, weights: weights.length ? weights : [\"400\"] };\n })\n .filter((f) => f.family);\n}\n\n/** The real font-family for a text style. A Typekit `font-ident`\n * (`T:Family:variant:obfuscated`) carries the true family in segment 2 while\n * `font-family` holds the obfuscated id (e.g. `ysxc`); Google idents (`G:…`) and\n * missing idents already have the true family in `font-family`. */\nfunction fontFamilyFromStyle(m: Record<string, unknown>): string {\n const ident = str(m[\"font-ident\"]);\n if (ident.startsWith(\"T:\")) {\n const fam = ident.split(\":\")[1]?.trim();\n if (fam) return fam;\n }\n return str(m[\"font-family\"]).replace(/['\"]/g, \"\");\n}\n\n/** Typekit fonts to preload, parsed from the comma-separated `settings.fonts.string`\n * idents (`T:Montserrat:n6:ysxc`). `fonts.google` omits Typekit faces, so the page's\n * Montserrat 600 would otherwise never be requested. Variant `nN`/`iN` → weight N×100. */\nfunction typekitFontLoads(fontString: string): FontLoad[] {\n const byFamily = new Map<string, string[]>();\n for (const ident of fontString.split(\",\").map((s) => s.trim())) {\n if (!ident.startsWith(\"T:\")) continue;\n const [, family = \"\", variant = \"\"] = ident.split(\":\");\n const fam = family.trim();\n if (!fam) continue;\n const v = variant.trim();\n const digit = /^[ni](\\d)$/.exec(v);\n const weight = digit ? `${Number(digit[1]) * 100}` : v === \"regular\" ? \"400\" : v;\n if (!weight) continue;\n const ws = byFamily.get(fam) ?? [];\n if (!ws.includes(weight)) ws.push(weight);\n byFamily.set(fam, ws);\n }\n return [...byFamily].map(([family, weights]) => ({ family, weights }));\n}\n\n/** Union `extra` font-loads into `base`, preserving `base` order and folding new\n * weights into an existing family (so Montserrat gains 600 instead of duplicating). */\nfunction mergeFontLoads(base: FontLoad[], extra: FontLoad[]): FontLoad[] {\n const out = base.map((f) => ({ family: f.family, weights: [...f.weights] }));\n for (const e of extra) {\n const existing = out.find((f) => f.family === e.family);\n if (existing) {\n for (const w of e.weights) if (!existing.weights.includes(w)) existing.weights.push(w);\n } else {\n out.push({ family: e.family, weights: [...e.weights] });\n }\n }\n return out;\n}\n\nconst CONFIDENCE_MIN = 0.5;\n\nfunction sectionFromBlock(b: BluxBlock, pageUid: string, diagnostics: Diagnostic[]): SectionIR {\n const a = archetype(b);\n if (a.confidence < CONFIDENCE_MIN) {\n diagnostics.push({\n kind: \"low-confidence-block\",\n where: pageUid,\n message: `block mapped to ${a.sliceType} at ${a.confidence}`,\n });\n }\n const heading = visibleText(b.title, b._title);\n const body = visibleText(b.body, b._body);\n const headingRole = heading !== undefined ? textRole(b._title) : undefined;\n const bodyRole = body !== undefined ? textRole(b._body) : undefined;\n const headingStyle = heading !== undefined ? inlineStyle(b._title) : undefined;\n const bodyStyle = body !== undefined ? inlineStyle(b._body) : undefined;\n // Route every value through cleanCssValue (as inlineStyle does) so numeric\n // block styles (a JSON `\"z-index\": 10`) are kept, not silently dropped.\n const block: Record<string, string> = {};\n for (const [k, v] of Object.entries(b.styles ?? {})) {\n const cleaned = cleanCssValue(v);\n if (cleaned) block[k] = cleaned;\n }\n const presentation = {\n ...(headingRole ? { headingRole } : {}),\n ...(bodyRole ? { bodyRole } : {}),\n ...(headingStyle ? { headingStyle } : {}),\n ...(bodyStyle ? { bodyStyle } : {}),\n ...(Object.keys(block).length ? { block } : {}),\n };\n const section: SectionIR = {\n sliceType: a.sliceType,\n variation: a.variation,\n confidence: a.confidence,\n fields: {\n ...(heading !== undefined ? { heading } : {}),\n ...(body !== undefined ? { body } : {}),\n ...(b.media?.media ? { media: b.media.media } : {}),\n ...(b.backgroundMedia?.media ? { backgroundMedia: b.backgroundMedia.media } : {}),\n ...(b.ratio ? { ratio: String(b.ratio) } : {}),\n ...(b.loadEffect ? { anim: String(b.loadEffect) } : {}),\n },\n ...(Object.keys(presentation).length ? { presentation } : {}),\n };\n if (Array.isArray(b.items) && b.items.length > 0) {\n section.children = b.items.map((child) => sectionFromBlock(child, pageUid, diagnostics));\n }\n return section;\n}\n\nfunction slugify(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\") || \"page\"\n );\n}\n\nexport function normalizePages(raw: BluxRaw): { pages: PageIR[]; diagnostics: Diagnostic[] } {\n const diagnostics: Diagnostic[] = [];\n const seen = new Set<string>();\n const pages = raw.pages.map((p, i) => {\n // The routing slug: the source `url` when set, else the title slug. The\n // FIRST page is the homepage (Blux is positional) — its uid is pinned to\n // \"home\", the render's root-route contract (getByUID(\"page\", \"home\")),\n // and its path is \"\" (the export renders it at the root index.html).\n const urlSlug = String(p.url ?? \"\").trim();\n const path = i === 0 ? \"\" : urlSlug ? slugify(urlSlug) : slugify(String(p.title ?? \"\"));\n let uid = i === 0 ? \"home\" : path;\n if (seen.has(uid)) {\n // Two pages slugging identically (e.g. a title-duplicate draft with no\n // url of its own) would silently overwrite each other's documents.\n const base = uid;\n for (let n = 2; seen.has(uid); n++) uid = `${base}-${n}`;\n diagnostics.push({\n kind: \"duplicate-page-uid\",\n where: base,\n message: `page ${i} (\"${String(p.title ?? \"\")}\") slugs to \"${base}\" already used — renamed to \"${uid}\"`,\n });\n }\n seen.add(uid);\n return {\n uid,\n title: String(p.title ?? \"\"),\n description: String(p.description ?? \"\"),\n path,\n sections: (p.items ?? []).map((b) => sectionFromBlock(b, uid, diagnostics)),\n };\n });\n return { pages, diagnostics };\n}\n\nexport function normalizeTheme(raw: BluxRaw): ThemeIR {\n const colors = Object.entries(raw.styles.colors ?? {}).map(([role, value]) => ({\n role,\n value: String(value),\n }));\n // styles.text is a position-stable array whose slots are { _label,\n // \".textN\": { css props incl font-ident } }. A deleted style leaves a\n // { removed: true } tombstone with no \".textN\" key — the role name comes\n // from that inner key (not the array index), so tombstones drop out and\n // roles never renumber if the array is ever compacted.\n const textStyles: TextStyleIR[] = [];\n for (const v of Object.values(raw.styles.text ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.text\\d+$/.test(k));\n if (!innerKey) continue;\n const m = (entry[innerKey] ?? {}) as Record<string, unknown>;\n const transform = cleanCssValue(m[\"text-transform\"]);\n const tracking = cleanCssValue(m[\"letter-spacing\"]);\n // The style's block margin carries Blux's stack rhythm (e.g. \"10px 0\" on\n // Grid Titles / Caption Body). An explicit \"0\" matches the render default,\n // so only real values ride the IR.\n const margin = cleanCssValue(m[\"margin\"]);\n const mobileSize = cleanCssValue(m[\"__media_mobile_font-size\"]);\n const mobileLineHeight = cleanCssValue(m[\"__media_mobile_line-height\"]);\n textStyles.push({\n role: innerKey.slice(1), // \".text11\" -> \"text11\"\n label: str(entry._label),\n fontFamily: fontFamilyFromStyle(m),\n size: cleanCssValue(m[\"font-size\"]) || \"16px\",\n weight:\n typeof m[\"font-weight\"] === \"number\" ? m[\"font-weight\"] : str(m[\"font-weight\"]) || 400,\n lineHeight: cleanCssValue(m[\"line-height\"]) || \"1.5\",\n ...(transform && transform !== \"none\" ? { transform } : {}),\n ...(tracking ? { letterSpacing: tracking } : {}),\n ...(margin && margin !== \"0\" ? { margin } : {}),\n ...(mobileSize ? { mobileSize } : {}),\n ...(mobileLineHeight ? { mobileLineHeight } : {}),\n });\n }\n // Button skins: styles.buttons mirrors the text-styles shape (an entry per\n // role with the values one level down under the \".buttonsN\" key, tombstones\n // as { removed: true }). Values pass through in DECLARATION ORDER — the\n // skins rely on it (a `border` shorthand then `border-top/right/left: 0`\n // overrides nets a bottom-only rule) — dropping empties and the internal\n // `font-ident` marker.\n const cleanCssMap = (v: unknown): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const [k, val] of Object.entries((v ?? {}) as Record<string, unknown>)) {\n if (k === \"font-ident\") continue;\n const s = cleanCssValue(typeof val === \"number\" ? String(val) : val);\n if (s) out[k] = s;\n }\n return out;\n };\n const buttonStyles: ButtonStyleIR[] = [];\n for (const v of Object.values(raw.styles.buttons ?? {})) {\n const entry = (v ?? {}) as Record<string, unknown>;\n const innerKey = Object.keys(entry).find((k) => /^\\.buttons\\d+$/.test(k));\n if (!innerKey) continue;\n const css = cleanCssMap(entry[innerKey]);\n if (Object.keys(css).length === 0) continue;\n const hover = cleanCssMap(entry[`${innerKey}:hover`]);\n const active = cleanCssMap(entry[`${innerKey}:active`]);\n buttonStyles.push({\n role: innerKey.slice(1), // \".buttons2\" -> \"buttons2\"\n label: str(entry._label),\n css,\n ...(Object.keys(hover).length ? { hover } : {}),\n ...(Object.keys(active).length ? { active } : {}),\n });\n }\n // Fonts: explicit settings win; otherwise Blux's own default roles —\n // text0 \"Title (Default)\" and text1 \"Body (Default)\".\n const fonts = (raw.settings.fonts ?? {}) as Record<string, unknown>;\n const roleFont = (r: string) => textStyles.find((t) => t.role === r)?.fontFamily ?? \"\";\n return {\n colors,\n fonts: {\n heading: str(fonts.heading) || roleFont(\"text0\"),\n body: str(fonts.body) || roleFont(\"text1\"),\n },\n fontLoad: mergeFontLoads(\n parseGoogleFonts(str(fonts.google)),\n typekitFontLoads(str(fonts.string)),\n ),\n textStyles,\n buttonStyles,\n };\n}\n","import type { BluxFeed, BluxRaw } from \"./parse.js\";\nimport type { CollectionIR, FieldDef, RecordIR } from \"./ir.js\";\n\nfunction singularSlug(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n return slug.replace(/s$/, \"\") || \"item\";\n}\n\nconst RICHTEXT_KEYS = new Set([\"body\", \"description\"]);\n\nfunction fieldType(key: string, value: unknown): FieldDef[\"type\"] {\n if (RICHTEXT_KEYS.has(key)) return \"richtext\";\n if (value && typeof value === \"object\" && \"media\" in (value as object)) return \"image\";\n if (Array.isArray(value)) return \"group\";\n if (typeof value === \"boolean\") return \"boolean\";\n if (typeof value === \"number\") return \"number\";\n if (key === \"date\") return \"date\";\n if (/^(url|link)/.test(key)) return \"link\";\n return \"text\";\n}\n\n/** Underscore-prefixed keys are per-element style config (same convention as\n * page blocks' _title/_body), not content — never model or migrate them. */\nconst isStyleKey = (key: string) => key.startsWith(\"_\");\n\nfunction deriveFields(feed: BluxFeed): FieldDef[] {\n const seen = new Map<string, FieldDef[\"type\"]>();\n // Declared custom fields first (Blux feed.fields), then observed item keys.\n for (const d of feed.fields ?? []) {\n if (d.field) seen.set(d.field, \"text\");\n }\n for (const item of feed.items ?? []) {\n for (const [key, value] of Object.entries(item)) {\n if (isStyleKey(key)) continue;\n if (!seen.has(key) || seen.get(key) === \"text\") seen.set(key, fieldType(key, value));\n }\n }\n return [...seen.entries()].map(([key, type]) => ({ key, type }));\n}\n\nfunction recordUid(values: Record<string, unknown>, i: number): string {\n const title = typeof values.title === \"string\" ? values.title : \"\";\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return slug || `item-${i}`;\n}\n\nexport function modelCollections(raw: BluxRaw): CollectionIR[] {\n const out: CollectionIR[] = [];\n for (const feed of Object.values(raw.feeds)) {\n const label = String(feed.name ?? \"\");\n const items = feed.items ?? [];\n const records: RecordIR[] = items.map((item, i) => {\n const mediaRefs: string[] = [];\n for (const value of Object.values(item)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n mediaRefs.push((value as { media: string }).media);\n }\n }\n const values = Object.fromEntries(Object.entries(item).filter(([key]) => !isStyleKey(key)));\n return { uid: recordUid(item, i), values, mediaRefs };\n });\n out.push({\n apiId: singularSlug(label),\n label,\n publishRoute: feed.publish ? String(feed.publish) : null,\n fields: deriveFields(feed),\n records,\n });\n }\n return out;\n}\n","import { parseBluxSite } from \"./parse.js\";\nimport { normalizePages, normalizeTheme } from \"./normalize.js\";\nimport { modelCollections } from \"./collections.js\";\nimport { collectAssetUrls } from \"./assets.js\";\nimport type { AssetRef, Diagnostic, SiteIR } from \"./ir.js\";\n\nexport function assembleIR(input: { siteJson: unknown; htmls: string[] }): SiteIR {\n const raw = parseBluxSite(input.siteJson);\n const { pages, diagnostics: pageDiags } = normalizePages(raw);\n const theme = normalizeTheme(raw);\n const collections = modelCollections(raw);\n const urlMap = collectAssetUrls(input.htmls);\n\n const diagnostics: Diagnostic[] = [...pageDiags];\n const assets: AssetRef[] = Object.entries(raw.media).map(([id, m]) => {\n const sourceUrl = urlMap.get(id) ?? null;\n if (!sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: id,\n message: `no CDN url for ${m.name ?? id}`,\n });\n }\n return {\n id,\n sourceUrl,\n name: String(m.name ?? \"\"),\n mime: String(m.type ?? \"\"),\n alt: String(m.name ?? \"\"),\n };\n });\n\n // The favicon is declared only in settings (settings.favicon.media) and its\n // uuid is routinely ABSENT from the media dict — so resolve it straight from\n // the scraped urlMap (the <link rel=\"icon\"> tags are part of the HTML scrape)\n // rather than through the assets list above. It lives on meta, not in\n // `assets`, because migration-plan assets get uploaded to Prismic media —\n // the wrong destination for a favicon (convert downloads it beside the plan).\n const faviconId = raw.settings.favicon?.media;\n const favicon = faviconId\n ? { assetId: faviconId, sourceUrl: urlMap.get(faviconId) ?? null }\n : undefined;\n if (favicon && !favicon.sourceUrl) {\n diagnostics.push({\n kind: \"unresolved-asset\",\n where: favicon.assetId,\n message: `no CDN url for favicon ${favicon.assetId}`,\n });\n }\n\n return {\n meta: { ...raw.meta, ...(favicon ? { favicon } : {}) },\n theme,\n pages,\n collections,\n assets,\n diagnostics,\n };\n}\n","import type { Diagnostic, SectionIR } from \"../ir.js\";\n\nexport type RichTextMarker = { __richtext_html: string };\nexport type AssetMarker = { __asset_id: string };\nexport const richText = (html: string): RichTextMarker => ({ __richtext_html: html });\nexport const assetRef = (id: string): AssetMarker => ({ __asset_id: id });\n\nexport type PlanSlice = {\n slice_type: string;\n variation: string;\n primary: Record<string, unknown>;\n items: Record<string, unknown>[];\n};\nexport type PlanDocument = { type: string; uid: string; data: Record<string, unknown> };\nexport type PlanCustomType = { id: string; label: string; repeatable: true; json: unknown };\nexport type PlanAsset = { id: string; url: string; alt: string };\n/** Presentation hints for one emitted slice; `index` is its position in the\n * document's slice zone (after empty-slice filtering). `items` aligns with a\n * kept section_grid's items. */\nexport type SliceStyleEntry = {\n index: number;\n sliceType: string;\n presentation?: NonNullable<SectionIR[\"presentation\"]>;\n items?: (NonNullable<SectionIR[\"presentation\"]> | null)[];\n};\n\nexport type MigrationPlan = {\n customTypes: PlanCustomType[];\n documents: PlanDocument[];\n assets: PlanAsset[];\n /** Per-page presentation hints (block styles + text roles) — design-pass\n * reference only, never pushed to Prismic. */\n stylesManifest: { pageUid: string; slices: SliceStyleEntry[] }[];\n /** Plan-time findings (skipped empty pages, dropped non-image assets, …). */\n diagnostics: Diagnostic[];\n};\n","import type { CollectionIR, FieldDef } from \"../ir.js\";\nimport type { PlanCustomType } from \"./plan.js\";\n\nconst FIELD_CONFIG: Record<\n FieldDef[\"type\"],\n () => { type: string; config: Record<string, unknown> }\n> = {\n text: () => ({ type: \"Text\", config: {} }),\n richtext: () => ({\n type: \"StructuredText\",\n config: { multi: \"paragraph,strong,em,hyperlink,list-item,o-list-item\" },\n }),\n image: () => ({ type: \"Image\", config: { constraint: {}, thumbnails: [] } }),\n group: () => ({ type: \"Group\", config: { fields: { value: { type: \"Text\", config: {} } } } }),\n date: () => ({ type: \"Date\", config: {} }),\n boolean: () => ({ type: \"Boolean\", config: {} }),\n number: () => ({ type: \"Number\", config: {} }),\n link: () => ({ type: \"Link\", config: { allowTargetBlank: true } }),\n};\n\nexport function buildCustomType(c: CollectionIR): PlanCustomType {\n const Main: Record<string, unknown> = {};\n for (const f of c.fields) {\n const spec = FIELD_CONFIG[f.type]();\n Main[f.key] = { ...spec, config: { ...spec.config, label: f.key } };\n }\n return {\n id: c.apiId,\n label: c.label,\n repeatable: true,\n json: { id: c.apiId, label: c.label, repeatable: true, status: true, json: { Main } },\n };\n}\n","/** HTML-level rich-text coercion so emitted plans validate against the slice\n * models' StructuredText restrictions (heading slots are `single` and\n * heading-restricted; body slots allow no headings — see Plan 4's field-type\n * table). Blux markup is simple generated HTML, so tag rewriting is reliable. */\n\nconst BLOCK_RE = /<(h[1-6]|p|div)(\\s[^>]*)?>[\\s\\S]*?<\\/\\1>/i;\n\n/** Coerce a heading-slot HTML fragment to a single block whose tag is in\n * `allowed` (e.g. [\"h2\",\"h3\"]): keep an allowed tag, clamp other headings to\n * the nearest allowed level, promote paragraphs/bare text to the LOWEST\n * allowed heading. Only the first block survives (the fields are `single`). */\nexport function coerceHeadingHtml(html: string, allowed: string[]): string {\n const m = html.match(BLOCK_RE);\n const block = m ? m[0] : html;\n const tagMatch = block.match(/^<(h[1-6]|p|div)(\\s[^>]*)?>/i);\n const tag = tagMatch?.[1]?.toLowerCase();\n if (tag && allowed.includes(tag)) return block;\n\n const levels = allowed.filter((t) => /^h[1-6]$/.test(t)).map((t) => Number(t[1]));\n const target =\n tag && tag.startsWith(\"h\")\n ? `h${levels.reduce((b, l) => (Math.abs(l - Number(tag[1])) < Math.abs(b - Number(tag[1])) ? l : b))}`\n : `h${Math.max(...levels)}`;\n\n if (!tag) return `<${target}>${block}</${target}>`;\n return block\n .replace(new RegExp(`^<${tag}`, \"i\"), `<${target}`)\n .replace(new RegExp(`</${tag}>$`, \"i\"), `</${target}>`);\n}\n\n/** Demote all headings in a body-slot fragment to paragraphs (body fields\n * allow no heading blocks). Attributes and inline markup pass through. */\nexport function demoteHeadingsHtml(html: string): string {\n return html.replace(/<(\\/?)h[1-6](\\s[^>]*)?>/gi, \"<$1p$2>\");\n}\n","import type { SectionIR } from \"../ir.js\";\nimport { richText, assetRef, type PlanSlice } from \"./plan.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\n\n/** Allowed heading tags per slice heading slot — MUST mirror the\n * StructuredText configs in reddoor-starter/src/lib/slices/<Slice>/model.json. */\nconst HEADING_TAGS = {\n hero: [\"h1\", \"h2\"],\n media_text: [\"h2\", \"h3\"],\n section_grid: [\"h2\", \"h3\"],\n section_grid_item: [\"h3\", \"h4\"],\n} satisfies Record<string, string[]>;\n\nfunction rt(html?: string) {\n return html ? richText(html) : undefined;\n}\nfunction rtHeading(html: string | undefined, slot: keyof typeof HEADING_TAGS) {\n return html ? richText(coerceHeadingHtml(html, HEADING_TAGS[slot])) : undefined;\n}\nfunction rtBody(html?: string) {\n return html ? richText(demoteHeadingsHtml(html)) : undefined;\n}\nfunction img(id?: string) {\n return id ? assetRef(id) : undefined;\n}\nfunction compact(o: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));\n}\n\nexport function sectionToSlice(s: SectionIR): PlanSlice {\n const f = s.fields;\n switch (s.sliceType) {\n case \"hero\":\n return {\n slice_type: \"hero\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"hero\"),\n body: rtBody(f.body),\n background_image: img(f.backgroundMedia ?? f.media),\n }),\n items: [],\n };\n case \"media_text\":\n return {\n slice_type: \"media_text\",\n variation: s.variation === \"imageLeft\" ? \"imageLeft\" : \"imageRight\",\n primary: compact({\n heading: rtHeading(f.heading, \"media_text\"),\n body: rtBody(f.body),\n media: img(f.media),\n }),\n items: [],\n };\n case \"collection_list\":\n return {\n slice_type: \"collection_list\",\n variation: s.variation === \"list\" ? \"list\" : \"grid\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n collection_type: s.collectionRef?.apiId ?? \"\",\n max_items: 24,\n }),\n items: [],\n };\n case \"grid\":\n case \"slider\":\n return {\n slice_type: \"section_grid\",\n variation: \"default\",\n primary: compact({\n heading: rtHeading(f.heading, \"section_grid\"),\n columns: f.columns ?? 3,\n }),\n items: (s.children ?? [])\n .map((c) =>\n compact({\n item_heading: rtHeading(c.fields.heading, \"section_grid_item\"),\n item_body: rtBody(c.fields.body),\n item_media: img(c.fields.media),\n }),\n )\n // a child whose only content was hidden text has nothing to show\n .filter((item) => Object.keys(item).length > 0),\n };\n case \"rich_text\":\n default:\n // rich_text content allows every block type — no coercion needed\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n primary: compact({ content: rt(f.heading ? `${f.heading}${f.body ?? \"\"}` : f.body) }),\n items: [],\n };\n }\n}\n","import type { SectionIR } from \"../ir.js\";\n\nconst isContainer = (s: SectionIR) => s.sliceType === \"grid\" || s.sliceType === \"slider\";\nconst isFlatLeaf = (s: SectionIR) =>\n (s.sliceType === \"media_text\" || s.sliceType === \"rich_text\") && !(s.children ?? []).length;\n\n/** Depth-first flatten: Prismic slices cannot nest, so a container survives\n * as a section_grid-with-items only when every child is representable as a\n * flat item (childless media_text/rich_text). Anything richer — nested\n * containers, heroes with backgrounds — explodes into sequential sibling\n * sections, and the container's OWN content (heading/body/media) survives\n * as a leading media_text or rich_text section. Proven need: thePointe's\n * depth-4 tree kept only 7/53 images under one-level flattening. */\nexport function flattenSections(sections: SectionIR[]): SectionIR[] {\n const out: SectionIR[] = [];\n for (const s of sections) {\n const children = s.children ?? [];\n\n if (s.sliceType === \"hero\" && s.fields.media && s.fields.backgroundMedia) {\n // the hero slice only models the background image — surface the\n // foreground image (a logo/overlay in Blux) as a sibling media_text\n const { media, ...heroFields } = s.fields;\n const { children: _heroKids, ...heroSelf } = s;\n out.push({ ...heroSelf, fields: heroFields });\n out.push({\n sliceType: \"media_text\",\n variation: \"imageRight\",\n confidence: s.confidence,\n fields: { media },\n });\n out.push(...flattenSections(children));\n continue;\n }\n\n // a container may keep its items form only when it has no content of its\n // own beyond a heading — section_grid's primary cannot carry media/body\n const keepable = isContainer(s) && !s.fields.media && !s.fields.body;\n if (!children.length) {\n if (isContainer(s) && !keepable) {\n out.push({ ...s, sliceType: s.fields.media ? \"media_text\" : \"rich_text\" });\n } else {\n out.push(s);\n }\n } else if (keepable && children.every(isFlatLeaf)) {\n out.push(s);\n } else if (isContainer(s)) {\n // the container's own content leads its exploded children: with media\n // it is a media_text-shaped section that merely carries a subtree,\n // otherwise its heading/body become a rich_text grouping label\n const { children: _kids, ...self } = s;\n if (self.fields.media) {\n out.push({ ...self, sliceType: \"media_text\" });\n } else if (self.fields.heading || self.fields.body) {\n out.push({ ...self, sliceType: \"rich_text\" });\n }\n out.push(...flattenSections(children));\n } else {\n // normalize attaches `children` to ANY block with items — a hero or\n // media_text can carry a subtree. Its own slice mapping ignores\n // children, so hoist them as following siblings instead of losing them.\n const { children: _hoisted, ...self } = s;\n out.push(self);\n out.push(...flattenSections(children));\n }\n }\n return out;\n}\n","import type { SiteIR, RecordIR, PageIR, AssetRef, Diagnostic } from \"../ir.js\";\nimport {\n richText,\n assetRef,\n type MigrationPlan,\n type PlanDocument,\n type SliceStyleEntry,\n} from \"./plan.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sectionToSlice } from \"./slices.js\";\nimport { coerceHeadingHtml, demoteHeadingsHtml } from \"./coerce-html.js\";\nimport { flattenSections } from \"./flatten.js\";\n\n/** Slice fields modeled as Prismic Image fields — only image assets may land here. */\nconst IMAGE_FIELDS = new Set([\"background_image\", \"media\", \"item_media\"]);\n\nconst RICHTEXT = new Set([\"body\", \"description\"]);\n\n/** A page with no title and no sections emits no document (and no review pair). */\nexport function isEmptyPage(p: PageIR): boolean {\n return !p.sections.length && !p.title.trim();\n}\n\n/** True when the asset may occupy a Prismic Image field. A known non-image\n * mime is disqualifying; a MISSING mime (exports often omit `type`) falls\n * back to the filename extension and keeps the asset unless it is clearly\n * not an image. */\nconst NON_IMAGE_EXT = /\\.(mp4|mov|webm|avi|mp3|wav|pdf|zip)$/i;\nfunction isImageAsset(a: AssetRef | undefined): boolean {\n if (!a) return true; // unknown asset id: leave it; migrate reports the miss\n if (a.mime) return a.mime.startsWith(\"image/\");\n return !NON_IMAGE_EXT.test(a.name);\n}\n\nfunction recordData(rec: RecordIR): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rec.values)) {\n if (\n value &&\n typeof value === \"object\" &&\n typeof (value as { media?: string }).media === \"string\"\n ) {\n data[key] = assetRef((value as { media: string }).media);\n } else if (RICHTEXT.has(key) && typeof value === \"string\") {\n // record rich-text models allow no heading blocks (see buildCustomType)\n data[key] = richText(demoteHeadingsHtml(value));\n } else {\n data[key] = value;\n }\n }\n return data;\n}\n\nexport function buildMigrationPlan(ir: SiteIR): MigrationPlan {\n const diagnostics: Diagnostic[] = [];\n const customTypes = ir.collections.map(buildCustomType);\n const assetById = new Map(ir.assets.map((a) => [a.id, a]));\n\n /** Drop non-image assets referenced by an Image field, with a diagnostic. */\n const dropNonImages = (rec: Record<string, unknown>, keys: Set<string> | null, where: string) => {\n for (const [key, val] of Object.entries(rec)) {\n if ((keys && !keys.has(key)) || !val || typeof val !== \"object\" || !(\"__asset_id\" in val))\n continue;\n const asset = assetById.get((val as { __asset_id: string }).__asset_id);\n if (!isImageAsset(asset)) {\n diagnostics.push({\n kind: \"non-image-in-image-field\",\n where: `${where}.${key}`,\n message: `${asset?.mime || \"unknown mime\"} asset dropped from image field`,\n });\n delete rec[key];\n }\n }\n };\n\n const documents: PlanDocument[] = [];\n const stylesManifest: MigrationPlan[\"stylesManifest\"] = [];\n for (const page of ir.pages) {\n if (isEmptyPage(page)) {\n diagnostics.push({\n kind: \"empty-page\",\n where: page.uid,\n message: \"page has no title and no sections; skipped\",\n });\n continue;\n }\n const slices: ReturnType<typeof sectionToSlice>[] = [];\n const styleEntries: SliceStyleEntry[] = [];\n for (const section of flattenSections(page.sections)) {\n const slice = sectionToSlice(section);\n for (const rec of [slice.primary, ...slice.items]) {\n dropNonImages(rec, IMAGE_FIELDS, `${page.uid}/${slice.slice_type}`);\n }\n // Structural defaults aren't content — a slice with nothing else to\n // show (e.g. a block whose only content was hidden text, or whose\n // sole video was dropped from an image field) is invisible; skip it.\n const STRUCTURAL = new Set([\"columns\", \"collection_type\", \"max_items\"]);\n const hasContent =\n Object.keys(slice.primary).some((k) => !STRUCTURAL.has(k)) || slice.items.length > 0;\n if (!hasContent) {\n diagnostics.push({\n kind: \"empty-slice\",\n where: `${page.uid}/${slice.slice_type}`,\n message: \"slice has no content after filtering; dropped\",\n });\n continue;\n }\n // Presentation hints ride beside the slice, keyed by its final index.\n // Grid items mirror sectionToSlice's emptiness rule so they stay aligned.\n const itemPresentations = (section.children ?? [])\n .filter((c) => c.fields.heading || c.fields.body || c.fields.media)\n .map((c) => c.presentation ?? null);\n const entry: SliceStyleEntry = {\n index: slices.length,\n sliceType: slice.slice_type,\n ...(section.presentation ? { presentation: section.presentation } : {}),\n ...(slice.slice_type === \"section_grid\" && itemPresentations.some(Boolean)\n ? { items: itemPresentations }\n : {}),\n };\n if (entry.presentation || entry.items) styleEntries.push(entry);\n slices.push(slice);\n }\n stylesManifest.push({ pageUid: page.uid, slices: styleEntries });\n documents.push({\n type: \"page\",\n uid: page.uid,\n // the page type's title is StructuredText(single heading1)\n data: {\n title: richText(coerceHeadingHtml(page.title || page.uid, [\"h1\"])),\n slices,\n },\n });\n }\n for (const c of ir.collections) {\n for (const rec of c.records) {\n const data = recordData(rec);\n // record asset markers only ever occupy Image fields — check them all\n dropNonImages(data, null, `${c.apiId}/${rec.uid}`);\n documents.push({ type: c.apiId, uid: rec.uid, data });\n }\n }\n\n const assets = ir.assets\n .filter((a) => a.sourceUrl !== null)\n .map((a) => ({ id: a.id, url: a.sourceUrl as string, alt: a.alt }));\n\n return { customTypes, documents, assets, stylesManifest, diagnostics };\n}\n","import type { ThemeIR } from \"../ir.js\";\n\n/** ThemeIR → a Tailwind v4 `@theme` block of CSS custom properties. Deterministic.\n * A leading comment lists the exact web-font weights the export loads, so the\n * design pass can install them without measuring the rendered site. */\nexport function emitThemeCss(theme: ThemeIR): string {\n const lines: string[] = [];\n if (theme.fontLoad.length) {\n const spec = theme.fontLoad.map((f) => `${f.family} ${f.weights.join(\",\")}`).join(\"; \");\n lines.push(`/* Fonts to load — ${spec} */`);\n }\n lines.push(\"@theme {\");\n for (const c of theme.colors) lines.push(` --color-${c.role}: ${c.value};`);\n lines.push(` --font-heading: ${theme.fonts.heading || \"sans-serif\"};`);\n lines.push(` --font-body: ${theme.fonts.body || \"sans-serif\"};`);\n for (const t of theme.textStyles) {\n if (t.label) lines.push(` /* ${t.role} — ${t.label} */`);\n lines.push(` --text-${t.role}: ${t.size};`);\n lines.push(` --text-${t.role}--line-height: ${t.lineHeight};`);\n lines.push(` --text-${t.role}--font-weight: ${t.weight};`);\n if (t.fontFamily) lines.push(` --text-${t.role}--font-family: ${t.fontFamily};`);\n if (t.transform) lines.push(` --text-${t.role}--text-transform: ${t.transform};`);\n if (t.letterSpacing) lines.push(` --text-${t.role}--letter-spacing: ${t.letterSpacing};`);\n if (t.margin) lines.push(` --text-${t.role}--margin: ${t.margin};`);\n if (t.mobileSize) lines.push(` --text-${t.role}--mobile-font-size: ${t.mobileSize};`);\n if (t.mobileLineHeight)\n lines.push(` --text-${t.role}--mobile-line-height: ${t.mobileLineHeight};`);\n }\n lines.push(\"}\");\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the `.txt-role-textN` utility layer: one rule per text style that\n * maps the role's `@theme` vars (emitted by {@link emitThemeCss}) onto the\n * h1–h6/p it wraps. A slice applies `txt-role-textN` to a block and its\n * headings/paragraphs pick up that role's type — the same mechanism the\n * per-site `gen-blux-theme.mjs` script used to hand-generate, now owned by\n * `blux emit` so a converted site consumes it with zero hand-tuning.\n *\n * letter-spacing and text-transform fall back through `var(…, default)` so a\n * role that omits them is inert for that property. font-family is set only when\n * the role declares one — a family-less role (a body default) keeps the natural\n * cascade (headings stay heading font, paragraphs stay body font) rather than\n * being forced onto either. margin falls back to 0: Blux's vertical rhythm\n * between stacked blocks is the text styles' own margins (e.g. \"10px 0\" on\n * Grid Titles), which collapse in normal flow — a role without one stays\n * flush, exactly like the original. Returns \"\" when the theme has no text\n * styles. */\nexport function emitRolesCss(theme: ThemeIR): string {\n if (!theme.textStyles.length) return \"\";\n const lines: string[] = [\n \"/* Role utilities — one .txt-role-textN per Blux text style, mapping its\",\n \" @theme vars onto the heading/paragraph it wraps. Generated by blux emit. */\",\n ];\n for (const t of theme.textStyles) {\n const r = t.role;\n lines.push(`.txt-role-${r} :is(h1, h2, h3, h4, h5, h6, p) {`);\n if (t.fontFamily) lines.push(` font-family: var(--text-${r}--font-family);`);\n lines.push(\n ` font-size: var(--text-${r});`,\n ` font-weight: var(--text-${r}--font-weight);`,\n ` line-height: var(--text-${r}--line-height);`,\n ` letter-spacing: var(--text-${r}--letter-spacing, normal);`,\n ` text-transform: var(--text-${r}--text-transform, none);`,\n ` margin: var(--text-${r}--margin, 0);`,\n `}`,\n );\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/** ThemeIR → the button-skin layer: one `.buttonsN` rule per declared Blux\n * button style (plus its :hover/:active variants), and the `.ib` inline-block\n * base the raw anchors rely on (`class=\"ib middle buttonsN\"` — an inline `a`\n * would ignore the skin's vertical padding). Properties emit in the export's\n * own declaration order: the skins rely on it (a `border` shorthand followed\n * by `border-top/right/left: 0` overrides nets a bottom-only rule). Returns \"\"\n * when the theme declares no button styles. */\nexport function emitButtonsCss(theme: ThemeIR): string {\n if (!theme.buttonStyles.length) return \"\";\n const lines: string[] = [\n \"/* Button skins — one .buttonsN per Blux button style; raw anchors carry\",\n \" `ib middle buttonsN` verbatim. Generated by blux emit. */\",\n \".ib {\",\n \" display: inline-block;\",\n \"}\",\n \"/* Text links (`ib middle links`) are underlined by the Blux platform CSS;\",\n \" an inline-block box does not inherit an ancestor's text-decoration, so\",\n \" the affordance must be declared on the anchor itself. */\",\n \".links {\",\n \" text-decoration: underline;\",\n \"}\",\n ];\n const rule = (selector: string, css: Record<string, string>) => {\n lines.push(`${selector} {`);\n for (const [k, v] of Object.entries(css)) lines.push(` ${k}: ${v};`);\n lines.push(\"}\");\n };\n for (const b of theme.buttonStyles) {\n if (b.label) lines.push(`/* ${b.role} — ${b.label} */`);\n rule(`.${b.role}`, b.css);\n if (b.hover) rule(`.${b.role}:hover`, b.hover);\n if (b.active) rule(`.${b.role}:active`, b.active);\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import type { SiteIR, Diagnostic } from \"../ir.js\";\nimport { isEmptyPage } from \"./migration-plan.js\";\n\nexport type ReviewPair = { uid: string; converted: string; original: string };\nexport type ReviewManifest = { pairs: ReviewPair[]; diagnostics: Diagnostic[] };\n\nexport function buildReviewManifest(\n ir: SiteIR,\n opts: { convertedBase: string; bluxBase: string },\n): ReviewManifest {\n // empty pages emit no document (see buildMigrationPlan) — nothing to review\n const pairs = ir.pages\n .filter((p) => !isEmptyPage(p))\n .map((p) => ({\n uid: p.uid,\n converted: `${opts.convertedBase}/${p.uid}`,\n // The home page maps to the site root; other pages to /uid.\n original: p.uid === \"home\" ? `${opts.bluxBase}/` : `${opts.bluxBase}/${p.uid}`,\n }));\n return { pairs, diagnostics: ir.diagnostics };\n}\n","/** Content-coverage validation: does a converted site actually render every\n * piece of text the Blux export shows? The export's rendered `index.html` is\n * the answer key; we extract its visible text runs and check each appears in\n * the converted site's rendered HTML. A missing run is a content gap the\n * deterministic transform left behind (e.g. a hero title that never mapped\n * onto a slice field) — surfaced without spending a token eyeballing pages. */\n\nconst DROP_ELEMENTS = /<(script|style|head|noscript|svg|template)[\\s\\S]*?<\\/\\1>/gi;\nconst COMMENTS = /<!--[\\s\\S]*?-->/g;\n\n/** Decode the entity forms an export mixes in so they match the raw characters\n * a Prismic render emits. Numeric first, then the common named ones. */\nfunction decodeEntities(s: string): string {\n return (\n s\n .replace(/&#(\\d+);/g, (_, n) => codePoint(Number(n)))\n .replace(/&#x([0-9a-f]+);/gi, (_, h) => codePoint(parseInt(h, 16)))\n .replace(/ /gi, \" \")\n .replace(/&(?:amp);/gi, \"&\")\n .replace(/&(?:lt);/gi, \"<\")\n .replace(/&(?:gt);/gi, \">\")\n .replace(/&(?:quot);/gi, '\"')\n .replace(/&(?:apos|#39);/gi, \"'\")\n // any remaining named entity (— ’ © …) becomes a space,\n // not the literal token \"mdash\"/\"rsquo\" — a phantom word would break the\n // substring match and report otherwise-present text as a false gap\n .replace(/&[a-z][a-z0-9]*;/gi, \" \")\n );\n}\n\nfunction codePoint(n: number): string {\n try {\n return String.fromCodePoint(n);\n } catch {\n return \" \";\n }\n}\n\n/** Fold text to a matchable form: decode, lowercase, and reduce every run of\n * non-alphanumeric characters (punctuation, smart quotes, em-dashes, symbols)\n * to a single space. Coverage then compares words, not typography — so\n * \"CBRE & Co. — Leasing\" and \"cbre co leasing\" are the same content. */\nexport function normalizeText(s: string): string {\n return decodeEntities(s)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim();\n}\n\n/** A normalized run counts as content only if it carries a real word — at least\n * three alphanumeric characters together. Filters whitespace, lone punctuation,\n * and single/double-letter fragments left by inline tags. */\nfunction hasWord(run: string): boolean {\n return /[a-z0-9]{3,}/.test(run);\n}\n\n/** Strip the non-content elements and all tags from an HTML string, leaving\n * one normalized text blob (used for the render side of a coverage check). */\nexport function flattenText(html: string): string {\n const text = html\n .replace(DROP_ELEMENTS, \" \")\n .replace(COMMENTS, \" \")\n .replace(/<[^>]+>/g, \" \");\n return normalizeText(text);\n}\n\n/** Split an HTML string into de-duplicated, normalized visible text runs — the\n * text between tags, once script/style/head are removed. Inline tags fragment a\n * sentence into several runs; each must still appear in the render to count. */\nexport function extractTextRuns(html: string): string[] {\n const stripped = html.replace(DROP_ELEMENTS, \" \").replace(COMMENTS, \" \");\n const seen = new Set<string>();\n for (const piece of stripped.split(/<[^>]+>/)) {\n const run = normalizeText(piece);\n if (hasWord(run)) seen.add(run);\n }\n return [...seen];\n}\n\nexport type CoverageReport = {\n total: number;\n covered: number;\n missing: string[];\n coveragePct: number;\n};\n\n/** Compare the export's rendered HTML (answer key) against the converted site's\n * rendered HTML. Every text run in the export should appear somewhere in the\n * render; the ones that don't are the content the transform dropped. */\nexport function validateCoverage(exportHtml: string, renderedHtml: string): CoverageReport {\n const runs = extractTextRuns(exportHtml);\n // Pad with spaces and match a space-bounded run so a short label (\"art\")\n // isn't counted as covered because it sits inside a longer word\n // (\"apartments\") — that would silently hide a real content gap.\n const blob = ` ${flattenText(renderedHtml)} `;\n const missing = runs.filter((run) => !blob.includes(` ${run} `));\n const covered = runs.length - missing.length;\n const coveragePct = runs.length === 0 ? 100 : Math.round((covered / runs.length) * 100);\n return { total: runs.length, covered, missing, coveragePct };\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement, Node as HTMLNode } from \"node-html-parser\";\nimport type { Band, Cell, GridToken, Media, Node } from \"./types.js\";\nimport { parseGridToken } from \"./token.js\";\nimport {\n textRoleFromClass,\n headingLevel,\n mediaFromElement,\n stripAssetExt,\n blockPlainText,\n readBgSizing,\n textLeafStyle,\n cssProp,\n} from \"./leaf.js\";\n\nconst DEFAULT_TOKEN: GridToken = { cols: 1, raw: \"grid-1\" };\n\nconst isElement = (n: HTMLNode): n is HTMLElement =>\n (n as HTMLElement).tagName !== undefined && (n as HTMLElement).tagName !== null;\n\nconst hasClass = (el: HTMLElement, c: string) => el.classNames.split(/\\s+/).includes(c);\n\n/** A hidden feed-template prototype: the `display:none` `cagriditem` (id\n * `…-template`) that Blux clones client-side, once per feed record, to render a\n * feed grid at runtime. Its content is Handlebars tokens (`{{media}}`,\n * `{{title}}`) — never real content — so it must be dropped from the static\n * parse; keeping it renders the literal `{{…}}` text and an empty tile. The\n * visible feed tiles are materialized separately (from the feed records), not\n * from this element. */\nconst isFeedTemplate = (el: HTMLElement): boolean => {\n const id = el.getAttribute(\"id\") ?? \"\";\n const style = el.getAttribute(\"style\") ?? \"\";\n const hidden = /display\\s*:\\s*none/i.test(style);\n return hidden && (/-template$/.test(id) || /\\{\\{[a-z_]+\\}\\}/i.test(el.innerHTML));\n};\n\n/** A foreground media holder: a `block-media-holder`, a bare\n * `camediaload[data-media]`, or a `<video>`. */\nconst isMediaHolder = (el: HTMLElement): boolean =>\n hasClass(el, \"block-media-holder\") ||\n (hasClass(el, \"camediaload\") && !!el.getAttribute(\"data-media\")) ||\n el.tagName === \"VIDEO\";\n\nconst isLeafElement = (el: HTMLElement): boolean =>\n hasClass(el, \"block-title\") ||\n hasClass(el, \"block-body\") ||\n hasClass(el, \"block-subtitle\") ||\n isMediaHolder(el);\n\n/** A leaf `<a>`: a CTA button / text link with no structural descendants. Such\n * an anchor is not a wrapper — peeling through it (its inner text/spans are not\n * structural) would drop the link entirely — so it is treated as a structural\n * leaf. A linked media/grid wrapper (`<a>` containing a camediaload/grid) is NOT\n * a leaf anchor: it keeps peeling so the inner media parses normally. */\nconst isLeafAnchor = (el: HTMLElement): boolean =>\n el.tagName === \"A\" && collectStructuralChildren(el).length === 0;\n\n/** Is this element a structural boundary (a leaf, a grid row, or a token-bearing\n * cell/holder), as opposed to a pure wrapper div we should peel through? */\nconst isStructural = (el: HTMLElement): boolean =>\n isLeafElement(el) ||\n el.hasAttribute(\"data-exec\") || // Blux custom-code embed (e.g. map mount)\n hasClass(el, \"cagrid\") ||\n isLeafAnchor(el) ||\n parseGridToken(el.classNames) !== null;\n\n/** The \"card\" styling inherited from the wrapper div(s) peeled to reach a\n * structural child — a Blux card's inline `background-color` (on the `.blocksN`\n * fill) and the `padding` its `.blocksNcontainer` insets the content by, both of\n * which the plain peel drops. The nearest wrapper wins for each. `nested` marks\n * that the walk has passed a grid-cell boundary (block-grid-container /\n * block-subcontent): only cell-level container padding is captured — the\n * BAND-level container's padding is the band's own content padding, already\n * handled via the band style/blockClass defaults, and capturing it here would\n * inset the content twice. */\ntype CardStyle = {\n background?: string;\n padding?: string;\n /** A nested block-in-cell's inline `min-height` (e.g. the-tower band 1's\n * 80vh gradient panel): the cell's block pins its own box height, which the\n * plain peel would drop — the cell then renders at content height. Captured\n * only inside a cell (like padding): a band-level container's min-height is\n * the band's full-height chrome, handled at the band level. */\n minHeight?: string;\n /** A `block-background-layer` sibling's paint — the abs-fill div a nested\n * block uses for gradient (or plain) fills that `inlineBg` never sees\n * (they're not a wrapper `background-color`). Emitted as the `background`\n * shorthand so gradients survive. Image layers (camediaload) stay out of\n * scope — those are band-background territory. */\n layerBackground?: string;\n /** A peeled `valignmiddle` wrapper: the original vertically centers this\n * cell's content against its row siblings (band 6/12's side captions sit\n * centered on their photos; band 3's stats card centers in its column) —\n * and, paired with `minHeight`, centers content within the block's own box.\n * Rides the node style as the `_valign` presentation hint. */\n valign?: boolean;\n /** The cell belongs to a `cagridFlexHeight` grid: the original stretches\n * each cell's direct block to the full row height (`.cagriditem>div\n * {height:100%}`), so a painted block fills its whole column — not just its\n * content box. Rides painted container nodes as the `_fill: column` hint. */\n fill?: boolean;\n nested?: boolean;\n};\n/** A structural child plus any card styling peeled off its wrapper(s). */\ntype StructuralChild = { el: HTMLElement; card?: CardStyle };\n\n/** A grid-cell boundary: once the peel passes one of these, wrappers below are\n * cell-level (their padding is real content inset, not band chrome). */\nconst isCellBoundary = (el: HTMLElement): boolean =>\n hasClass(el, \"block-grid-container\") || hasClass(el, \"block-subcontent\");\n\n/** Inline `background-color` off an element's style attribute, ignoring the\n * transparent default (which is not a deviation worth carrying). */\nfunction inlineBg(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"background-color\")?.trim();\n if (!c) return undefined;\n const low = c.toLowerCase().replace(/\\s+/g, \"\");\n if (low === \"transparent\" || low === \"rgba(0,0,0,0)\") return undefined;\n return c;\n}\n\n/** Inline `padding` shorthand off an element's style attribute, ignoring an\n * all-zero value (no inset worth carrying). */\nfunction inlinePadding(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"padding\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem)?\\s*)+$/i.test(c)) return undefined;\n return c;\n}\n\n/** Inline `min-height` off an element's style attribute, ignoring zero/auto\n * (no sizing worth carrying). Blux repeats the value on the item AND its\n * inner `height: 1px` container — same value, either capture works. */\nfunction inlineMinHeight(el: HTMLElement): string | undefined {\n const c = cssProp(el.getAttribute(\"style\") ?? \"\", \"min-height\")?.trim();\n if (!c) return undefined;\n if (/^(0(px|%|em|rem|vh|vw)?|auto)$/i.test(c)) return undefined;\n return c;\n}\n\n/** The paint of a `block-background-layer` child, if `el` has one: the\n * abs-fill div that fills a nested block-in-cell (gradient or plain color —\n * carried in the `background` shorthand, which `inlineBg`'s background-color\n * read never sees). `none`/`transparent` carry no paint. */\nfunction layerBackground(el: HTMLElement): string | undefined {\n for (const child of el.childNodes) {\n if (!isElement(child) || !hasClass(child, \"block-background-layer\")) continue;\n const s = child.getAttribute(\"style\") ?? \"\";\n const bg = (cssProp(s, \"background\") ?? cssProp(s, \"background-color\"))?.trim();\n if (bg && !/^(none|transparent)$/i.test(bg)) return bg;\n }\n return undefined;\n}\n\n/** The child elements that carry structure, peeling pure wrapper divs. A peeled\n * card wrapper's inline background-color and (cell-level) content padding ride\n * along to the structural node it wraps (the nearest wrapper wins for each) so\n * a card's fill and inset survive the peel.\n *\n * Two wrapper shapes are PROMOTED to structural instead of peeled, so they\n * parse to their own stack and the styling/containment attaches exactly once:\n * - a multi-child `block-subcontent`: a Blux grid CELL groups its blocks —\n * the original contains their margins per cell (a block-content clearfix\n * blocks the collapse), so flattening the boundary away merges rhythm that\n * the original keeps separate;\n * - a padded wrapper around ≥2 structural children: threading the padding\n * onto each child would inset every one of them (duplication) — the group\n * is the thing that's padded. */\nexport function collectStructuralChildren(\n el: HTMLElement,\n inherited: CardStyle = {},\n): StructuralChild[] {\n const out: StructuralChild[] = [];\n // A nested block's background-layer sibling paints the whole block: fold it\n // onto the card its content siblings inherit. Band-level layers stay out —\n // a band's background is SectionBand territory.\n const layerBg = inherited.nested === true ? layerBackground(el) : undefined;\n const base: CardStyle =\n layerBg !== undefined ? { ...inherited, layerBackground: layerBg } : inherited;\n for (const child of el.childNodes) {\n if (!isElement(child)) continue;\n // The layer itself is pure paint (abs-fill, never content) — consumed above.\n if (hasClass(child, \"block-background-layer\")) continue;\n // A hidden feed-template prototype ({{…}} tokens, cloned by JS per record)\n // is not real content — drop it so the band doesn't render literal braces.\n if (isFeedTemplate(child)) continue;\n const nested = base.nested === true || isCellBoundary(child);\n const background = inlineBg(child) ?? base.background;\n // Cell-level wrappers only — a band-level container's padding (and\n // min-height: the full-height band chrome) is the band's own concern\n // (see CardStyle.nested).\n const padding = (nested ? inlinePadding(child) : undefined) ?? base.padding;\n const minHeight = (nested ? inlineMinHeight(child) : undefined) ?? base.minHeight;\n const valign = (nested && hasClass(child, \"valignmiddle\")) || base.valign === true;\n const card: CardStyle = {\n ...(background !== undefined ? { background } : {}),\n ...(padding !== undefined ? { padding } : {}),\n ...(minHeight !== undefined ? { minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(valign ? { valign } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n // A boxed wrapper (padding, min-height, or a paint layer) around ≥2\n // structural children is PROMOTED so the box applies exactly once —\n // threading it onto each child would inset/size/paint every one of them.\n const group =\n !isStructural(child) &&\n ((hasClass(child, \"block-subcontent\") && parseGridToken(child.classNames) === null) ||\n (nested &&\n (inlinePadding(child) !== undefined ||\n inlineMinHeight(child) !== undefined ||\n layerBackground(child) !== undefined))) &&\n collectStructuralChildren(child).length >= 2;\n if (isStructural(child)) {\n // A structural child's card carries ONLY what the peeled wrappers above\n // it accumulated — never the child's own inline style: leaves self-carry\n // it (textLeafStyle, readImgSizing) and boxing it here would apply it\n // twice (band-3 stat labels' 8px inset, once threaded contexts reach\n // inside token-bearing holders).\n const inheritedCard: CardStyle = {\n ...(base.background !== undefined ? { background: base.background } : {}),\n ...(base.padding !== undefined ? { padding: base.padding } : {}),\n ...(base.minHeight !== undefined ? { minHeight: base.minHeight } : {}),\n ...(base.layerBackground !== undefined ? { layerBackground: base.layerBackground } : {}),\n ...(base.valign === true ? { valign: true } : {}),\n ...(base.fill === true ? { fill: true } : {}),\n ...(nested ? { nested } : {}),\n };\n out.push({\n el: child,\n ...(Object.keys(inheritedCard).length > 0 ? { card: inheritedCard } : {}),\n });\n } else if (group) {\n // The promoted wrapper IS the box — its own values ride the group card.\n // Its own paint layer sits one level down where the pre-scan (which only\n // reads el's direct children) won't see it again — fold it on here. A\n // promoted cell boundary keeps its inner walk's capture instead (its\n // own pre-scan runs nested).\n const ownLayer = !isCellBoundary(child) ? layerBackground(child) : undefined;\n const groupCard: CardStyle =\n ownLayer !== undefined ? { ...card, layerBackground: ownLayer } : card;\n out.push({\n el: child,\n ...(Object.keys(groupCard).length > 0 ? { card: groupCard } : {}),\n });\n } else {\n out.push(...collectStructuralChildren(child, card));\n }\n }\n return out;\n}\n\n/** Does a parsed leaf node carry real text (not just empty markup)? Gates caption\n * capture so an empty `.block-*` inside a media holder adds nothing. Routes\n * heading/body through `blockPlainText` (same as the subtitle path) so an\n * entity- or ` `-only block reads as empty, not as text. */\nfunction nodeHasText(n: Node): boolean {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) !== \"\";\n if (n.kind === \"subtitle\") return n.text.trim() !== \"\";\n return false;\n}\n\n/** Is a caption element (or an ancestor up to and including the media holder)\n * marked `class:\"disable\"`? Blux omits disabled blocks from the rendered HTML,\n * but guard anyway so hidden copy is never leaked into a caption. */\nfunction isDisabledWithin(caption: HTMLElement, holder: HTMLElement): boolean {\n let a: HTMLElement | null | undefined = caption;\n while (a) {\n if (hasClass(a, \"disable\")) return true;\n if (a === holder) return false;\n a = a.parentNode as HTMLElement | null | undefined;\n }\n return false;\n}\n\n/** Parse one element into a grid Node. Leaves dispatch by role; everything else\n * becomes a row / stack / single / raw via parseContainer. */\nexport function parseNode(el: HTMLElement, nested = false): Node {\n if (hasClass(el, \"block-title\") && /^H[1-6]$/.test(el.tagName ?? \"\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"heading\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n level: headingLevel(el),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-body\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n return {\n kind: \"body\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n html: el.innerHTML,\n };\n }\n if (hasClass(el, \"block-subtitle\")) {\n const role = textRoleFromClass(el.classNames);\n const style = textLeafStyle(el);\n // Route through blockPlainText (not raw `.text`): a `<br>` in a display\n // subtitle survives as a newline while insignificant source whitespace\n // collapses — `.text` alone can't tell a hard break from source formatting.\n return {\n kind: \"subtitle\",\n ...(role ? { role } : {}),\n ...(style ? { style } : {}),\n text: blockPlainText(el.innerHTML),\n };\n }\n if (isMediaHolder(el)) {\n const media = mediaFromElement(el);\n if (media) {\n // Blux slider tiles nest the slide's CAPTION inside the media holder (the\n // holder is `data-bgmedia` and the copy overlays it). The holder is an\n // opaque media leaf, so those captions would be dropped. When one carries\n // block-title/body/subtitle text, emit the media PLUS the caption(s) as a\n // stack so the copy survives. Pure-media holders — the vast majority —\n // stay a bare media node, byte-identical (no `.block-*` descendant → no\n // extra work). This does NOT change the peel boundary: the holder is still\n // a structural leaf; only its own internal text is recovered here.\n const captions = el\n .querySelectorAll(\".block-title, .block-body, .block-subtitle\")\n .filter((c) => !isDisabledWithin(c, el))\n .map((c) => parseNode(c))\n .filter(nodeHasText);\n if (captions.length) {\n return { kind: \"stack\", children: [{ kind: \"media\", media }, ...captions] };\n }\n return { kind: \"media\", media };\n }\n }\n if (el.hasAttribute(\"data-exec\")) {\n // Custom-code embed (map, third-party widget). Keep the whole subtree —\n // including id=\"burbank_map\" and any inline initMap/KmlLayer scripts — so\n // extract-map can read it and Grid.svelte can render it verbatim.\n return { kind: \"raw\", html: el.outerHTML };\n }\n if (el.tagName === \"A\") {\n // A leaf CTA button / text link (isLeafAnchor). Preserve the whole anchor —\n // href + label — verbatim so the render layer keeps the clickable link.\n return { kind: \"raw\", html: el.outerHTML };\n }\n return parseContainer(el, nested);\n}\n\n/** Attach a peeled card wrapper's styling to a container node's `style`\n * (row/stack only — a Blux card wraps a grid or a stack of blocks, never a bare\n * leaf). The background is the card's fill; the padding is the content inset its\n * `.blocksNcontainer` applies. Padding rides only when a background marks this a\n * real card, so a plain band container's padding (already handled via the band's\n * blockClass defaults) is not double-captured onto a nested node. */\n/** Does a container node's style carry visible paint or sizing — the cues that\n * make a `cagridFlexHeight` column-fill hint worth emitting? */\nconst nodePainted = (style: Record<string, string>): boolean =>\n \"background\" in style || \"background-color\" in style || \"min-height\" in style;\n\nfunction withCardStyle(node: Node, card?: CardStyle): Node {\n if (\n !card ||\n (card.background === undefined &&\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign &&\n !card.fill)\n )\n return node;\n // background-color first, `background` shorthand second: when a block has\n // both a wrapper fill and a paint layer, the fuller layer paint wins (CSS\n // last-declaration order in the emitted style attribute).\n if (node.kind === \"row\" || node.kind === \"stack\") {\n const style: Record<string, string> = { ...(node.style ?? {}) };\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n // The FlexHeight column fill only matters when there's paint/sizing to\n // extend — an unpainted stack stretching is visually identity.\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n if (Object.keys(style).length === 0) return node;\n return { ...node, style };\n }\n // A MEDIA leaf under a min-height wrapper keeps its leaf shape: the frame\n // height folds into media.minHeight (slider slides repeat the same value on\n // holder and wrapper) instead of boxing the slide in a synthetic stack —\n // which would demote Carousel classification (carouselSlides matches bare\n // media) and turn working sliders into static galleries.\n if (node.kind === \"media\" && card.padding === undefined && !card.valign) {\n if (card.minHeight !== undefined && node.media.minHeight === undefined)\n return { ...node, media: { ...node.media, minHeight: card.minHeight } };\n return node;\n }\n // A boxed wrapper around a bare non-media leaf (padding, min-height, valign,\n // or a paint layer — e.g. band 11's `20px 0 30px` cell container wrapping a\n // single heading, or a flush gradient card holding one): the leaf has no\n // container-style slot, so a synthetic one-child stack carries the box. A\n // wrapper background-color ALONE still drops, as before — those (the\n // carousel captions) are handled by their own render path, and the Grid\n // tree must not invent a box for them.\n if (\n card.padding === undefined &&\n card.minHeight === undefined &&\n card.layerBackground === undefined &&\n !card.valign\n )\n return node;\n const style: Record<string, string> = {};\n if (card.padding !== undefined) style.padding = card.padding;\n if (card.background !== undefined) style[\"background-color\"] = card.background;\n if (card.layerBackground !== undefined) style[\"background\"] = card.layerBackground;\n if (card.minHeight !== undefined) style[\"min-height\"] = card.minHeight;\n if (card.valign) style[\"_valign\"] = \"middle\";\n if (card.fill && nodePainted(style)) style[\"_fill\"] = \"column\";\n return { kind: \"stack\", children: [node], style };\n}\n\n/** Parse a wrapper/cell/band-body element: a row when it is a grid or holds\n * ≥2 token-bearing children, else a stack / single / raw. */\nexport function parseContainer(el: HTMLElement, nested = false): Node {\n // A container that IS a cell/grid element starts its walk inside the cell\n // context, so its own inner wrappers' padding is captured (CardStyle.nested).\n // A PROMOTED wrapper (a boxed group inside a cell) threads `nested` in from\n // its call site — restarting outside the cell context would silently drop\n // the padding/valign its inner wrappers still carry. A cagridFlexHeight grid\n // additionally stretches each cell's direct block to the full row height —\n // its cells' cards carry `fill` so painted blocks emit the `_fill: column`\n // hint (the fill never threads past the cell: the cell's own parseContainer\n // restarts the walk without it).\n const kids = collectStructuralChildren(\n el,\n isCellBoundary(el) || hasClass(el, \"cagrid\")\n ? { nested: true, ...(hasClass(el, \"cagridFlexHeight\") ? { fill: true } : {}) }\n : nested\n ? { nested: true }\n : {},\n );\n // Parse each structural child up front, then drop any that collapse to an\n // EMPTY raw — an empty `.caslider`/wrapper (no static slides, JS-hydrated)\n // yields `raw:\"\"`, which would otherwise survive as a phantom sibling (e.g.\n // turning a lone poster image into `[media, empty-block]`). A non-empty raw\n // (a `[data-exec]` embed, a leaf `<a>`) always has real html, so it is kept.\n // A structural child may carry `card` styling (background + padding) peeled off\n // a card wrapper — it rides onto that child's container node via withCardStyle.\n const parsed = kids\n .map((k) => ({\n token: parseGridToken(k.el.classNames),\n node: withCardStyle(parseNode(k.el, k.card?.nested === true), k.card),\n }))\n .filter((p) => !(p.node.kind === \"raw\" && p.node.html.trim() === \"\"));\n const isGrid = hasClass(el, \"cagrid\");\n const tokenCount = parsed.filter((p) => p.token).length;\n // A LONE width-constrained cell (grid-2-r60 etc.) still needs its row: the\n // token IS the content column's width (band 9/11's 60% column) — flattening\n // it to a stack silently renders the content full-width. cols 1 / \"any\"\n // carry no width constraint, so a lone one still flattens.\n const hasWidthToken = parsed.some((p) => p.token && p.token.cols !== 1 && p.token.cols !== \"any\");\n\n if ((isGrid || tokenCount >= 2 || hasWidthToken) && parsed.length > 0) {\n const cells: Cell[] = parsed.map((p) => ({ token: p.token ?? DEFAULT_TOKEN, node: p.node }));\n if (hasClass(el, \"caslider\")) {\n // A source slider row. `data-columns` = slides visible at a time; only a\n // positive integer is meaningful (conditional build keeps `columns`\n // absent, not undefined, under exactOptionalPropertyTypes).\n const cols = Number(el.getAttribute(\"data-columns\"));\n const slider = Number.isInteger(cols) && cols > 0 ? { columns: cols } : {};\n return { kind: \"row\", cells, slider };\n }\n return { kind: \"row\", cells };\n }\n const [only] = parsed;\n if (parsed.length === 1 && only) return only.node;\n if (parsed.length === 0) return { kind: \"raw\", html: rawInnerHtml(el) };\n return { kind: \"stack\", children: parsed.map((p) => p.node) };\n}\n\n/** The raw-fallback innerHTML, blanked when it is a JS-hydrated feed grid's\n * leftover `{{…}}` template (a container with no structural content of its\n * own). Real content is already extracted as nodes before this fallback is\n * reached, so Handlebars tokens here are never real content — dumping them\n * would render literal `{{media}}` text. A genuinely-unrecognized container\n * (no tokens) still surfaces verbatim as before. */\nfunction rawInnerHtml(el: HTMLElement): string {\n const html = el.innerHTML;\n return /\\{\\{[a-z_]+\\}\\}/i.test(html) ? \"\" : html;\n}\n\nconst BAND_ID_RE = /^page-block-(\\d+)$/;\n// The band wrapper's blocksN class — kept off the HTML because site.json\n// items[].class is unreliable (null for 7/16 the-pointe blocks).\nconst BLOCK_CLASS_RE = /\\bblocks\\d+\\b/;\n\n/** Read the band-level background media off a `camediaload` band wrapper. */\nfunction bandBackground(el: HTMLElement): Media | undefined {\n if (!hasClass(el, \"camediaload\")) return undefined;\n const rawId = el.getAttribute(\"data-media\");\n if (!rawId) return undefined;\n const ext = el.getAttribute(\"data-ext\") ?? undefined;\n const base = el.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n // A band background carries its own render sizing (background-size/position);\n // a corner-anchored `auto` accent must not be centered + full-bleed.\n ...readBgSizing(el),\n };\n}\n\n/** Parse the rendered Blux index.html into the page's top-level band tree. */\nexport function parseGridBands(html: string): Band[] {\n const root = parse(html);\n const content = root.querySelector(\"#page-content\");\n if (!content) return [];\n const bands: Band[] = [];\n for (const child of content.childNodes) {\n if (!isElement(child)) continue;\n const m = BAND_ID_RE.exec(child.getAttribute(\"id\") ?? \"\");\n if (!m) continue;\n const idStr = m[1];\n if (idStr === undefined) continue;\n const background = bandBackground(child);\n const blockClass = BLOCK_CLASS_RE.exec(child.classNames)?.[0];\n bands.push({\n index: Number(idStr),\n ...(blockClass ? { blockClass } : {}),\n ...(background ? { background } : {}),\n root: parseContainer(child),\n });\n }\n return bands;\n}\n","import type { GridToken } from \"./types.js\";\n\nconst TOKEN_RE = /\\bgrid-(\\d+|any)(?:-(r|s)(\\d+))?\\b/;\n\n/** Parse the `grid-*` layout token out of an element's class string.\n * `grid-2` -> equal 2-col; `grid-2-r60` -> 60% of a 2-col row; `grid-1-s40` ->\n * 1-col with 40px inter-cell spacing (the `s` suffix is the grid's gap, matching\n * `data-spacing`, NOT a width — width comes from the column count). Returns null\n * when the class has no grid token (e.g. `grid-container`, no column count). */\nexport function parseGridToken(className: string): GridToken | null {\n const m = TOKEN_RE.exec(className);\n if (!m) return null;\n const cols = m[1] === \"any\" ? \"any\" : Number(m[1]);\n const token: GridToken = { cols, raw: m[0] };\n if (m[2] === \"r\") token.ratio = Number(m[3]);\n if (m[2] === \"s\") token.spacing = Number(m[3]);\n return token;\n}\n","import { parse } from \"node-html-parser\";\nimport type { HTMLElement } from \"node-html-parser\";\nimport type { Media, VideoPlayback } from \"./types.js\";\n\nconst ROLE_RE = /\\btext\\d+\\b/;\n\n/** The Blux text role (`text5`, `text11`, …) carried on a block-title/body/subtitle\n * element's class in the rendered HTML, or undefined when none is present. */\nexport function textRoleFromClass(className: string): string | undefined {\n return ROLE_RE.exec(className)?.[0];\n}\n\n/** The heading level (1..6) for an h1..h6 element. */\nexport function headingLevel(el: HTMLElement): number {\n const m = /^H([1-6])$/.exec(el.tagName ?? \"\");\n return m ? Number(m[1]) : 2;\n}\n\n/** Plain text of a block's inner HTML for a title field: a hard line break\n * (`<br>`, with or without attributes/self-close) becomes a newline; every other\n * tag drops and HTML entities decode (`Bar & Grill` → `Bar & Grill`); all\n * source-formatting whitespace collapses to single spaces. Robust to\n * pretty-printed exports — insignificant newlines in the markup are NOT mistaken\n * for hard breaks — by routing `<br>` through a sentinel that survives the\n * whitespace collapse. */\nexport function blockPlainText(html: string): string {\n const BR = \"\\uE000\";\n // node-html-parser `.text` strips tags AND decodes entities; the <br>→BR swap\n // runs first so hard breaks survive as the sentinel through the collapse below.\n const text = parse(html.replace(/<br\\b[^>]*>/gi, BR)).text;\n return text\n .replace(/\\s+/g, \" \")\n .replace(/ *\\uE000 */g, \"\\n\")\n .trim();\n}\n\n/** The last path segment of a CDN url, sans extension (the Blux asset uuid). */\nfunction uuidFromUrl(url: string): { id: string; ext?: string } {\n const base = url.split(/[?#]/)[0] ?? \"\";\n const file = base.split(\"/\").pop() ?? \"\";\n const dot = file.lastIndexOf(\".\");\n return dot > 0 ? { id: file.slice(0, dot), ext: file.slice(dot + 1) } : { id: file };\n}\n\n/** Blux `data-media` is sometimes `uuid.ext` and sometimes a bare `uuid`; the\n * existing asset pipeline keys on the bare uuid (extension stripped), matching the\n * video path. Strip a trailing `.<ext>` when `data-ext` names it. */\nexport function stripAssetExt(rawId: string, ext?: string): string {\n return ext && rawId.endsWith(`.${ext}`) ? rawId.slice(0, -(ext.length + 1)) : rawId;\n}\n\n/** Read a single CSS declaration's value out of an inline `style` string. */\nexport function cssProp(style: string, prop: string): string | undefined {\n const m = new RegExp(`(?:^|;|\\\\s)${prop}\\\\s*:\\\\s*([^;]+)`, \"i\").exec(style);\n return m?.[1]?.trim();\n}\n\nconst MARGIN_UTIL_RE = /\\bmargin-(\\d+)(r|l|t|b)\\b/g;\nconst MARGIN_SIDES = { r: \"right\", l: \"left\", t: \"top\", b: \"bottom\" } as const;\n\n/** Decode Blux margin utility classes: `margin-20r` → `margin-right: 20%`.\n * Only `margin-N(r|l|t|b)` decodes — the `pd_*` padding utilities are always\n * duplicated inline in the export, so inline capture already covers them. */\nexport function utilityStylesFromClass(className: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const m of className.matchAll(MARGIN_UTIL_RE)) {\n const [, n, side] = m;\n if (n && side) out[`margin-${MARGIN_SIDES[side as keyof typeof MARGIN_SIDES]}`] = `${n}%`;\n }\n return out;\n}\n\n/** A text leaf's style deviations: the allowlisted inline declarations (`color`,\n * `padding`, `margin*` — everything else is theme noise) merged with the margin\n * utilities decoded off its class. An inline declaration wins over a class\n * utility on conflict. Null when the leaf carries neither, so callers can keep\n * the `style` key absent (exactOptionalPropertyTypes). */\nexport function textLeafStyle(el: HTMLElement): Record<string, string> | null {\n const out = utilityStylesFromClass(el.classNames ?? \"\");\n for (const decl of (el.getAttribute(\"style\") ?? \"\").split(\";\")) {\n const colon = decl.indexOf(\":\");\n if (colon < 0) continue;\n const prop = decl.slice(0, colon).trim().toLowerCase();\n const value = decl.slice(colon + 1).trim();\n if (!value) continue;\n if (prop === \"color\" || prop === \"padding\" || prop.startsWith(\"margin\")) out[prop] = value;\n }\n return Object.keys(out).length ? out : null;\n}\n\nconst CENTERED = new Set([\"center\", \"center center\", \"50% 50%\", \"50%\"]);\n\n/** Background sizing off a BAND-background wrapper's inline style: its\n * `background-size` → `fit` (a background's `auto`/`contain` is meaningful — a\n * native-size decorative accent, not a full-bleed `cover`) and its\n * `background-position` → `position` (e.g. \"right bottom\"). Unlike `readImgSizing`\n * (foreground), `auto` is kept; `cover` and a centered position are the render\n * defaults, so they are left absent to keep the manifest to deviations only. */\nexport function readBgSizing(el: HTMLElement): Pick<Media, \"fit\" | \"position\"> {\n const style = el.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"fit\" | \"position\"> = {};\n const size = cssProp(style, \"background-size\")?.toLowerCase();\n if (size === \"auto\" || size === \"contain\") out.fit = size;\n const pos = cssProp(style, \"background-position\");\n if (pos && !CENTERED.has(pos.toLowerCase())) out.position = pos;\n return out;\n}\n\n/** Intrinsic render sizing off a foreground image holder: the inline pixel\n * `width`, the `.mediaRatio` `data-og-ratio` (→ `aspect`), and the\n * `background-size` (→ `fit`, only when contain/cover — a background's `auto`\n * is not foreground sizing). Each field is present only when the source has it,\n * so a plain holder still yields a bare `Media`. */\nfunction readImgSizing(holder: HTMLElement): Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> {\n const style = holder.getAttribute(\"style\") ?? \"\";\n const out: Pick<Media, \"width\" | \"aspect\" | \"fit\" | \"minHeight\"> = {};\n // Only a pixel width is a faithful intrinsic size. A `%`/`vw`/`em`/`calc()`\n // width is relative to context and must NOT be mistaken for px (which the\n // render layer would then apply literally) — skip it, leaving `width` absent.\n const w = cssProp(style, \"width\");\n const wpx = w ? /^(\\d+(?:\\.\\d+)?)px$/i.exec(w) : null;\n if (wpx?.[1]) out.width = Math.round(parseFloat(wpx[1]));\n const ogr = holder.querySelector(\".mediaRatio\")?.getAttribute(\"data-og-ratio\");\n if (ogr) {\n const n = Number(ogr);\n if (Number.isFinite(n)) out.aspect = Math.round(n * 1000) / 1000;\n }\n const fit = cssProp(style, \"background-size\")?.toLowerCase();\n if (fit === \"contain\" || fit === \"cover\") out.fit = fit;\n // The holder's inline min-height (e.g. \"80vh\" on a slider slide) is the\n // height the export reserves for a cover-rendered frame — keep it.\n const mh = cssProp(style, \"min-height\");\n if (mh) out.minHeight = mh;\n return out;\n}\n\n/** Playback semantics from a `<video>`'s boolean attributes — only those PRESENT\n * are set (an absent field = attribute absent). Undefined when none is present. */\nfunction readVideoPlayback(video: HTMLElement): VideoPlayback | undefined {\n const flags = [\"controls\", \"playsinline\", \"autoplay\", \"loop\", \"muted\"] as const;\n const pb: VideoPlayback = {};\n for (const f of flags) if (video.hasAttribute(f)) pb[f] = true;\n return Object.keys(pb).length ? pb : undefined;\n}\n\n/** Intrinsic aspect for a foreground `<video>`, reserved on a nearby\n * `.ib[data-og-ratio]` holder OR a `.mediaRatio` (inline `padding-bottom:NN%`).\n * Values are percent-suffixed strings (e.g. \"56.25%\") — strip the `%` (raw\n * `Number()` NaNs), then reuse the `aspect` = height-%-of-width convention.\n * Fail-safe: no parseable ratio → undefined (video keeps its bare shape). */\nfunction readVideoAspect(video: HTMLElement): number | undefined {\n let raw: string | undefined;\n let anc: HTMLElement | null | undefined = video.parentNode;\n for (let i = 0; i < 3 && anc && !raw; i++) {\n raw = anc.getAttribute?.(\"data-og-ratio\") ?? undefined;\n if (!raw) {\n const mr = anc.querySelector?.(\".mediaRatio\");\n raw = mr ? cssProp(mr.getAttribute(\"style\") ?? \"\", \"padding-bottom\") : undefined;\n }\n anc = anc.parentNode as HTMLElement | null | undefined;\n }\n if (!raw) return undefined;\n const n = parseFloat(raw.replace(/%\\s*$/, \"\"));\n return Number.isFinite(n) ? Math.round(n * 1000) / 1000 : undefined;\n}\n\n/** Resolve the media an element carries: a `.camediaload` descendant (image, via\n * `data-media`) or a `<video>` (via its src uuid). Returns null when there is none. */\nexport function mediaFromElement(el: HTMLElement): Media | null {\n if (el.tagName === \"VIDEO\") {\n const src = el.getAttribute(\"src\") ?? \"\";\n const { id, ext } = uuidFromUrl(src);\n if (!id) return null;\n // The full CDN url sits on `<video src>`; capture its prefix as `base` (the\n // same field an image carries from `data-base`) so `mediaCdnUrl` rebuilds it\n // OFFLINE. Without this a video resolves only via the IR sourceUrl, i.e. it\n // depends on site.json listing the asset — breaking convert's offline\n // invariant even though the url is right here in the markup.\n const clean = src.split(/[?#]/)[0] ?? \"\";\n const slash = clean.lastIndexOf(\"/\");\n const base = slash >= 0 ? clean.slice(0, slash + 1) : undefined;\n const aspect = readVideoAspect(el);\n const playback = readVideoPlayback(el);\n return {\n kind: \"video\",\n assetId: id,\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...(aspect !== undefined ? { aspect } : {}),\n ...(playback ? { playback } : {}),\n };\n }\n const img =\n el.classList.contains(\"camediaload\") && el.getAttribute(\"data-media\")\n ? el\n : el.querySelector(\".camediaload[data-media]\");\n if (img) {\n const rawId = img.getAttribute(\"data-media\");\n if (rawId) {\n const ext = img.getAttribute(\"data-ext\") ?? undefined;\n const base = img.getAttribute(\"data-base\") ?? undefined;\n return {\n kind: \"image\",\n assetId: stripAssetExt(rawId, ext),\n ...(ext ? { ext } : {}),\n ...(base ? { base } : {}),\n ...readImgSizing(img),\n };\n }\n }\n const video = el.querySelector(\"video\");\n if (video) return mediaFromElement(video);\n return null;\n}\n","import type { Band, Cell, Media, Node, Widget } from \"./types.js\";\nimport type { CarouselSlide, CarouselSpec, SliceSpec } from \"./slice-spec.js\";\nimport { blockPlainText } from \"./leaf.js\";\n\n/** Depth-first collect of every `media` node's `Media` in a subtree. */\nexport function collectMedia(node: Node): Media[] {\n switch (node.kind) {\n case \"media\":\n return [node.media];\n case \"row\":\n return node.cells.flatMap((c) => collectMedia(c.node));\n case \"stack\":\n return node.children.flatMap(collectMedia);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of text nodes (heading/body/subtitle). */\nexport function collectText(node: Node): Node[] {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectText(c.node));\n case \"stack\":\n return node.children.flatMap(collectText);\n case \"media\":\n case \"widget\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every widget in a subtree. */\nexport function collectWidgets(node: Node): Widget[] {\n switch (node.kind) {\n case \"widget\":\n return [node.widget];\n case \"row\":\n return node.cells.flatMap((c) => collectWidgets(c.node));\n case \"stack\":\n return node.children.flatMap(collectWidgets);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"raw\":\n return [];\n }\n}\n\n/** Depth-first collect of every `raw` node in a subtree. */\nfunction collectRaws(node: Node): Node[] {\n switch (node.kind) {\n case \"raw\":\n return [node];\n case \"row\":\n return node.cells.flatMap((c) => collectRaws(c.node));\n case \"stack\":\n return node.children.flatMap(collectRaws);\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n return [];\n }\n}\n\n/** The root row NODE, or null when the root is not a single row. A `stack`\n * whose only child is a row also counts (Blux wraps rows in holders). */\nexport function topRowNode(node: Node): Extract<Node, { kind: \"row\" }> | null {\n if (node.kind === \"row\") return node;\n if (node.kind === \"stack\" && node.children.length === 1) {\n const [only] = node.children;\n if (only && only.kind === \"row\") return only;\n }\n return null;\n}\n\n/** The cells of the root row, or null when the root is not a single row. */\nexport function topRow(node: Node): Cell[] | null {\n return topRowNode(node)?.cells ?? null;\n}\n\n/** A `raw` node carrying no rendered text or nested block — the shape a\n * client-injected mount (e.g. the map container) parses to. */\nexport function isEmptyRaw(node: Node): boolean {\n if (node.kind !== \"raw\") return false;\n const text = node.html.replace(/<[^>]*>/g, \"\").trim();\n return text.length === 0;\n}\n\n/** Options for the classifier. `isMapMount` is injected by plan 4\n * (`extract-map.ts`); by default nothing is recognized as a map. */\nexport type ClassifyOptions = {\n isMapMount?: (node: Node) => boolean;\n};\n\n/** The band's slice-zone base carried onto every spec (conditional spread keeps\n * `blockClass`/`background` absent, not `undefined`, under\n * exactOptionalPropertyTypes). */\nfunction base(band: Band): { index: number; blockClass?: string; background?: Media } {\n return {\n index: band.index,\n ...(band.blockClass ? { blockClass: band.blockClass } : {}),\n ...(band.background ? { background: band.background } : {}),\n };\n}\n\n/** Plain text of a heading/subtitle/body node. Hard line breaks (Blux `<br>`)\n * survive as newlines so the render layer can split a display title back into\n * lines; all other tags and source whitespace collapse to single spaces. */\nfunction nodeText(node: Node): string {\n switch (node.kind) {\n case \"heading\":\n case \"body\":\n // Raw markup — `<br>` → newline, other tags + source formatting → spaces.\n return blockPlainText(node.html);\n case \"subtitle\":\n // Already normalized at parse via blockPlainText (entities decoded, hard\n // breaks as newlines, source whitespace collapsed) — pass it through.\n return node.text.trim();\n case \"row\":\n case \"stack\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return \"\";\n }\n}\n\n/** The single media of a pure-media cell, or null if the cell isn't pure media. */\n/** See through synthetic style boxes — a one-child stack carrying a peeled\n * wrapper's padding/fill — when pattern-matching. The box is presentation, not\n * structure: a cell must not flip from SplitFeature/Gallery to Grid just\n * because its container gained a captured inset. */\nfunction unboxed(node: Node): Node {\n let n = node;\n while (n.kind === \"stack\" && n.children.length === 1) {\n const only = n.children[0];\n if (!only) break;\n n = only;\n }\n return n;\n}\n\nfunction pureCellMedia(cell: Cell): Media | null {\n const n = unboxed(cell.node);\n return n.kind === \"media\" ? n.media : null;\n}\n\n/** TitleBand/Hero presentation metadata: the heading's textN role + h-level and\n * the subtitle's role. Carried alongside the plain-string text (page-doc) so the\n * render applies the right display font/tag — band 15's script accent heading\n * (`h2.text11`) must not render like a plain `text5` title. */\nfunction textRoleMeta(\n heading: Node | undefined,\n subtitle: Node | undefined,\n): { headingRole?: string; headingLevel?: number; subtitleRole?: string } {\n return {\n ...(heading?.kind === \"heading\" && heading.role ? { headingRole: heading.role } : {}),\n ...(heading?.kind === \"heading\" ? { headingLevel: heading.level } : {}),\n ...(subtitle?.kind === \"subtitle\" && subtitle.role ? { subtitleRole: subtitle.role } : {}),\n };\n}\n\n/** A cell's effective column share as a percentage, from its grid token.\n *\n * Width comes from the explicit `ratio` (`grid-2-r60` → 60) or, absent that,\n * an equal split of the column count. `spacing` (the `s` suffix) is a gap, not\n * a width, so it never factors in here. The `\"any\"` → 50 fallback assumes a\n * 2-cell row — the only shape that reaches here today (SplitFeature requires\n * exactly two cells); a bare-`any` cell in a wider row would need real division. */\nfunction cellRatio(cell: Cell): number {\n const t = cell.token;\n if (typeof t.ratio === \"number\") return t.ratio;\n if (t.cols === \"any\") return 50;\n return Math.round(100 / t.cols);\n}\n\nconst isTextNode = (n: Node): boolean =>\n n.kind === \"heading\" || n.kind === \"body\" || n.kind === \"subtitle\";\n\n/** A text node with no rendered text — an empty hero body block (`<p></p>`) or\n * a whitespace-only heading/subtitle. Not a real caption line. */\nconst isBlankText = (n: Node): boolean => {\n if (n.kind === \"heading\" || n.kind === \"body\") return blockPlainText(n.html) === \"\";\n if (n.kind === \"subtitle\") return n.text.trim() === \"\";\n return false;\n};\n\n/** The carousel slides of a slider row, or null when any cell isn't a media\n * slide. A slide is a bare `media` cell, or a stack whose FIRST child is media\n * followed only by text nodes — the band-8 gallery slide `stack[media,\n * heading]` and the full-page hero slide `stack[media, heading, body]` (title +\n * location). The heading is the caption; the first non-blank body/subtitle\n * after it is the `subcaption` (the hero's location line). A slide with a\n * non-text tail (a nested row/media) is richer than a captioned slide, so the\n * band stays a faithful Grid. ≥2 qualifying slides required. */\nfunction carouselSlides(cells: Cell[]): CarouselSlide[] | null {\n const out: CarouselSlide[] = [];\n for (const c of cells) {\n const n = c.node;\n if (n.kind === \"media\") {\n out.push({ media: n.media });\n continue;\n }\n if (n.kind === \"stack\" && n.children.length >= 2) {\n const [m, ...rest] = n.children;\n const h = rest.find((r) => r.kind === \"heading\");\n if (m?.kind === \"media\" && h?.kind === \"heading\" && rest.every(isTextNode)) {\n // The secondary line: the first body/subtitle after the title that\n // carries real text (the hero's location; skip empty body blocks).\n const sub = rest.find(\n (r) => r !== h && (r.kind === \"body\" || r.kind === \"subtitle\") && !isBlankText(r),\n );\n const subcaption =\n sub?.kind === \"body\"\n ? { html: sub.html, ...(sub.role ? { role: sub.role } : {}) }\n : sub?.kind === \"subtitle\"\n ? { html: sub.text, ...(sub.role ? { role: sub.role } : {}) }\n : undefined;\n out.push({\n media: m.media,\n caption: { html: h.html, level: h.level, ...(h.role ? { role: h.role } : {}) },\n ...(subcaption ? { subcaption } : {}),\n });\n continue;\n }\n }\n return null;\n }\n return out.length >= 2 ? out : null;\n}\n\n/** If every cell of a row is exactly one media node, return them in order. */\nfunction galleryMedia(cells: Cell[]): Media[] | null {\n const out: Media[] = [];\n for (const c of cells) {\n const m = pureCellMedia(c);\n if (!m) return null;\n out.push(m);\n }\n return out.length >= 2 ? out : null;\n}\n\n/** Return a copy of the tree with every node matching `isMapMount` replaced by a\n * `widget:map` node. Pure — does not mutate the input. Top-down: a matched\n * container is replaced whole, so its children are never visited. */\nfunction rewriteMapMounts(node: Node, isMapMount: (n: Node) => boolean): Node {\n if (isMapMount(node)) return { kind: \"widget\", widget: { type: \"map\" } };\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({\n token: c.token,\n node: rewriteMapMounts(c.node, isMapMount),\n })),\n // Preserve the row's own markers when rebuilding: dropping the slider\n // would silently demote a Carousel to Grid, and dropping the style would\n // lose a card background — for every band whenever a map config exists.\n ...(node.slider ? { slider: node.slider } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"stack\":\n return {\n kind: \"stack\",\n children: node.children.map((n) => rewriteMapMounts(n, isMapMount)),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n return node;\n }\n}\n\n/** The single significant child of a container (ignoring empty raw), or the node\n * itself. Used to detect a band whose dominant content is one widget. */\nfunction soleSignificant(node: Node): Node {\n let kids: Node[];\n switch (node.kind) {\n case \"row\":\n kids = node.cells.map((c) => c.node);\n break;\n case \"stack\":\n kids = node.children;\n break;\n case \"heading\":\n case \"body\":\n case \"subtitle\":\n case \"media\":\n case \"widget\":\n case \"raw\":\n kids = [node];\n break;\n }\n const significant = kids.filter((n) => !isEmptyRaw(n));\n return significant.length === 1 && significant[0] ? significant[0] : node;\n}\n\n/** Classify one band into a SliceSpec. Conservative: only unambiguous shapes\n * become pattern slices; everything else is a render-faithful Grid fallback. */\nexport function classifyBand(band: Band, opts: ClassifyOptions = {}): SliceSpec {\n // Widget rewrite runs FIRST, so the pattern branches and the Grid fallback\n // all see `widget` nodes in place of injected mounts.\n const root = opts.isMapMount ? rewriteMapMounts(band.root, opts.isMapMount) : band.root;\n const widgets = collectWidgets(root);\n const media = collectMedia(root);\n const text = collectText(root);\n const rowNode = topRowNode(root);\n const row = rowNode ? rowNode.cells : null;\n // A raw node with real content is text we cannot account for — every\n // promotion must refuse to fire over it (only the Grid fallback keeps it).\n const hasSignificantRaw = collectRaws(root).some((n) => !isEmptyRaw(n));\n\n // Top-level widget promotion (before the structural patterns).\n const sole = soleSignificant(root);\n if (sole.kind === \"widget\" && sole.widget.type === \"map\") {\n return { slice: \"LocationMap\", ...base(band) };\n }\n if (\n media.length === 1 &&\n media[0]?.kind === \"video\" &&\n text.length === 0 &&\n widgets.length === 0 &&\n row === null &&\n !hasSignificantRaw\n ) {\n const v = media[0];\n return { slice: \"VideoFeature\", ...base(band), media: v };\n }\n\n const headings = text.filter((n) => n.kind === \"heading\");\n const subtitles = text.filter((n) => n.kind === \"subtitle\");\n const bodies = text.filter((n) => n.kind === \"body\");\n\n // Text-only bands (no media, no row, no widgets, no significant raw — any\n // co-located content must survive via the Grid fallback, not be swallowed).\n if (media.length === 0 && row === null && widgets.length === 0 && !hasSignificantRaw) {\n // TitleBand: exactly one heading + at most one subtitle, nothing else —\n // TitleBandSpec has nowhere to carry surplus text.\n if (headings.length === 1 && subtitles.length <= 1 && bodies.length === 0 && !band.background) {\n const first = headings[0];\n const sub = subtitles[0];\n return {\n slice: \"TitleBand\",\n ...base(band),\n heading: first ? nodeText(first) : \"\",\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...textRoleMeta(first, sub),\n };\n }\n // RichText: body node(s) only — a subtitle would be dropped from the html.\n if (headings.length === 0 && subtitles.length === 0 && bodies.length > 0 && !band.background) {\n return {\n slice: \"RichText\",\n ...base(band),\n html: bodies.map((b) => (b.kind === \"body\" ? b.html : \"\")).join(\"\\n\"),\n };\n }\n }\n\n // Full-bleed hero: a background image with overlay text and no grid row. At\n // most one of each overlay text kind — HeroSpec keeps one heading/subtitle/\n // body, so surplus would be silently dropped.\n if (\n band.background &&\n headings.length === 1 &&\n subtitles.length <= 1 &&\n bodies.length <= 1 &&\n row === null &&\n media.length === 0 &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const h = headings[0];\n const sub = subtitles[0];\n const bod = bodies[0];\n return {\n slice: \"Hero\",\n ...base(band),\n ...(h ? { heading: nodeText(h) } : {}),\n ...(sub ? { subtitle: nodeText(sub) } : {}),\n ...(bod && bod.kind === \"body\" ? { body: bod.html } : {}),\n ...textRoleMeta(h, sub),\n };\n }\n\n // Carousel: a source slider row (.caslider) whose every cell is a media\n // slide, optionally captioned (stack[media, heading]). Anything richer\n // falls through to the faithful Grid fallback.\n if (rowNode?.slider) {\n const slides = carouselSlides(rowNode.cells);\n if (slides) {\n const spec: CarouselSpec = { slice: \"Carousel\", ...base(band), slides };\n if (rowNode.slider.columns !== undefined) spec.columns = rowNode.slider.columns;\n return spec;\n }\n }\n\n // Gallery: a row whose cells are all single media.\n if (row) {\n const gm = galleryMedia(row);\n if (gm) return { slice: \"Gallery\", ...base(band), media: gm };\n }\n\n // MediaFull: one media, no text, no top row, no widgets, no significant raw —\n // a row sibling, a co-located widget (e.g. a map mount), or raw prose would\n // be silently dropped, so those stay Grid.\n if (\n media.length === 1 &&\n text.length === 0 &&\n row === null &&\n widgets.length === 0 &&\n !hasSignificantRaw\n ) {\n const m = media[0];\n if (m) return { slice: \"MediaFull\", ...base(band), media: m };\n }\n\n // SplitFeature: exactly two cells, one pure media, one text-bearing.\n if (row && row.length === 2) {\n const [c0, c1] = row;\n if (c0 && c1) {\n const m0 = pureCellMedia(c0);\n const m1 = pureCellMedia(c1);\n const t0 = collectText(c0.node).length > 0;\n const t1 = collectText(c1.node).length > 0;\n if (m0 && !m1 && t1) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m0,\n mediaSide: \"left\",\n ratio: cellRatio(c0),\n text: c1.node,\n };\n }\n if (m1 && !m0 && t0) {\n return {\n slice: \"SplitFeature\",\n ...base(band),\n media: m1,\n mediaSide: \"right\",\n ratio: cellRatio(c1),\n text: c0.node,\n };\n }\n }\n }\n\n return { slice: \"Grid\", ...base(band), root };\n}\n\nexport function classifyBands(bands: Band[], opts: ClassifyOptions = {}): SliceSpec[] {\n return bands.map((b) => classifyBand(b, opts));\n}\n","// Plan 4 of docs/superpowers/specs/2026-07-08-blux-faithful-grid-slices-design.md:\n// deterministic extraction of the Blux map widget's config from the rendered\n// index.html. The initMap script carries mount/styles/layers; the toggle-chip\n// labels live in the band markup and the group logic in the site's clickMap\n// script. The Google API key lives only in the separate loader URL and is\n// deliberately NOT extracted — render uses VITE_GOOGLE_MAPS_KEY.\n\nimport type { Node } from \"./types.js\";\n\nexport type MapKmlLayer = {\n /** mapLayers key in the source script, e.g. \"Hotels\". */\n name: string;\n lid: string;\n /** Constructed with `map: map` — visible before any toggle. */\n initiallyVisible: boolean;\n /** Absent in source = false = layer fits the viewport to its KML bounds. */\n preserveViewport: boolean;\n};\n\nexport type MapToggleGroup = {\n label: string;\n layers: string[];\n /** The content panel (0-based `cagrid` cell) this chip reveals — chip index\n * `i` shows panel `i`, hiding the others; pairs with `MapConfig.defaultToggle`. */\n panelIndex: number;\n};\n\nexport type MapConfig = {\n mountId: string;\n mid: string;\n layers: MapKmlLayer[];\n toggles: MapToggleGroup[];\n /** Google Maps style rules, verbatim JSON. */\n styles: unknown[];\n center?: { lat: number; lng: number };\n zoom?: number;\n /** The mount div's inline height (e.g. \"600px\"); absent = let the render decide. */\n height?: string;\n /** Which toggle/panel is active on load (source default = the first, 0). Present\n * only when there are toggles. */\n defaultToggle?: number;\n};\n\nconst SCRIPT_RE = /<script\\b[^>]*>([\\s\\S]*?)<\\/script>/g;\n\nfunction findScript(html: string, marker: RegExp): string | null {\n for (const m of html.matchAll(SCRIPT_RE)) {\n const body = m[1];\n if (body !== undefined && marker.test(body)) return body;\n }\n return null;\n}\n\n/** Balanced-bracket slice of the JSON array starting at `styles:[`. */\nfunction extractStyles(script: string): unknown[] {\n const at = script.indexOf(\"styles:[\");\n if (at === -1) return [];\n const start = at + \"styles:\".length;\n let depth = 0;\n for (let i = start; i < script.length; i++) {\n const ch = script[i];\n if (ch === \"[\") depth++;\n else if (ch === \"]\") {\n depth--;\n if (depth === 0) {\n try {\n // The Blux styles literal uses unquoted keys — quote them for JSON.parse.\n const raw = script.slice(start, i + 1);\n const jsonish = raw.replace(/([{,])\\s*([A-Za-z_][A-Za-z0-9_.]*)\\s*:/g, '$1\"$2\":');\n const parsed: unknown = JSON.parse(jsonish);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n }\n }\n return [];\n}\n\nconst LAYER_RE = /(\\w+)\\s*:\\s*new google\\.maps\\.KmlLayer\\(\\{([^}]*)\\}\\)/g;\n\nexport function extractMapConfig(html: string): MapConfig | null {\n const init = findScript(html, /function initMap\\(\\)[\\s\\S]*new google\\.maps\\.Map/);\n if (!init) return null;\n\n const mountId = /getElementById\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/.exec(init)?.[1];\n if (!mountId) return null;\n\n const layers: MapKmlLayer[] = [];\n let mid: string | undefined;\n for (const m of init.matchAll(LAYER_RE)) {\n const name = m[1];\n const args = m[2];\n if (!name || args === undefined) continue;\n const layerMid = /[?&]mid=([^&\"']+)/.exec(args)?.[1];\n const lid = /[?&]lid=([^&\"']+)/.exec(args)?.[1];\n if (!layerMid || !lid) continue;\n mid ??= layerMid;\n layers.push({\n name,\n lid,\n initiallyVisible: /\\bmap\\s*:\\s*map\\b/.test(args),\n preserveViewport: /preserveViewport\\s*:\\s*true/.test(args),\n });\n }\n if (!mid || layers.length === 0) return null;\n\n const centerM = /center\\s*:\\s*\\{\\s*lat\\s*:\\s*(-?[\\d.]+)\\s*,\\s*lng\\s*:\\s*(-?[\\d.]+)\\s*\\}/.exec(\n init,\n );\n const zoomM = /zoom\\s*:\\s*(\\d+)/.exec(init);\n\n // The mount div's own inline height (`<div id=\"burbank_map\" style=\"…height:600px\">`)\n // — it lives on the inner mount, not the section, so section-style capture misses it.\n const mountTag = new RegExp(`<[^>]*\\\\bid=[\"']${mountId}[\"'][^>]*>`, \"i\").exec(html)?.[0] ?? \"\";\n const height = /height\\s*:\\s*([\\d.]+(?:px|%|vh|vw|em|rem))/i.exec(mountTag)?.[1];\n\n const toggles = extractToggles(html);\n\n return {\n mountId,\n mid,\n layers,\n toggles,\n styles: extractStyles(init),\n ...(centerM?.[1] && centerM[2]\n ? { center: { lat: Number(centerM[1]), lng: Number(centerM[2]) } }\n : {}),\n ...(zoomM?.[1] ? { zoom: Number(zoomM[1]) } : {}),\n ...(height ? { height } : {}),\n ...(toggles.length ? { defaultToggle: 0 } : {}),\n };\n}\n\n/** The classifier predicate (plan-2 `ClassifyOptions.isMapMount`): matches the\n * raw node carrying the map mount element. Mounts parse to `raw` nodes; the\n * mount id survives verbatim in the serialized html. */\nexport function makeIsMapMount(config: MapConfig): (node: Node) => boolean {\n const marker = `id=\"${config.mountId}\"`;\n return (node) => node.kind === \"raw\" && node.html.includes(marker);\n}\n\n/** Pairs the band's map_icon chip labels (DOM order) with the clickMap\n * groups (index order). Either side missing → no toggles (map still renders\n * its initially-visible layers). */\nfunction extractToggles(html: string): MapToggleGroup[] {\n const labels = [...html.matchAll(/map_icon_text\">([^<]*)</g)]\n .map((m) => m[1])\n .filter((l): l is string => l !== undefined);\n const click = findScript(html, /clickMap\\s*=\\s*\\{\\s*0\\s*:/);\n if (!click || labels.length === 0) return [];\n const bodyM = /clickMap\\s*=\\s*(\\{[\\s\\S]*?\\}\\})\\s*;/.exec(click);\n if (!bodyM?.[1]) return [];\n const groups: MapToggleGroup[] = [];\n for (const g of bodyM[1].matchAll(/(\\d+)\\s*:\\s*function\\s*\\(onoff\\)\\s*\\{([^}]*)\\}/g)) {\n const idx = Number(g[1]);\n const body = g[2] ?? \"\";\n const layerNames = [\n ...new Set(\n [...body.matchAll(/mapLayers\\.(\\w+)\\./g)]\n .map((m) => m[1])\n .filter((n): n is string => n !== undefined),\n ),\n ];\n const label = labels[idx];\n if (label === undefined) return [];\n groups[idx] = { label, layers: layerNames, panelIndex: idx };\n }\n // Non-contiguous clickMap indices would leave holes that serialize as null.\n return groups.length === labels.length && groups.every((g) => g !== undefined) ? groups : [];\n}\n","// Feed-grid materialization. Blux feed grids (gallery/portfolio) render their\n// tiles CLIENT-SIDE from feed records — the static export ships only a\n// display:none {{…}} template (dropped by the parser). This module rebuilds\n// the visible tiles DETERMINISTICALLY from the feed data at convert time, so\n// the faithful render shows the real content instead of an empty band.\n//\n// A feed band's site.json item carries `sources` (a feed id, or `__media` for\n// the media library) and `sourceConfig` (a tag filter, a sort, per-tile style\n// config). We resolve the matching records, expand each into a tile\n// (image + optional title/body), and return a Grid node tree — so the band\n// classifies and renders as any other Grid, with no new render surface.\nimport type { Cell, Media, Node } from \"./types.js\";\nimport { CDN_HOSTS } from \"../assets.js\";\n\n/** A media-library entry (`site.json.media[uuid]`) or a feed record. Both are\n * loose bags; we read only the fields the tile needs. */\nexport type FeedRecord = Record<string, unknown>;\n\n/** A resolved tile: an image (already a Media) and/or display text. `title`\n * and `body` are RENDER-READY HTML — feed records store them as HTML (entities\n * pre-encoded, `<br>` markup), and `__media` plain text (name/description) is\n * escaped at resolve time — so the render places them verbatim, never\n * re-escaping (which would double-encode `&` or show a literal `<br>`). */\nexport type FeedTile = { media?: Media; title?: string; body?: string };\n\n/** Everything the materializer needs from the export, injected so the module\n * stays pure/offline and testable. */\nexport type FeedResolvers = {\n /** Feed records by feed id (site.json feeds → their JSON arrays). */\n feeds: Map<string, FeedRecord[]>;\n /** The media library: uuid → its `{ name, type, tags }` entry. */\n media: Map<string, FeedRecord>;\n /** Build a Media (with a resolved url base) for an asset uuid, given its mime\n * type and/or filename (either can supply the extension) — the render\n * resolver turns Media→url the same way it does for parsed media, so feed\n * images flow through one url path. */\n mediaFor: (uuid: string, type: string | undefined, name?: string) => Media | null;\n};\n\n/** A term matches a tag when they're equal OR differ only by a trailing `s`\n * (singular/plural) — Blux's server-side feed resolver stems this way, so a\n * `projects` filter also selects `project`-tagged media (7 real gallery tiles\n * that an exact match drops). Conservative: only a single trailing `s`, so it\n * never over-selects unrelated tags. */\nconst termMatchesTag = (term: string, tag: string): boolean =>\n term === tag ||\n (term.endsWith(\"s\") && term.slice(0, -1) === tag) ||\n (tag.endsWith(\"s\") && tag.slice(0, -1) === term);\n\n/** Parse a Blux tag filter expression into a predicate over a tag set. The\n * DSL: `&&` joins AND terms, `||` joins OR groups; a record matches when ANY\n * OR group has ALL its terms present (singular/plural-insensitive, see\n * `termMatchesTag`). Leading/empty terms (`&&metal&&sofa`) are ignored.\n * Case-insensitive. An empty/absent expression matches all. */\nexport function tagFilter(expr: string | undefined): (tags: string[]) => boolean {\n const groups = (expr ?? \"\")\n .split(\"||\")\n .map((g) =>\n g\n .split(\"&&\")\n .map((t) => t.trim().toLowerCase())\n .filter(Boolean),\n )\n .filter((g) => g.length > 0);\n if (!groups.length) return () => true;\n return (tags) => {\n const set = tags.map((t) => t.toLowerCase());\n return groups.some((g) => g.every((term) => set.some((tag) => termMatchesTag(term, tag))));\n };\n}\n\n/** Records sorted by a Blux `sort` key. `title` → by title with\n * `localeCompare` (matching Blux's own client sort, which uses\n * `((a.sort||\"\")+\"\").localeCompare(b.sort)` for non-numeric sort values);\n * `fdate`/`date` → by the record's date descending (newest first, Blux's\n * default), the undated last; anything else → source order preserved.\n * Stable. */\nfunction sortRecords(records: FeedRecord[], sort: string | undefined): FeedRecord[] {\n if (sort === \"title\") {\n return [...records].sort((a, b) =>\n String(a[\"title\"] ?? \"\").localeCompare(String(b[\"title\"] ?? \"\")),\n );\n }\n if (sort === \"fdate\" || sort === \"date\") {\n return [...records].sort((a, b) =>\n String(b[\"date\"] ?? \"\").localeCompare(String(a[\"date\"] ?? \"\")),\n );\n }\n return records;\n}\n\n/** A validated \"W:H\" crop ratio (e.g. \"4:3\") from a sourceConfig value, or\n * undefined when absent/malformed. Both terms must be positive numbers. */\nexport function cropRatioOf(v: unknown): string | undefined {\n if (typeof v !== \"string\") return undefined;\n const m = /^\\s*(\\d+(?:\\.\\d+)?)\\s*:\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(v);\n if (!m) return undefined;\n const w = Number(m[1]);\n const h = Number(m[2]);\n return w > 0 && h > 0 ? `${w}:${h}` : undefined;\n}\n\nconst isDisabled = (r: FeedRecord): boolean => r[\"disabled\"] === true || r[\"disable\"] === true;\nconst styleDisabled = (cfg: unknown): boolean =>\n !!cfg && typeof cfg === \"object\" && (cfg as { class?: string })[\"class\"] === \"disable\";\n\n/** Resolve a feed band's source into ordered tiles. `sources[0]` is either\n * `__media` (the media library, tiles = tag-matched images) or a feed id\n * (tiles = its records, filtered + sorted, expanded via the template config).\n * Returns null when the source is unknown or yields nothing. */\nexport function resolveFeedTiles(\n bandDef: {\n sources?: unknown;\n sourceConfig?: Record<string, unknown>;\n },\n resolvers: FeedResolvers,\n): FeedTile[] | null {\n const sources = Array.isArray(bandDef.sources) ? bandDef.sources.map(String) : [];\n const source = sources[0];\n if (!source) return null;\n const cfg = bandDef.sourceConfig ?? {};\n const filterExpr = (cfg[\"filters\"] as { tag?: string } | undefined)?.tag;\n const match = tagFilter(filterExpr);\n const sort = cfg[\"sort\"] as string | undefined;\n const bodyOff = styleDisabled(cfg[\"_body\"]);\n const titleOff = styleDisabled(cfg[\"_title\"]);\n // The grid's tile crop ratio (\"4:3\"): the render frames each tile image in a\n // fixed-aspect cover box (uniform gallery tiles, like the original).\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n const framed = (m: Media | null): Media | null =>\n m && ratio ? { ...m, cropRatio: ratio, fit: \"cover\" } : m;\n\n if (source === \"__media\") {\n // Media-library grid: every tag-matched image, sorted by the config (the\n // gallery/portfolio grids are `fdate` — newest first). Library entries DO\n // carry display text: `name` is a caption (a real title, not a filename)\n // and `description` the body — Blux binds both into the tile overlay\n // (unless _title/_body is disabled). These are PLAIN text, so escape them.\n const matched: FeedRecord[] = [];\n for (const [uuid, entry] of resolvers.media) {\n const type = String(entry[\"type\"] ?? \"\");\n if (!type.startsWith(\"image/\")) continue;\n if (!match((entry[\"tags\"] as string[] | undefined) ?? [])) continue;\n matched.push({ ...entry, __uuid: uuid });\n }\n const tiles = sortRecords(matched, sort)\n .map((entry): FeedTile => {\n const media = framed(\n resolvers.mediaFor(\n String(entry[\"__uuid\"]),\n entry[\"type\"] as string | undefined,\n entry[\"name\"] as string | undefined,\n ),\n );\n const tile: FeedTile = {};\n if (media) tile.media = media;\n if (!titleOff && entry[\"name\"]) tile.title = escapeHtml(String(entry[\"name\"]));\n if (!bodyOff && entry[\"description\"]) tile.body = plainToHtml(String(entry[\"description\"]));\n return tile;\n })\n .filter((t) => t.media || t.title || t.body);\n return tiles.length ? tiles : null;\n }\n\n const records = resolvers.feeds.get(source);\n if (!records) return null;\n const enabled = records.filter((r) => !isDisabled(r));\n const filtered = filterExpr\n ? enabled.filter((r) => match((r[\"tags\"] as string[]) ?? []))\n : enabled;\n const ordered = sortRecords(filtered, sort);\n const tiles = ordered.map((r): FeedTile => {\n const tile: FeedTile = {};\n const m = r[\"media\"] as { media?: string; type?: string } | undefined;\n if (m?.media) {\n const media = framed(resolvers.mediaFor(m.media, m.type));\n if (media) tile.media = media;\n }\n // Feed record title/body are stored as HTML (entities encoded, `<br>`\n // markup) — keep them VERBATIM, never re-escape.\n if (!titleOff && r[\"title\"]) tile.title = String(r[\"title\"]);\n if (!bodyOff && r[\"body\"]) tile.body = String(r[\"body\"]);\n return tile;\n });\n return tiles.length ? tiles : null;\n}\n\n/** An overlay tile treatment (Blux `layout: \"behind\"`, `overlay: true`): the\n * caption sits OVER the cropped image (a colored panel revealed on hover), so a\n * tile is only as tall as its image — not image + a caption row below. Threaded\n * onto the tile stack as `_overlay` presentation hints the render consumes. */\nexport type TileOverlay = { ratio: string; color?: string; valign?: string };\n\n/** A materialized feed grid: the band's heading (if any) over a row of tile\n * cells. Each tile is a stack of its image + title + body (whichever it has);\n * a lone image/heading stays bare. With `overlay`, the tile becomes an\n * overlay card (caption over the cropped image). `columns` sets each cell's\n * grid token so the render lays them out in a grid (the source `columns`/\n * `data-columns`; default 3). Returns the heading alone when there are no\n * tiles, null when neither. */\nexport function materializeFeedGrid(opts: {\n heading?: { html: string; level: number; role?: string };\n tiles: FeedTile[] | null;\n columns: number;\n spacing?: number;\n overlay?: TileOverlay;\n}): Node | null {\n const { heading, tiles, columns, spacing, overlay } = opts;\n const headingNode: Node | null = heading\n ? {\n kind: \"heading\",\n level: heading.level,\n html: heading.html,\n ...(heading.role ? { role: heading.role } : {}),\n }\n : null;\n if (!tiles || !tiles.length) return headingNode;\n\n const cols = Math.max(1, Math.round(columns));\n const cells: Cell[] = tiles.map((t) => {\n const parts: Node[] = [];\n if (t.media) parts.push({ kind: \"media\", media: t.media });\n // title/body are already render-ready HTML (feed records store HTML;\n // __media plain text was escaped at resolve time) — place them verbatim.\n if (t.title) parts.push({ kind: \"heading\", level: 6, html: t.title, role: \"text6\" });\n if (t.body) parts.push({ kind: \"body\", html: t.body });\n let node: Node;\n if (overlay && t.media) {\n // Overlay card: the stack carries the crop ratio + panel style; the\n // render cover-fills the media and overlays the caption on it.\n node = {\n kind: \"stack\",\n children: parts,\n style: {\n _overlay: overlay.ratio,\n ...(overlay.color ? { _overlayColor: overlay.color } : {}),\n ...(overlay.valign ? { _overlayValign: overlay.valign } : {}),\n },\n };\n } else {\n node = parts.length === 1 ? parts[0]! : { kind: \"stack\", children: parts };\n }\n return {\n token: {\n cols,\n raw: `grid-${cols}${spacing ? `-s${spacing}` : \"\"}`,\n ...(spacing ? { spacing } : {}),\n },\n node,\n };\n });\n const row: Node = { kind: \"row\", cells };\n return headingNode ? { kind: \"stack\", children: [headingNode, row] } : row;\n}\n\n/** HTML-escape a PLAIN-text value (a `__media` name/description) for placement\n * into an html-bearing node. Feed-record title/body are already HTML and skip\n * this. */\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\n/** A plain-text body (`__media` description) as body html: escaped, with the\n * source's hard newlines becoming `<br>` (Blux descriptions are multi-line —\n * \"DESIGN: …\\nPROCUREMENT: …\"), wrapped in a `<p>`. */\nfunction plainToHtml(s: string): string {\n const inner = escapeHtml(s.trim()).replace(/\\r?\\n/g, \"<br>\");\n return `<p>${inner}</p>`;\n}\n\n/** A file extension for a media asset: the mime map first (image/jpeg → jpg),\n * else the extension off the entry's own filename (`name`) — Blux names carry\n * the real extension, so an unmapped/absent mime (image/jpg, avif, heic, a\n * bare `custom`) still resolves instead of silently dropping the tile. null\n * only when neither yields an image extension. */\nexport function extFor(mime: string | undefined, name: string | undefined): string | null {\n const byMime: Record<string, string> = {\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/png\": \"png\",\n \"image/svg+xml\": \"svg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n \"image/avif\": \"avif\",\n \"image/bmp\": \"bmp\",\n \"image/tiff\": \"tiff\",\n };\n if (mime && byMime[mime]) return byMime[mime];\n const m = /\\.([a-z0-9]{2,5})$/i.exec(name ?? \"\");\n const ext = m?.[1]?.toLowerCase();\n const IMG = new Set([\"jpg\", \"jpeg\", \"png\", \"svg\", \"gif\", \"webp\", \"avif\", \"bmp\", \"tif\", \"tiff\"]);\n return ext && IMG.has(ext) ? (ext === \"jpeg\" ? \"jpg\" : ext) : null;\n}\n\n/** Is this a feed-driven band? — its site.json item declares `sources`. */\nexport function isFeedBand(\n item: unknown,\n): item is { sources: unknown[]; sourceConfig?: Record<string, unknown> } {\n return (\n !!item &&\n typeof item === \"object\" &&\n Array.isArray((item as { sources?: unknown }).sources) &&\n (item as { sources: unknown[] }).sources.length > 0\n );\n}\n\n/** The CDN base (`https://<host>/<siteId>/`) an export actually serves assets\n * from — scraped from a rendered `data-base` so the RIGHT host is used (Blux\n * spreads assets across two CDN hosts; a hardcoded host would 404 the other).\n * Falls back to the first known host + siteId when no data-base is present. */\nexport function feedAssetBase(htmls: string[], siteId: string): string {\n for (const html of htmls) {\n const m = /data-base=\"(https?:\\/\\/[^\"]+?\\/)\"/i.exec(html);\n if (m?.[1]) return m[1].replace(/^http:/, \"https:\");\n }\n return `https://${CDN_HOSTS[0]}/${siteId}/`;\n}\n\n/** Build the feed resolvers from a parsed site: feed records (site.json.feeds\n * carries them inline), the media library, and a Media builder that\n * reconstructs the CDN url (`<base><uuid>.<ext>` — the untransformed base the\n * export's own `data-base` uses, which resolves full-res). `base` comes from\n * `feedAssetBase` (the export's real host); `ext` from mime-or-filename. Pure. */\nexport function buildFeedResolvers(\n feeds: Record<string, { items?: FeedRecord[] } | undefined>,\n mediaLibrary: Record<string, FeedRecord>,\n base: string,\n): FeedResolvers {\n const feedMap = new Map<string, FeedRecord[]>();\n for (const [id, f] of Object.entries(feeds)) {\n if (Array.isArray(f?.items)) feedMap.set(id, f.items);\n }\n const mediaMap = new Map<string, FeedRecord>(Object.entries(mediaLibrary));\n return {\n feeds: feedMap,\n media: mediaMap,\n mediaFor: (uuid, type, name) => {\n const ext = extFor(type, name);\n if (!ext) return null;\n return { kind: \"image\", assetId: uuid, base, ext };\n },\n };\n}\n","// Products feed → a clean, materialized product catalog. Blux renders a detail\n// page per feed record at /products/<slug> from a Handlebars template; the\n// static export ships only the template, so this rebuilds the catalog\n// deterministically at convert time: canonical categories (the raw feed data is\n// dirty — whitespace/case variants + typos), the faithful url-or-derive slug,\n// and resolved main + gallery images. Pure; the convert injects `resolveImage`.\n\nexport type ProductImage = { assetId: string; url: string };\n\nexport type Product = {\n /** The detail-page slug: /products/<slug>. */\n slug: string;\n title: string;\n /** Canonical category (drives the back-link + faceting). */\n category: string;\n /** Canonical sub-category, or \"\" when the record has none. */\n subCategory: string;\n /** Rendered verbatim — the export's dimension strings are inconsistent\n * (`94\"W x 38\"D` vs `94\" W x 38\" D`) and that's what the live page shows. */\n dimensions: string;\n tags: string[];\n /** Hidden from listing grids, but Blux still serves its detail page (200), so\n * the page is generated regardless. */\n disabled: boolean;\n /** The main image, when the record has one (39/552 have none). */\n image?: ProductImage;\n /** Additional images shown as detail-page thumbnails (160/552 have ≥1). */\n gallery: ProductImage[];\n};\n\nexport type ProductRecord = {\n title?: unknown;\n category?: unknown;\n sub_category?: unknown;\n dimensions?: unknown;\n tags?: unknown;\n disabled?: unknown;\n /** Present on only a handful of records; when set it is the authoritative\n * slug (and can override what the title would derive). */\n url?: unknown;\n media?: unknown;\n items?: unknown;\n};\n\n// The five product categories the site publishes. Upholstered/Case/Exterior are\n// populated by the feed; Metal/Finishes are listing pages with no feed records.\nconst CANONICAL_CATEGORIES = [\"Upholstered\", \"Case\", \"Exterior\", \"Metal\", \"Finishes\"];\n\n// Misspellings in the raw data that don't fold by case/whitespace alone.\nconst CATEGORY_ALIASES: Record<string, string> = {\n upholstrered: \"Upholstered\",\n upholsered: \"Upholstered\",\n};\n\n// Sub-category variants that need explicit folding (order/wording/typos).\nconst SUBCATEGORY_ALIASES: Record<string, string> = {\n banuette: \"Banquette\",\n \"benches & ottomans\": \"Ottomans & Benches\",\n miscellaneous: \"Misc.\",\n};\n\nfunction cleanWs(s: unknown): string {\n return String(s ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction titleCase(s: string): string {\n return s.replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Canonicalize a raw product category. Folds whitespace/case variants and\n * known typos onto the canonical set; an unrecognized category is kept\n * (title-cased) rather than dropped, so no product is silently lost. */\nexport function normalizeCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (CATEGORY_ALIASES[lower]) return CATEGORY_ALIASES[lower];\n const canon = CANONICAL_CATEGORIES.find((c) => c.toLowerCase() === lower);\n return canon ?? titleCase(lower);\n}\n\n/** Canonicalize a raw sub-category (own facet; folds typos/word-order). \"\" when\n * the record has none. */\nexport function normalizeSubCategory(raw: unknown): string {\n const cleaned = cleanWs(raw);\n if (!cleaned) return \"\";\n const lower = cleaned.toLowerCase();\n if (SUBCATEGORY_ALIASES[lower]) return SUBCATEGORY_ALIASES[lower];\n return titleCase(lower);\n}\n\n/** The detail-page slug for a record: the stored `url` wins (a few records\n * carry an editorial slug the title wouldn't derive — \"Howdy Set\" → howdyset),\n * else derive from the title (lowercase, non-alphanumeric runs → single \"-\",\n * trimmed). */\nexport function productSlug(record: ProductRecord): string {\n const url = typeof record.url === \"string\" ? record.url.trim() : \"\";\n if (url) return url.replace(/^\\/+products\\/+/i, \"\").replace(/^\\/+|\\/+$/g, \"\");\n return String(record.title ?? \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction mediaUuid(media: unknown): string | undefined {\n if (media && typeof media === \"object\") {\n const m = (media as { media?: unknown }).media;\n if (typeof m === \"string\" && m) return m;\n }\n return undefined;\n}\n\nfunction imageFor(\n uuid: string | undefined,\n resolveImage: (uuid: string) => string | null,\n): ProductImage | undefined {\n if (!uuid) return undefined;\n const url = resolveImage(uuid);\n return url ? { assetId: uuid, url } : undefined;\n}\n\n/** Build the product catalog from the feed records. `resolveImage` turns an\n * asset uuid into a CDN url (null when unresolvable). Slug-collision safe: two\n * records that map to the same slug (a handful of duplicate/legacy names) keep\n * ONE page — an enabled record wins over a disabled one, else the first seen.\n * Records with no usable slug are dropped. */\nexport function materializeProducts(\n records: ProductRecord[],\n resolveImage: (uuid: string) => string | null,\n): Product[] {\n const bySlug = new Map<string, Product>();\n for (const r of records) {\n const slug = productSlug(r);\n if (!slug) continue;\n\n const gallery: ProductImage[] = [];\n if (Array.isArray(r.items)) {\n for (const it of r.items as ProductRecord[]) {\n const img = imageFor(mediaUuid(it?.media), resolveImage);\n if (img) gallery.push(img);\n }\n }\n\n const image = imageFor(mediaUuid(r.media), resolveImage);\n const product: Product = {\n slug,\n title: cleanWs(r.title),\n category: normalizeCategory(r.category),\n subCategory: normalizeSubCategory(r.sub_category),\n dimensions: String(r.dimensions ?? \"\"),\n tags: Array.isArray(r.tags) ? r.tags.filter((t): t is string => typeof t === \"string\") : [],\n disabled: r.disabled === true,\n gallery,\n // `exactOptionalPropertyTypes`: omit the key rather than set undefined.\n ...(image ? { image } : {}),\n };\n\n const existing = bySlug.get(slug);\n if (!existing || (existing.disabled && !product.disabled)) bySlug.set(slug, product);\n }\n return [...bySlug.values()];\n}\n","import { cleanCssValue } from \"../normalize.js\";\n\n/** Per-band block styles (background-color, min-height, text-align, …) sourced\n * from site.json's top-level page blocks, cleaned exactly as normalize.ts does.\n * Keyed by the block's render-order index, which equals the grid `Band.index`\n * (the `page-block-N` number) for a contiguously-rendered page.\n *\n * site.json's page blocks live at `content.pages[0].items` (see\n * `parseBluxSite` in parse.ts, which reads `j.content.pages`, and `BluxPage`,\n * whose blocks field is `items`) — not `pages[0].blocks` as a naive reading\n * of the export's top level might suggest. */\nexport function blockStylesByIndex(\n siteJson: unknown,\n pageIndex = 0,\n): Map<number, Record<string, string>> {\n const out = new Map<number, Record<string, string>>();\n const blocks = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content?.pages?.[\n pageIndex\n ]?.items;\n if (!Array.isArray(blocks)) return out;\n blocks.forEach((b, i) => {\n const styles = (b as { styles?: Record<string, unknown> })?.styles ?? {};\n const cleaned: Record<string, string> = {};\n for (const [k, v] of Object.entries(styles)) {\n const c = cleanCssValue(v);\n if (c) cleaned[k] = c;\n }\n if (Object.keys(cleaned).length) out.set(i, cleaned);\n });\n return out;\n}\n\n/** A block class's `.blocksNcontainer` defaults: the content padding (with its\n * `__media_mobile_padding` responsive override) and max-width that apply when a\n * block's own styles omit them. */\nexport type BlockDefaults = {\n padding?: string;\n mobilePadding?: string;\n maxWidth?: string;\n};\n\nconst CONTAINER_RE = /^\\.(blocks\\d+)container$/;\n\n/** Per-block-class container defaults keyed by the bare class (\"blocks0\",\n * \"blocks2\", …), sourced from site.json's `styles.blocks` — a position-stable\n * array of `{ _label, \".blocksN\": …, \".blocksNcontainer\": … }` slots. Only the\n * `.blocksNcontainer` entries matter here; the head `<style>` is never parsed\n * (styles.blocks is its structured source). Values are cleaned exactly as\n * `blockStylesByIndex` cleans them, so the export's malformed tombstones\n * (`padding: \"px\"`, empty strings) drop instead of leaking. */\nexport function blockClassDefaults(siteJson: unknown): Map<string, BlockDefaults> {\n const out = new Map<string, BlockDefaults>();\n const blocks = (siteJson as { styles?: { blocks?: unknown[] } })?.styles?.blocks;\n if (!Array.isArray(blocks)) return out;\n for (const slot of blocks) {\n if (slot === null || typeof slot !== \"object\") continue;\n for (const [selector, css] of Object.entries(slot as Record<string, unknown>)) {\n const blockClass = CONTAINER_RE.exec(selector)?.[1];\n if (!blockClass || css === null || typeof css !== \"object\") continue;\n const rec = css as Record<string, unknown>;\n const padding = cleanCssValue(rec[\"padding\"]);\n const mobilePadding = cleanCssValue(rec[\"__media_mobile_padding\"]);\n const maxWidth = cleanCssValue(rec[\"max-width\"]);\n const defaults: BlockDefaults = {\n ...(padding ? { padding } : {}),\n ...(mobilePadding ? { mobilePadding } : {}),\n ...(maxWidth ? { maxWidth } : {}),\n };\n if (Object.keys(defaults).length) out.set(blockClass, defaults);\n }\n }\n return out;\n}\n","import { blockPlainText, type SliceSpec } from \"../grid/index.js\";\nimport { type PlanSlice, richText } from \"./plan.js\";\n\n/** Strip all tags → the plain text a Prismic \"Text\" (key-text) field holds. */\nfunction stripTags(html: string): string {\n return html\n .replace(/<[^>]*>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Map one classified band to its page-doc slice. Text + band index only —\n * media, layout, style and map all live in the presentation manifest. */\nexport function sliceSpecToPlanSlice(spec: SliceSpec): PlanSlice {\n switch (spec.slice) {\n case \"Hero\":\n return {\n slice_type: \"hero\",\n variation: \"band\",\n items: [],\n primary: {\n band: spec.index,\n ...(spec.heading ? { heading: spec.heading } : {}),\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n ...(spec.body ? { body: stripTags(spec.body) } : {}),\n },\n };\n case \"TitleBand\":\n return {\n slice_type: \"title_band\",\n variation: \"default\",\n items: [],\n primary: {\n band: spec.index,\n heading: spec.heading,\n ...(spec.subtitle ? { subtitle: spec.subtitle } : {}),\n },\n };\n case \"RichText\":\n return {\n slice_type: \"rich_text\",\n variation: \"default\",\n items: [],\n primary: { content: richText(spec.html), band: spec.index },\n };\n case \"SplitFeature\":\n return {\n slice_type: \"split_feature\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Gallery\":\n return {\n slice_type: \"gallery\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Carousel\":\n // One item per slide, in slide order (the render zips by index); an\n // uncaptioned slide contributes {} so the alignment holds. Captions are\n // heading nodes, so blockPlainText (entities decoded, <br> kept as a\n // newline) — same as the Hero/TitleBand heading path. A hero slide's\n // secondary line (location) rides as `subcaption`.\n return {\n slice_type: \"carousel\",\n variation: \"default\",\n items: spec.slides.map((s) => ({\n ...(s.caption ? { caption: blockPlainText(s.caption.html) } : {}),\n ...(s.subcaption ? { subcaption: blockPlainText(s.subcaption.html) } : {}),\n })),\n primary: { band: spec.index },\n };\n case \"MediaFull\":\n case \"VideoFeature\":\n return {\n slice_type: \"media_full\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"LocationMap\":\n return {\n slice_type: \"location_map\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n case \"Grid\":\n return {\n slice_type: \"grid_band\",\n variation: \"default\",\n items: [],\n primary: { band: spec.index },\n };\n }\n}\n","import { type SliceSpec, type Media, collectMedia } from \"../grid/index.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { buildCustomType } from \"./custom-types.js\";\nimport { sliceSpecToPlanSlice } from \"./grid-slice.js\";\nimport { type MigrationPlan, type PlanAsset, type PlanDocument, richText } from \"./plan.js\";\n\n/** Build the CDN url for a media from its parser-captured base + uuid + ext.\n * Null when the node carried no `data-base` (the manifest resolver then falls\n * back to the IR asset's sourceUrl — see `buildGridPlan`). Exported so the\n * `blux convert` manifest resolver builds byte-identical urls (a later task\n * rewrites the manifest by matching plan.assets urls). */\nexport function mediaCdnUrl(m: Media): string | null {\n return m.base ? `${m.base}${m.assetId}${m.ext ? `.${m.ext}` : \"\"}` : null;\n}\n\n/** The absolute source url for a media: the parser-captured CDN url\n * (data-base + uuid + ext) if present, else the IR asset's scraped sourceUrl,\n * else null. Shared by the plan's asset list and the manifest resolver so the\n * two url strings are byte-identical (the migrate rewrite keys on that). */\nexport function mediaUrl(\n m: Media,\n sourceUrlById: Map<string, string | null | undefined>,\n): string | null {\n return mediaCdnUrl(m) ?? sourceUrlById.get(m.assetId) ?? null;\n}\n\n/** Every Media referenced across all specs: band backgrounds, direct media\n * fields, and media inside node trees (SplitFeature.text / Grid.root). Deduped\n * by assetId, first occurrence wins, insertion order preserved. `resolve`\n * turns a media into its upload entry (CDN-base url, else IR sourceUrl) or null\n * when neither is available — an unresolvable media is dropped and (if a\n * `diagnostics` sink is passed) recorded once per assetId. */\nexport function collectPlanAssets(\n specs: SliceSpec[],\n resolve: (m: Media) => PlanAsset | null,\n diagnostics?: Diagnostic[],\n): PlanAsset[] {\n const byId = new Map<string, PlanAsset>();\n const seen = new Set<string>();\n const add = (m: Media) => {\n if (seen.has(m.assetId)) return;\n seen.add(m.assetId);\n const asset = resolve(m);\n if (asset) byId.set(m.assetId, asset);\n else\n diagnostics?.push({\n kind: \"unresolved-asset\",\n where: m.assetId,\n message: `media ${m.assetId} has no CDN base nor IR source url — not uploaded`,\n });\n };\n for (const spec of specs) {\n if (spec.background) add(spec.background);\n switch (spec.slice) {\n case \"Gallery\":\n spec.media.forEach(add);\n break;\n case \"Carousel\":\n spec.slides.forEach((s) => add(s.media));\n break;\n case \"MediaFull\":\n case \"VideoFeature\":\n add(spec.media);\n break;\n case \"SplitFeature\":\n add(spec.media);\n collectMedia(spec.text).forEach(add);\n break;\n case \"Grid\":\n collectMedia(spec.root).forEach(add);\n break;\n default:\n break; // Hero/TitleBand/RichText/LocationMap: only background (handled above)\n }\n }\n return [...byId.values()];\n}\n\n/** Build the Prismic migration plan for a grid-converted site: one text-only\n * page document + the assets its manifest references (uploaded so the manifest\n * can be rewritten to Prismic urls at migrate time). Collections flow through\n * `buildCustomType`, unchanged from archetype. */\nexport function buildGridPlan(specs: SliceSpec[], ir: SiteIR): MigrationPlan {\n const page = ir.pages[0];\n return buildGridSitePlan(\n [{ uid: page?.uid ?? \"home\", title: page?.title ?? page?.uid ?? \"home\", specs }],\n ir,\n );\n}\n\n/** The multi-page grid plan: one page document per converted page (uid-keyed,\n * mirroring the archetype plan's per-page loop), the asset union across all\n * pages' specs, and the collections' custom types. */\nexport function buildGridSitePlan(\n pages: { uid: string; title: string; specs: SliceSpec[] }[],\n ir: SiteIR,\n): MigrationPlan {\n const documents: PlanDocument[] = pages.map((p) => ({\n type: \"page\",\n uid: p.uid,\n data: { title: richText(`<h1>${p.title}</h1>`), slices: p.specs.map(sliceSpecToPlanSlice) },\n }));\n // Upload-url resolution MUST stay identical to the later manifest resolver:\n // CDN-base url first, else the IR asset's sourceUrl. `mediaUrl` is the\n // single shared implementation (see its doc comment above).\n const assetById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const resolve = (m: Media): PlanAsset | null => {\n const asset = assetById.get(m.assetId);\n const url = mediaUrl(m, sourceUrlById);\n return url ? { id: m.assetId, url, alt: asset?.alt ?? \"\" } : null;\n };\n const diagnostics: Diagnostic[] = [...(ir.diagnostics ?? [])];\n const assets = collectPlanAssets(\n pages.flatMap((p) => p.specs),\n resolve,\n diagnostics,\n );\n const customTypes = ir.collections.map(buildCustomType);\n return { customTypes, documents, assets, stylesManifest: [], diagnostics };\n}\n","// Render-faithful presentation manifest (plan 5). Transforms the source\n// node tree (src/blux/grid/types.ts `Node`) and the classified `SliceSpec[]`\n// into `blux-presentation.json`, the render-side contract the-pointe's\n// Svelte layer loads (src/lib/blux/presentation.ts — that file is the fixed\n// target; these mirror types must match it exactly). Media/style/map are\n// resolved via injected `deps` so this builder stays pure and offline.\nimport type {\n Media,\n Node,\n GridToken as SrcToken,\n SliceSpec,\n VideoPlayback,\n} from \"../grid/index.js\";\nimport type { BlockDefaults } from \"./block-styles.js\";\n\n// ---------------------------------------------------------------------------\n// Render-side mirror types (must match the-pointe's src/lib/blux/presentation.ts)\n// ---------------------------------------------------------------------------\n\nexport type RenderMedia = {\n kind: \"image\" | \"video\";\n url: string;\n alt?: string;\n // Intrinsic sizing carried from the source. `width`/`aspect` size a foreground\n // graphic (capped to its cell) so rules/logos keep their true size. `fit` +\n // `position` carry a band background's `background-size`/`-position` so a\n // corner-anchored `auto` accent isn't centered full-bleed. `playback` carries a\n // video's source `<video>` attributes. Mirrors the-pointe's src/lib/blux/presentation.ts.\n width?: number;\n aspect?: number;\n fit?: \"contain\" | \"cover\" | \"auto\";\n position?: string;\n /** The source holder's inline min-height (e.g. \"80vh\" on slider slides), so\n * a cover-frame carousel reserves the original's height. */\n minHeight?: string;\n /** A feed grid's tile crop ratio (\"W:H\", e.g. \"4:3\") — the render frames the\n * image in a fixed-aspect object-cover box (uniform gallery tiles). */\n cropRatio?: string;\n playback?: VideoPlayback;\n};\n\nexport type RenderToken = { cols: number | \"any\"; ratio?: number; spacing?: number };\n\nexport type RenderNode =\n | {\n kind: \"row\";\n cells: RenderCell[];\n style?: Record<string, string>;\n /** This row's cells are the map widget's toggle-switched content panels\n * (the Blux clickMap wiring): cell i shows when toggle i is active, the\n * rest are hidden. Set only on the row that directly follows a widget:map\n * sibling and has exactly one cell per map toggle. */\n panels?: boolean;\n }\n | { kind: \"stack\"; children: RenderNode[]; style?: Record<string, string> }\n | {\n kind: \"heading\";\n level: number;\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"body\";\n html: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | {\n kind: \"subtitle\";\n text: string;\n role?: string;\n /** Inline deviations the export carries on the text leaf (color, padding)\n * plus decoded margin utilities (margin-20r → margin-right:20%). The\n * margin-right percentage is desktop-only in the source (reset ≤800px) —\n * the render scopes it to md+. */\n style?: Record<string, string>;\n }\n | { kind: \"media\"; media: RenderMedia }\n | { kind: \"raw\"; html: string }\n | { kind: \"widget\"; widget: { type: \"map\" } };\n\nexport type RenderCell = { token: RenderToken; node: RenderNode };\n\nexport type MapLayer = {\n name: string;\n lid: string;\n initiallyVisible: boolean;\n preserveViewport: boolean;\n};\nexport type MapToggle = { label: string; layers: string[]; panelIndex: number };\nexport type MapRenderConfig = {\n mid: string;\n layers: MapLayer[];\n toggles: MapToggle[];\n styles: unknown[];\n center?: { lat: number; lng: number };\n zoom?: number;\n height?: string;\n defaultToggle?: number;\n};\n\nexport type BandPresentation = {\n style?: Record<string, string>;\n background?: RenderMedia;\n tree?: RenderNode;\n split?: { mediaSide: \"left\" | \"right\"; ratio: number; media: RenderMedia; text: RenderNode };\n gallery?: RenderMedia[];\n /** Carousel payload: the band is a source slider (.caslider). Caption TEXT\n * lives in the page doc's items (Prismic-editable); the manifest carries the\n * media and the caption's role metadata. `columns` = slides visible at once\n * (source data-columns). */\n carousel?: {\n slides: {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n }[];\n columns?: number;\n };\n media?: RenderMedia;\n map?: MapRenderConfig;\n /** Hero/TitleBand heading textN role + h-level and subtitle role, so the\n * render applies the right display font/tag. The text itself is the Prismic\n * page-doc string; this is presentation metadata only. */\n text?: { headingRole?: string; headingLevel?: number; subtitleRole?: string };\n};\n\nexport type Presentation = { bands: Record<string, BandPresentation> };\n\n/** A whole-site manifest: per-page band manifests keyed by page uid. Band\n * indices are page-local (page-block-N restarts at 0 on every page), so a flat\n * bands map would collide across pages. `blux convert` writes this shape;\n * single-page consumers select a page with `selectPagePresentation`. The\n * render mirror is reddoor-starter's `SitePresentation`. */\nexport type SitePresentation = Presentation | { pages: Record<string, Presentation> };\n\n/** One page's slice of a site manifest (the flat form passes through). The\n * `&& m.pages` guard tolerates a hand-edited `{ pages: undefined }`; the flat\n * branch is a `Presentation` by construction (cast bridges the union gap). */\nexport function selectPagePresentation(m: SitePresentation, uid = \"home\"): Presentation {\n if (\"pages\" in m && m.pages) return m.pages[uid] ?? { bands: {} };\n return m as Presentation;\n}\n\nexport type PresentationDeps = {\n resolveMedia: (media: Media) => RenderMedia | null;\n styleFor: (index: number) => Record<string, string> | undefined;\n /** The `.blocksNcontainer` class defaults for a band's `blockClass`\n * (see `blockClassDefaults`) — filled into `BandPresentation.style` only\n * where the block's own styles omit the key. */\n defaultsFor: (blockClass: string) => BlockDefaults | undefined;\n map?: MapRenderConfig | null;\n};\n\n// ---------------------------------------------------------------------------\n// Node-tree serializer: source Node → RenderNode\n// ---------------------------------------------------------------------------\n\n/** Drop the source-only `raw` field from a grid token; keep only cols/ratio/spacing. */\nfunction renderToken(t: SrcToken): RenderToken {\n return {\n cols: t.cols,\n ...(t.ratio !== undefined ? { ratio: t.ratio } : {}),\n ...(t.spacing !== undefined ? { spacing: t.spacing } : {}),\n };\n}\n\n/** Recursively serialize a source Node → RenderNode: resolve media (dropping\n * unresolved media nodes), strip token.raw. Never mutates the input. */\nfunction renderNode(node: Node, resolve: PresentationDeps[\"resolveMedia\"]): RenderNode | null {\n switch (node.kind) {\n case \"row\": {\n const cells: RenderCell[] = [];\n for (const c of node.cells) {\n const rn = renderNode(c.node, resolve);\n if (rn) cells.push({ token: renderToken(c.token), node: rn });\n }\n return { kind: \"row\", cells, ...(node.style ? { style: node.style } : {}) };\n }\n case \"stack\": {\n const children: RenderNode[] = [];\n for (const c of node.children) {\n const rn = renderNode(c, resolve);\n if (rn) children.push(rn);\n }\n return { kind: \"stack\", children, ...(node.style ? { style: node.style } : {}) };\n }\n case \"heading\":\n return {\n kind: \"heading\",\n level: node.level,\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"body\":\n return {\n kind: \"body\",\n html: node.html,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"subtitle\":\n return {\n kind: \"subtitle\",\n text: node.text,\n ...(node.role ? { role: node.role } : {}),\n ...(node.style ? { style: node.style } : {}),\n };\n case \"media\": {\n const m = resolve(node.media);\n return m ? { kind: \"media\", media: m } : null; // drop unresolved media\n }\n case \"raw\":\n return { kind: \"raw\", html: node.html };\n case \"widget\":\n return { kind: \"widget\", widget: node.widget };\n }\n}\n\n/** Does a (source) node tree contain a map widget anywhere? */\nexport function hasMapWidget(node: Node): boolean {\n if (node.kind === \"widget\") return node.widget.type === \"map\";\n if (node.kind === \"row\") return node.cells.some((c) => hasMapWidget(c.node));\n if (node.kind === \"stack\") return node.children.some(hasMapWidget);\n return false;\n}\n\n/** Mark the map widget's content-panel row: in the Blux export the clickMap\n * widget switches the area below the map between N sibling panels (the address\n * grid + lazy logo strips on the-pointe), one per toggle. Structurally that is\n * a row directly following the widget:map inside a stack, with exactly one cell\n * per toggle — the marker lets the render show only the active toggle's panel\n * instead of stacking all of them. Anything else is left untouched. */\nexport function markPanelRows(node: RenderNode, toggleCount: number): RenderNode {\n if (node.kind === \"row\") {\n return {\n ...node,\n cells: node.cells.map((c) => ({ ...c, node: markPanelRows(c.node, toggleCount) })),\n };\n }\n if (node.kind === \"stack\") {\n const children = node.children.map((c) => markPanelRows(c, toggleCount));\n for (let i = 0; i + 1 < children.length; i++) {\n const cur = children[i];\n const next = children[i + 1];\n if (\n cur?.kind === \"widget\" &&\n cur.widget.type === \"map\" &&\n next?.kind === \"row\" &&\n next.cells.length === toggleCount\n ) {\n children[i + 1] = { ...next, panels: true };\n }\n }\n return { ...node, children };\n }\n return node;\n}\n\n// ---------------------------------------------------------------------------\n// Per-variant builder: SliceSpec[] → Presentation\n// ---------------------------------------------------------------------------\n\nexport function buildPresentation(specs: SliceSpec[], deps: PresentationDeps): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const spec of specs) {\n const bp: BandPresentation = {};\n const style = deps.styleFor(spec.index);\n if (style) bp.style = style;\n // Class-default padding/max-width — for EVERY slice type (TitleBand/Hero\n // bands need their band padding too), filling only the keys the block's\n // own styles omit. The trigger is \"no `_contentPadding` in the block's\n // styles\", never \"no style record\" (a block can style other things and\n // still rely on the class padding). `_contentPaddingMobile` only ever\n // pairs with a filled default: a block's own padding has no mobile twin.\n const defaults = spec.blockClass ? deps.defaultsFor(spec.blockClass) : undefined;\n if (defaults) {\n const own = bp.style ?? {};\n const fill: Record<string, string> = {};\n if (own[\"_contentPadding\"] === undefined && defaults.padding) {\n fill[\"_contentPadding\"] = defaults.padding;\n if (defaults.mobilePadding) fill[\"_contentPaddingMobile\"] = defaults.mobilePadding;\n }\n if (own[\"_max-content-width\"] === undefined && defaults.maxWidth) {\n fill[\"_max-content-width\"] = defaults.maxWidth;\n }\n // Copy, never mutate — the styleFor record may be shared/cached.\n if (Object.keys(fill).length) bp.style = { ...own, ...fill };\n }\n if (spec.background) {\n const bg = deps.resolveMedia(spec.background);\n if (bg) bp.background = bg;\n }\n\n switch (spec.slice) {\n case \"Hero\":\n case \"TitleBand\": {\n // Text lives in the page doc; carry only the role/level metadata so the\n // render picks the right display font + heading tag.\n const meta = {\n ...(spec.headingRole ? { headingRole: spec.headingRole } : {}),\n ...(spec.headingLevel !== undefined ? { headingLevel: spec.headingLevel } : {}),\n ...(spec.subtitleRole ? { subtitleRole: spec.subtitleRole } : {}),\n };\n if (Object.keys(meta).length) bp.text = meta;\n break;\n }\n case \"RichText\":\n break; // content is in the page doc\n case \"Gallery\": {\n const g = spec.media.map(deps.resolveMedia).filter((m): m is RenderMedia => m !== null);\n if (g.length) bp.gallery = g;\n break;\n }\n case \"Carousel\": {\n // Caption TEXT lives in the page doc's items; only its role metadata\n // rides the manifest. An unresolved media TRUNCATES the slide list —\n // splicing it out would shift later slides onto the wrong page-doc\n // caption (the render zips items↔slides by index). Either way the\n // count shrink trips validateLayout's media-dropped finding.\n type CarouselManifestSlide = {\n media: RenderMedia;\n caption?: { level?: number; role?: string };\n subcaption?: { role?: string };\n };\n const slides: CarouselManifestSlide[] = [];\n for (const s of spec.slides) {\n const media = deps.resolveMedia(s.media);\n if (!media) break;\n const slide: CarouselManifestSlide = { media };\n if (s.caption) {\n const caption: { level?: number; role?: string } = { level: s.caption.level };\n if (s.caption.role !== undefined) caption.role = s.caption.role;\n slide.caption = caption;\n }\n if (s.subcaption) {\n slide.subcaption = s.subcaption.role !== undefined ? { role: s.subcaption.role } : {};\n }\n slides.push(slide);\n }\n if (slides.length > 0) {\n bp.carousel = spec.columns !== undefined ? { slides, columns: spec.columns } : { slides };\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n const m = deps.resolveMedia(spec.media);\n if (m) bp.media = m;\n break;\n }\n case \"SplitFeature\": {\n const media = deps.resolveMedia(spec.media);\n const text = renderNode(spec.text, deps.resolveMedia);\n if (media && text) bp.split = { mediaSide: spec.mediaSide, ratio: spec.ratio, media, text };\n break;\n }\n case \"LocationMap\":\n if (deps.map) bp.map = deps.map;\n break;\n case \"Grid\": {\n const tree = renderNode(spec.root, deps.resolveMedia);\n // Co-located map (widget:map inside the tree): attach the map config and\n // mark the toggle-switched panel row so the render can wire them up.\n const co = deps.map && hasMapWidget(spec.root) ? deps.map : null;\n if (co) bp.map = co;\n if (tree) bp.tree = co ? markPanelRows(tree, co.toggles.length) : tree;\n break;\n }\n }\n bands[String(spec.index)] = bp;\n }\n return { bands };\n}\n","import {\n parseGridBands,\n extractMapConfig,\n classifyBands,\n makeIsMapMount,\n type Band,\n type MapConfig,\n type SliceSpec,\n} from \"../grid/index.js\";\nimport { assembleIR } from \"../assemble.js\";\nimport { parseBluxSite } from \"../parse.js\";\nimport { normalizePages } from \"../normalize.js\";\nimport {\n buildFeedResolvers,\n cropRatioOf,\n feedAssetBase,\n isFeedBand,\n materializeFeedGrid,\n resolveFeedTiles,\n type FeedResolvers,\n type TileOverlay,\n} from \"../grid/feed-grid.js\";\nimport type { Node } from \"../grid/types.js\";\nimport type { Diagnostic, SiteIR } from \"../ir.js\";\nimport { blockClassDefaults, blockStylesByIndex } from \"./block-styles.js\";\nimport { buildGridPlan, buildGridSitePlan, mediaUrl } from \"./grid-plan.js\";\nimport type { MigrationPlan } from \"./plan.js\";\nimport {\n buildPresentation,\n type MapRenderConfig,\n type Presentation,\n type PresentationDeps,\n type RenderMedia,\n} from \"./presentation.js\";\n\nexport type ConvertResult = {\n bands: Band[];\n specs: SliceSpec[];\n ir: SiteIR;\n mapConfig: MapConfig | null;\n plan: MigrationPlan;\n presentation: Presentation;\n};\n\n/** Drop the source-only `mountId` from an extracted MapConfig → the render-side\n * MapRenderConfig the presentation manifest carries. */\nexport function mapRenderFromConfig(c: MapConfig): MapRenderConfig {\n return {\n mid: c.mid,\n layers: c.layers,\n toggles: c.toggles,\n styles: c.styles,\n ...(c.center ? { center: c.center } : {}),\n ...(c.zoom !== undefined ? { zoom: c.zoom } : {}),\n ...(c.height ? { height: c.height } : {}),\n ...(c.defaultToggle !== undefined ? { defaultToggle: c.defaultToggle } : {}),\n };\n}\n\n/** The offline convert pipeline shared by `blux convert` (writes files) and\n * `blux validate` (checks fidelity): parse + classify index.html, assemble the\n * IR from site.json, and build both emit artifacts through a single media\n * resolver (CDN base ?? IR sourceUrl) so plan, manifest, and validation all\n * agree on which media resolve. Pure + offline — no writes, no network. */\nexport function convertExport({\n html,\n siteJson,\n}: {\n html: string;\n siteJson: unknown;\n}): ConvertResult {\n const bands = parseGridBands(html);\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const ir = assembleIR({ siteJson, htmls: [html] });\n\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const styles = blockStylesByIndex(siteJson);\n const defaults = blockClassDefaults(siteJson);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n\n const plan = buildGridPlan(specs, ir);\n const presentation = buildPresentation(specs, deps);\n return { bands, specs, ir, mapConfig, plan, presentation };\n}\n\n/** The leading heading/subtitle nodes of a parsed band root — a feed band's\n * real heading survives the parse (only the tile template was dropped), so we\n * keep it and append the materialized tile row below. */\nfunction leadingHeadings(root: Node): Node[] {\n if (root.kind === \"heading\" || root.kind === \"subtitle\") return [root];\n if (root.kind === \"stack\") {\n const out: Node[] = [];\n for (const c of root.children) {\n if (c.kind === \"heading\" || c.kind === \"subtitle\") out.push(c);\n else break; // headings lead; stop at the first non-heading (the dropped grid)\n }\n return out;\n }\n return [];\n}\n\n/** A feed band's grid column count: the source `columns` (site.json), else a\n * sensible default. */\nfunction feedColumns(item: { columns?: unknown }): number {\n const n = Number(item.columns);\n return Number.isFinite(n) && n > 0 ? n : 3;\n}\n\n/** The overlay-tile treatment of a feed band, or null. A grid with\n * `sourceConfig.overlay` + a crop `ratio`/`mediaRatio` (gallery/portfolio)\n * shows its caption OVER the cropped image — the render frames each tile at\n * the ratio and reveals the caption panel on hover, so a tile is only as tall\n * as its image. */\nfunction tileOverlay(item: unknown): TileOverlay | null {\n const cfg = (item as { sourceConfig?: Record<string, unknown> })?.sourceConfig;\n if (!cfg || cfg[\"overlay\"] !== true) return null;\n const ratio = cropRatioOf(cfg[\"mediaRatio\"] ?? cfg[\"ratio\"]);\n if (!ratio) return null;\n const color = typeof cfg[\"overlayColor\"] === \"string\" ? cfg[\"overlayColor\"] : undefined;\n const valign = typeof cfg[\"contentvalign\"] === \"string\" ? cfg[\"contentvalign\"] : undefined;\n return { ratio, ...(color ? { color } : {}), ...(valign ? { valign } : {}) };\n}\n\n/** Did this band lose its content to the feed-template drop? — its parsed root\n * is empty (just heading(s)/subtitle/empty-raw, no media and no populated\n * row). Only such a band is a safe materialization target: a band that already\n * parsed real content is NOT a JS-hydrated feed grid, so the positional\n * site.json join landed on the wrong band and must not clobber it. */\nfunction isEmptyish(root: Node): boolean {\n switch (root.kind) {\n case \"heading\":\n case \"subtitle\":\n return true;\n case \"raw\":\n return root.html.trim() === \"\";\n case \"media\":\n case \"widget\":\n return false;\n case \"row\":\n return root.cells.length === 0;\n case \"stack\":\n return root.children.every(isEmptyish);\n default:\n return false;\n }\n}\n\n/** Replace each feed band's root (parsed to just its heading, the tile\n * template having been dropped) with the heading over a materialized tile row\n * rebuilt from the feed records. Feed bands whose source resolves to no tiles\n * keep their heading and get an `empty-feed-grid` diagnostic. Mutates `bands`.\n * The join is positional: site.json `items[i]` ↔ the band whose index is `i`\n * (the block-styles convention); a page with non-contiguous band ids simply\n * finds no item and is left as parsed. */\nfunction materializeFeedBands(\n bands: Band[],\n pageItems: unknown[] | undefined,\n resolvers: FeedResolvers,\n diagnostics: Diagnostic[],\n): void {\n if (!Array.isArray(pageItems)) return;\n for (const band of bands) {\n const item = pageItems[band.index];\n if (!isFeedBand(item)) continue;\n // Guard the positional join: only a band that lost its content to the\n // template drop is a real feed grid. A band that already parsed real\n // content means items[band.index] misaligned (a non-contiguous page) —\n // materializing would CLOBBER it, so skip with a diagnostic instead.\n if (!isEmptyish(band.root)) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `band ${band.index} has parsed content but site.json item is a feed source — positional join misaligned, left as parsed`,\n });\n continue;\n }\n const tiles = resolveFeedTiles(item, resolvers);\n if (!tiles) {\n diagnostics.push({\n kind: \"empty-feed-grid\",\n where: String(band.index),\n message: `feed band ${band.index} (source ${String(item.sources[0])}) resolved to no tiles`,\n });\n continue;\n }\n const spacing = parseInt(String((item as { spacing?: unknown }).spacing ?? \"\"), 10);\n const row = materializeFeedGrid({\n tiles,\n columns: feedColumns(item as { columns?: unknown }),\n ...(Number.isFinite(spacing) && spacing > 0 ? { spacing } : {}),\n ...(tileOverlay(item) ? { overlay: tileOverlay(item)! } : {}),\n });\n if (!row) continue;\n const headings = leadingHeadings(band.root);\n band.root = headings.length ? { kind: \"stack\", children: [...headings, row] } : row;\n }\n}\n\n/** The site's page routing table (uid + export path + title), derivable from\n * site.json alone — the CLI uses it to locate each page's rendered html\n * (root index.html for the homepage, `<path>/index.html` for the rest)\n * before running convertSite. */\nexport function sitePages(siteJson: unknown): { uid: string; path: string; title: string }[] {\n const { pages } = normalizePages(parseBluxSite(siteJson));\n return pages.map((p) => ({ uid: p.uid, path: p.path, title: p.title }));\n}\n\n/** One converted page of a multi-page site. */\nexport type ConvertedPage = {\n uid: string;\n title: string;\n path: string;\n bands: Band[];\n specs: SliceSpec[];\n mapConfig: MapConfig | null;\n};\n\n/** A multi-page presentation manifest: per-page band manifests keyed by page\n * uid. Band indices are page-local (`page-block-N` restarts at 0 on every\n * page), so a flat bands map would collide across pages — the render side's\n * `loadPresentation(uid)` selects the page slice. */\nexport type MultiPagePresentation = { pages: Record<string, Presentation> };\n\n/** The whole-site faithful-grid convert: every page of the export (each page\n * dir's rendered index.html) through the same parse → classify → presentation\n * pipeline `convertExport` runs for one page. One IR assembled from ALL page\n * htmls (the asset urlMap then resolves media that only appear on inner\n * pages), one migration plan (a document per page, the asset union), and a\n * per-page presentation manifest. Pages whose html is missing from\n * `htmlByUid` (unexported drafts) are skipped with a diagnostic. */\nexport function convertSite({\n siteJson,\n htmlByUid,\n}: {\n siteJson: unknown;\n htmlByUid: Map<string, string>;\n}): {\n pages: ConvertedPage[];\n ir: SiteIR;\n plan: MigrationPlan;\n presentation: MultiPagePresentation;\n} {\n const ir = assembleIR({ siteJson, htmls: [...htmlByUid.values()] });\n const assetsById = new Map(ir.assets.map((a) => [a.id, a] as const));\n const sourceUrlById = new Map(ir.assets.map((a) => [a.id, a.sourceUrl] as const));\n const defaults = blockClassDefaults(siteJson);\n\n // Feed-grid materialization: gallery/portfolio tiles render client-side from\n // feed records (the static export ships only the dropped {{…}} template), so\n // we rebuild them deterministically from the feed data (see feed-grid.ts).\n // The asset base is scraped from the export's own data-base so feed images\n // use the RIGHT CDN host (Blux spreads assets across two).\n const raw = parseBluxSite(siteJson);\n const assetBase = feedAssetBase([...htmlByUid.values()], ir.meta.bluxSiteId);\n const feedResolvers = buildFeedResolvers(raw.feeds, raw.media, assetBase);\n const pageItemsByIndex = (siteJson as { content?: { pages?: { items?: unknown[] }[] } })?.content\n ?.pages;\n\n const pages: ConvertedPage[] = [];\n const presentation: MultiPagePresentation = { pages: {} };\n ir.pages.forEach((page, pageIndex) => {\n const html = htmlByUid.get(page.uid);\n if (html === undefined) {\n ir.diagnostics.push({\n kind: \"missing-page-html\",\n where: page.uid,\n message: `no rendered html for page \"${page.uid}\" (${page.path || \"/\"}) — page skipped`,\n });\n return;\n }\n const bands = parseGridBands(html);\n materializeFeedBands(\n bands,\n pageItemsByIndex?.[pageIndex]?.items,\n feedResolvers,\n ir.diagnostics,\n );\n const mapConfig = extractMapConfig(html);\n const specs = classifyBands(bands, mapConfig ? { isMapMount: makeIsMapMount(mapConfig) } : {});\n const styles = blockStylesByIndex(siteJson, pageIndex);\n const deps: PresentationDeps = {\n resolveMedia: (m) => {\n const url = mediaUrl(m, sourceUrlById);\n if (!url) return null;\n const alt = assetsById.get(m.assetId)?.alt;\n const rm: RenderMedia = {\n kind: m.kind,\n url,\n ...(alt ? { alt } : {}),\n ...(m.width !== undefined ? { width: m.width } : {}),\n ...(m.aspect !== undefined ? { aspect: m.aspect } : {}),\n ...(m.fit ? { fit: m.fit } : {}),\n ...(m.position ? { position: m.position } : {}),\n ...(m.minHeight ? { minHeight: m.minHeight } : {}),\n ...(m.cropRatio ? { cropRatio: m.cropRatio } : {}),\n ...(m.playback ? { playback: m.playback } : {}),\n };\n return rm;\n },\n styleFor: (i) => styles.get(i),\n defaultsFor: (blockClass) => defaults.get(blockClass),\n map: mapConfig ? mapRenderFromConfig(mapConfig) : null,\n };\n presentation.pages[page.uid] = buildPresentation(specs, deps);\n pages.push({ uid: page.uid, title: page.title, path: page.path, bands, specs, mapConfig });\n });\n\n const plan = buildGridSitePlan(\n pages.map((p) => ({ uid: p.uid, title: p.title, specs: p.specs })),\n ir,\n );\n return { pages, ir, plan, presentation };\n}\n","// Site chrome (navigation + footer) → a render-side config. The Blux export's\n// `navigation` is a nested tree (top items with optional dropdown children)\n// and `footer` a list of social-link + copyright items; both were dropped by\n// the page-focused convert. This builds a deterministic `site-config.json` the\n// render's Nav/Footer consume (additive: a site with no config renders the\n// starter's logo-only bar and placeholder footer, exactly as before).\n\n/** A navigation entry: a label + href, with optional dropdown children. */\nexport type NavItem = { label: string; href: string; children?: NavItem[] };\n\n/** A footer social link: the network id (facebook, instagram, …) and, when the\n * export carries one, its url. */\nexport type FooterSocial = { network: string; href?: string };\n\nexport type SiteConfig = {\n nav: {\n /** The logo image url (resolved), if the export declares one. */\n logo?: { url: string; maxWidth?: string };\n items: NavItem[];\n };\n footer: {\n socials: FooterSocial[];\n /** The copyright / rights line. */\n text?: string;\n };\n};\n\ntype RawNavItem = { title?: unknown; link?: unknown; items?: unknown };\n\n/** Parse the nested navigation tree from a Blux `navigation` value. Only items\n * with a title survive; an item with children becomes a dropdown (its own link\n * is often empty — a heading), a leaf keeps its href. Recurses one+ levels. */\nfunction parseNavItems(items: unknown): NavItem[] {\n if (!Array.isArray(items)) return [];\n const out: NavItem[] = [];\n for (const raw of items as RawNavItem[]) {\n const label = typeof raw?.title === \"string\" ? raw.title.trim() : \"\";\n if (!label) continue;\n const href = typeof raw?.link === \"string\" ? raw.link : \"\";\n const children = parseNavItems(raw?.items);\n out.push({ label, href, ...(children.length ? { children } : {}) });\n }\n return out;\n}\n\n/** The domain(s) that identify each Blux social network. Blux's social widget\n * stores only the network *flags* (`networks: { facebook: true, … }`) — the\n * profile urls are injected at render time from account config that isn't in\n * the export, so we recover them from the scraped live footer by matching the\n * link's host against these.\n *\n * Keep this in lockstep with the render's Footer `NETWORK` map (reddoor-starter\n * src/lib/components/Footer.svelte): a network we resolve an href for but the\n * Footer can't draw is silently dropped, so only list networks the Footer\n * renders. */\nconst SOCIAL_DOMAINS: Record<string, string[]> = {\n facebook: [\"facebook.com\", \"fb.com\"],\n twitter: [\"twitter.com\", \"x.com\"],\n instagram: [\"instagram.com\"],\n pinterest: [\"pinterest.com\"],\n linkedin: [\"linkedin.com\"],\n \"linkedin-company\": [\"linkedin.com\"],\n youtube: [\"youtube.com\", \"youtu.be\"],\n reddit: [\"reddit.com\"],\n};\n\n/** True when `host` is `domain` or a subdomain of it — not merely a suffix\n * match (so `notfacebook.com` never matches `facebook.com`). */\nfunction hostMatches(host: string, domain: string): boolean {\n return host === domain || host.endsWith(\".\" + domain);\n}\n\nfunction hostOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n/** The `<footer>…</footer>` region of a page (last occurrence), or the whole\n * html when there's no footer element. Social links live in the footer, so we\n * scan there — a body link to, say, a facebook post must not outrank the real\n * footer profile link. */\nfunction footerRegion(html: string): string {\n const lower = html.toLowerCase();\n const start = lower.lastIndexOf(\"<footer\");\n if (start === -1) return html;\n const end = lower.indexOf(\"</footer>\", start);\n return end === -1 ? html.slice(start) : html.slice(start, end + \"</footer>\".length);\n}\n\n/** A network→href resolver built from scraped page HTML. Blux renders the same\n * footer on every page, so any page's html carries the social links; we scan\n * the footer region for absolute http(s) hrefs and match each to a network by\n * host. Returns a function so `buildSiteConfig` stays free of the html-scan\n * mechanics. */\nexport function socialHrefResolverFromHtml(\n htmls: string[],\n): (network: string) => string | undefined {\n const hrefs: string[] = [];\n const re = /href\\s*=\\s*[\"']([^\"']+)[\"']/gi;\n for (const html of htmls) {\n if (typeof html !== \"string\") continue;\n const region = footerRegion(html);\n let m: RegExpExecArray | null;\n while ((m = re.exec(region))) {\n const url = m[1];\n if (url && /^https?:\\/\\//i.test(url)) hrefs.push(url);\n }\n }\n return (network) => {\n const domains = SOCIAL_DOMAINS[network];\n if (!domains) return undefined;\n return hrefs.find((u) => {\n const host = hostOf(u);\n return host && domains.some((d) => hostMatches(host, d));\n });\n };\n}\n\n/** The known social networks and the http(s) url found for one in a raw footer\n * social item (Blux stores `networks: { facebook: true, … }` — the flags — and\n * sometimes per-network urls elsewhere; we carry the network id and any url).\n * When the export omits a url, `resolveSocialHref` recovers it from the scraped\n * live footer (see `socialHrefResolverFromHtml`). */\nfunction parseSocials(\n raw: unknown,\n resolveSocialHref?: (network: string) => string | undefined,\n): FooterSocial[] {\n const out: FooterSocial[] = [];\n const items = Array.isArray(raw) ? raw : [];\n for (const it of items as { media?: { type?: unknown; networks?: unknown; urls?: unknown } }[]) {\n const media = it?.media;\n if (!media || media.type !== \"social\") continue;\n const networks = media.networks;\n if (!networks || typeof networks !== \"object\") continue;\n const urls = (media.urls ?? {}) as Record<string, unknown>;\n for (const [network, on] of Object.entries(networks as Record<string, unknown>)) {\n if (on !== true) continue;\n const fromExport = typeof urls[network] === \"string\" ? (urls[network] as string) : undefined;\n const url = fromExport ?? resolveSocialHref?.(network);\n out.push({ network, ...(url ? { href: url } : {}) });\n }\n }\n return out;\n}\n\n/** Every non-empty `title` under a footer subtree, in document order. The\n * copyright often sits nested inside a column item, not at the top level. */\nfunction collectTitles(node: unknown, acc: string[]): string[] {\n if (Array.isArray(node)) {\n for (const n of node) collectTitles(n, acc);\n return acc;\n }\n if (node && typeof node === \"object\") {\n const rec = node as Record<string, unknown>;\n if (typeof rec.title === \"string\" && rec.title.trim()) acc.push(rec.title.trim());\n for (const k of Object.keys(rec)) if (k !== \"title\") collectTitles(rec[k], acc);\n }\n return acc;\n}\n\n/** The footer's rights/copyright line. Blux footers interleave link-column\n * headings (\"About Us\", \"Quick Links\") with the copyright — and the copyright\n * is often nested inside a column — so \"first titled item\" grabs a heading on\n * many sites. Match the copyright by its shape (©, \"copyright\", or \"rights\n * reserved\") across the whole footer subtree instead. Returns undefined when\n * none matches (the render shows its generic notice, not a wrong heading). */\nconst COPYRIGHT_RE = /©|\\(c\\)|\\bcopyright\\b|rights reserved/i;\nfunction parseFooterText(raw: unknown): string | undefined {\n return collectTitles(raw, []).find((t) => COPYRIGHT_RE.test(t));\n}\n\n/** Build the render-side site config from the export's navigation + footer.\n * `resolveLogo` turns the nav logo's asset uuid into a url (null when\n * unresolved). `resolveSocialHref` recovers a footer social's profile url from\n * the scraped live footer when the export omits it. Pure. */\nexport function buildSiteConfig(\n siteJson: unknown,\n resolveLogo: (uuid: string) => string | null,\n resolveSocialHref?: (network: string) => string | undefined,\n): SiteConfig {\n const j = siteJson as { navigation?: unknown; footer?: unknown };\n const navRoot = (Array.isArray(j.navigation) ? j.navigation[0] : j.navigation) as\n | { items?: unknown; logo?: { media?: unknown } }\n | undefined;\n const footRoot = (Array.isArray(j.footer) ? j.footer[0] : j.footer) as\n | { items?: unknown }\n | undefined;\n\n const items = parseNavItems(navRoot?.items);\n // The logo is `logo: { media: { media: <uuid>, \"max-width\": … } }` — the\n // asset uuid and its render sizing are nested one level inside `logo.media`.\n const logoMedia = navRoot?.logo?.media as { media?: unknown; \"max-width\"?: unknown } | undefined;\n const logoUuid = typeof logoMedia?.media === \"string\" ? logoMedia.media : undefined;\n const logoUrl = logoUuid ? resolveLogo(logoUuid) : null;\n const maxWidth =\n typeof logoMedia?.[\"max-width\"] === \"string\" ? logoMedia[\"max-width\"] : undefined;\n\n const socials = parseSocials(footRoot?.items, resolveSocialHref);\n const text = parseFooterText(footRoot?.items);\n\n return {\n nav: {\n ...(logoUrl ? { logo: { url: logoUrl, ...(maxWidth ? { maxWidth } : {}) } } : {}),\n items,\n },\n footer: {\n socials,\n ...(text ? { text } : {}),\n },\n };\n}\n","// Offline layout-fidelity gate (plan 6). Diffs the classified source\n// (`SliceSpec[]`, already gated band→spec by grid-classify-golden) against the\n// emitted presentation manifest (plan 5), naming every band whose layout,\n// media, or map drifted. Pure + offline — the render side (Playwright DOM\n// signature) is plan 7's verify. See the plan's \"The comparison, precisely\".\nimport type { Cell, Node, SliceSpec } from \"../grid/index.js\";\nimport {\n hasMapWidget,\n type Presentation,\n type RenderCell,\n type RenderNode,\n} from \"./presentation.js\";\n\n/** Canonical grid-token key — cols + optional ratio/spacing, WITHOUT the\n * source-only `raw` string, so a source token and its render-side twin (which\n * never carries `raw`) compare equal. */\nfunction tokKey(t: { cols: number | \"any\"; ratio?: number; spacing?: number }): string {\n return `${t.cols}${t.ratio !== undefined ? `r${t.ratio}` : \"\"}${t.spacing !== undefined ? `s${t.spacing}` : \"\"}`;\n}\n\n/** Compact structural signature of a node tree, computed identically for a\n * source `Node` and its serialized `RenderNode` twin (they share `kind`s and\n * aligned fields). Prose is excluded; only kinds + grid tokens + media/widget\n * kinds appear — so it snapshots LAYOUT, not content. A source media node the\n * manifest dropped (unresolved url) makes the two signatures diverge, which is\n * exactly the fidelity signal we want. Mirrors `grid/signature.ts`'s exhaustive\n * switch so a new node kind is a compile error, not a silent drop. */\nexport function sigOf(node: Node | RenderNode): string {\n switch (node.kind) {\n case \"row\":\n return `row[${node.cells\n .map((c: Cell | RenderCell) => `${tokKey(c.token)}:${sigOf(c.node)}`)\n .join(\",\")}]`;\n case \"stack\":\n return `stack[${node.children.map(sigOf).join(\",\")}]`;\n case \"heading\":\n return `h${node.level}`;\n case \"body\":\n return \"body\";\n case \"subtitle\":\n return \"subtitle\";\n case \"media\":\n return `media:${node.media.kind}`;\n case \"widget\":\n return `widget:${node.widget.type}`;\n case \"raw\":\n return \"raw\";\n }\n}\n\nexport type LayoutFinding =\n | { kind: \"band-count\"; specs: number; manifest: number }\n | { kind: \"band-missing\"; band: number }\n | { kind: \"tree-drift\"; band: number; expected: string; actual: string }\n | { kind: \"media-dropped\"; band: number; where: string }\n | { kind: \"map-missing\"; band: number };\n\nexport type LayoutRow = {\n band: number;\n slice: SliceSpec[\"slice\"];\n source: string;\n converted: string;\n ok: boolean;\n};\n\nexport type LayoutReport = {\n /** number of source bands (== specs.length) */\n bands: number;\n /** Grid-fallback bands whose tree fidelity was signature-checked */\n gridBands: number;\n faithful: boolean;\n findings: LayoutFinding[];\n rows: LayoutRow[];\n};\n\n/** A short source-side label per slice, used for the report row's `source`\n * column and the missing-band row. Grid uses the full structural signature. */\nfunction sourceLabel(spec: SliceSpec): string {\n switch (spec.slice) {\n case \"Grid\":\n return sigOf(spec.root);\n case \"Gallery\":\n return `gallery(${spec.media.length})`;\n case \"Carousel\":\n return `carousel(${spec.slides.length})`;\n case \"MediaFull\":\n return \"media_full\";\n case \"VideoFeature\":\n return \"video\";\n case \"SplitFeature\":\n return `split(${spec.mediaSide},${spec.ratio})`;\n case \"LocationMap\":\n return \"location_map\";\n case \"Hero\":\n return \"hero\";\n case \"TitleBand\":\n return \"title_band\";\n case \"RichText\":\n return \"rich_text\";\n }\n}\n\n/** Diff the classified source against the emitted manifest. Grid bands must\n * round-trip their structural signature exactly (spec.root vs the serialized\n * tree); smart slices are checked for payload completeness (gallery count,\n * split/media present, background/map present). Returns a structured report;\n * `faithful` is true iff `findings` is empty. */\nexport function validateLayout(specs: SliceSpec[], presentation: Presentation): LayoutReport {\n const findings: LayoutFinding[] = [];\n const rows: LayoutRow[] = [];\n const manifestKeys = Object.keys(presentation.bands);\n let gridBands = 0;\n\n if (specs.length !== manifestKeys.length) {\n findings.push({ kind: \"band-count\", specs: specs.length, manifest: manifestKeys.length });\n }\n\n // We iterate specs (the source answer key): a stray manifest band with no spec surfaces only via the band-count mismatch above, never named — by design.\n for (const spec of specs) {\n const source = sourceLabel(spec);\n const bp = presentation.bands[String(spec.index)];\n if (!bp) {\n findings.push({ kind: \"band-missing\", band: spec.index });\n rows.push({ band: spec.index, slice: spec.slice, source, converted: \"∅\", ok: false });\n continue;\n }\n const before = findings.length;\n let converted = source;\n\n // Band background (any slice) must survive if the source declared one.\n if (spec.background && !bp.background) {\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"background\" });\n }\n\n switch (spec.slice) {\n case \"Grid\": {\n gridBands++;\n converted = bp.tree ? sigOf(bp.tree) : \"∅\";\n if (source !== converted) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n if (hasMapWidget(spec.root) && !bp.map) {\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Gallery\": {\n const got = bp.gallery?.length ?? 0;\n converted = `gallery(${got})`;\n if (got < spec.media.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `gallery ${got}/${spec.media.length}`,\n });\n }\n break;\n }\n case \"Carousel\": {\n const got = bp.carousel?.slides.length ?? 0;\n converted = `carousel(${got})`;\n if (got < spec.slides.length) {\n findings.push({\n kind: \"media-dropped\",\n band: spec.index,\n where: `carousel ${got}/${spec.slides.length}`,\n });\n }\n break;\n }\n case \"MediaFull\":\n case \"VideoFeature\": {\n if (!bp.media) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"media\" });\n }\n break;\n }\n case \"SplitFeature\": {\n if (!bp.split) {\n converted = \"∅\";\n findings.push({ kind: \"media-dropped\", band: spec.index, where: \"split\" });\n } else {\n converted = `split(${bp.split.mediaSide},${bp.split.ratio})`;\n // Defensive: the current builder copies mediaSide/ratio verbatim, so\n // this can't fire against real convertExport output — it guards a\n // future transforming builder that reshapes the split.\n if (converted !== source) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: source,\n actual: converted,\n });\n }\n // The split's text side is a full node subtree (band 1 of the-pointe\n // nests media inside it); buildPresentation serializes it via\n // renderNode, which DROPS unresolved media. Signature-check it like a\n // Grid tree so a dropped nested media isn't silently reported faithful.\n const expectedText = sigOf(spec.text);\n const actualText = sigOf(bp.split.text);\n if (expectedText !== actualText) {\n findings.push({\n kind: \"tree-drift\",\n band: spec.index,\n expected: `split.text ${expectedText}`,\n actual: `split.text ${actualText}`,\n });\n }\n }\n break;\n }\n case \"LocationMap\": {\n if (!bp.map) {\n converted = \"∅\";\n findings.push({ kind: \"map-missing\", band: spec.index });\n }\n break;\n }\n case \"Hero\":\n case \"TitleBand\":\n case \"RichText\":\n break; // text lives in the page doc (gated by grid-slice.test.ts); nothing manifest-carried to lose beyond background\n default: {\n // A new slice kind must be handled explicitly, not silently pass as\n // faithful. `sourceLabel` above is also exhaustive, so this is a local\n // belt-and-suspenders guard rather than the sole gate.\n const _exhaustive: never = spec;\n throw new Error(`validateLayout: unhandled slice ${(_exhaustive as SliceSpec).slice}`);\n }\n }\n\n rows.push({\n band: spec.index,\n slice: spec.slice,\n source,\n converted,\n ok: findings.length === before,\n });\n }\n\n return { bands: specs.length, gridBands, faithful: findings.length === 0, findings, rows };\n}\n\nfunction fmtFinding(f: LayoutFinding): string {\n switch (f.kind) {\n case \"band-count\":\n return ` band count mismatch: ${f.specs} source bands vs ${f.manifest} manifest bands`;\n case \"band-missing\":\n return ` band ${f.band}: no manifest entry`;\n case \"tree-drift\":\n return ` band ${f.band}: grid tree drift\\n expected ${f.expected}\\n actual ${f.actual}`;\n case \"media-dropped\":\n return ` band ${f.band}: media dropped (${f.where})`;\n case \"map-missing\":\n return ` band ${f.band}: map config missing`;\n }\n}\n\n/** Render a LayoutReport as a terminal summary: a headline verdict, a per-band\n * signature table (`ok` / `!!` per band), then the findings detail. */\nexport function formatLayoutReport(r: LayoutReport): string {\n const headline = `layout fidelity: ${\n r.faithful ? \"FAITHFUL\" : `${r.findings.length} finding(s)`\n } — ${r.bands} bands (${r.gridBands} grid-tree checked)`;\n const table = r.rows.map((x) => {\n const tag = x.ok ? \"ok\" : \"!!\";\n const sig = x.ok ? x.source : `${x.source} -> ${x.converted}`;\n return ` band ${String(x.band).padStart(2)} ${x.slice.padEnd(13)} ${tag} ${sig}`;\n });\n const detail = r.findings.length ? [\"findings:\", ...r.findings.map(fmtFinding)] : [];\n return [headline, ...table, ...detail].join(\"\\n\");\n}\n","import type {\n BandPresentation,\n Presentation,\n RenderMedia,\n RenderNode,\n SitePresentation,\n} from \"./presentation.js\";\n\nconst swap = (m: RenderMedia, map: Map<string, string>): RenderMedia => {\n const url = map.get(m.url);\n return url ? { ...m, url } : m;\n};\n\nfunction walkNode(node: RenderNode, map: Map<string, string>): RenderNode {\n switch (node.kind) {\n case \"row\":\n return {\n kind: \"row\",\n cells: node.cells.map((c) => ({ token: c.token, node: walkNode(c.node, map) })),\n };\n case \"stack\":\n return { kind: \"stack\", children: node.children.map((c) => walkNode(c, map)) };\n case \"media\":\n return { kind: \"media\", media: swap(node.media, map) };\n default:\n return node; // heading/body/subtitle/raw/widget carry no media url\n }\n}\n\nfunction walkBand(bp: BandPresentation, map: Map<string, string>): BandPresentation {\n const out: BandPresentation = { ...bp };\n if (bp.background) out.background = swap(bp.background, map);\n if (bp.media) out.media = swap(bp.media, map);\n if (bp.gallery) out.gallery = bp.gallery.map((m) => swap(m, map));\n if (bp.carousel)\n out.carousel = {\n ...bp.carousel,\n slides: bp.carousel.slides.map((s) => ({ ...s, media: swap(s.media, map) })),\n };\n if (bp.tree) out.tree = walkNode(bp.tree, map);\n if (bp.split)\n out.split = {\n ...bp.split,\n media: swap(bp.split.media, map),\n text: walkNode(bp.split.text, map),\n };\n return out; // style / map untouched (no media urls)\n}\n\nfunction rewritePage(p: Presentation, map: Map<string, string>): Presentation {\n const bands: Record<string, BandPresentation> = {};\n for (const [k, bp] of Object.entries(p.bands)) bands[k] = walkBand(bp, map);\n return { bands };\n}\n\n/** Deep copy of the manifest with every RenderMedia.url present in `urlMap`\n * replaced by its mapped value. Unknown urls left intact. Handles BOTH shapes:\n * the flat single-page `{ bands }` and the multi-page `{ pages: { <uid>:\n * { bands } } }` that `blux convert` now writes — the migrate step must rewrite\n * every site's urls regardless of shape. Pure. */\nexport function rewriteManifestUrls(\n manifest: SitePresentation,\n urlMap: Map<string, string>,\n): SitePresentation {\n if (\"pages\" in manifest && manifest.pages) {\n const pages: Record<string, Presentation> = {};\n for (const [uid, p] of Object.entries(manifest.pages)) pages[uid] = rewritePage(p, urlMap);\n return { pages };\n }\n // The flat branch is a Presentation by construction (cast bridges the union).\n return rewritePage(manifest as Presentation, urlMap);\n}\n"],"mappings":"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","names":["base","parse","base","img","base","style","parse","tiles","base","img","base"]}
|