diff --git a/compiler/src/generate/sectionSplit.ts b/compiler/src/generate/sectionSplit.ts index f209ca7..5835654 100644 --- a/compiler/src/generate/sectionSplit.ts +++ b/compiler/src/generate/sectionSplit.ts @@ -15,6 +15,7 @@ import type { IR, IRNode } from "../normalize/ir.js"; import { isTextChild } from "../normalize/ir.js"; import type { RecipeCandidate, RecipeKind, RecipeReport } from "../infer/recipes.js"; +import { detectSectionNodes, heroLikeHeader } from "../infer/sections.js"; import { subtreeSignature } from "../site/sharedLayout.js"; export type SectionPlan = { @@ -23,7 +24,7 @@ export type SectionPlan = { }; const MIN_SECTION_H = 56; -const MAX_SECTIONS = 24; +const MAX_SECTIONS = 32; function box(n: IRNode, cw: number): { width: number; height: number } | undefined { return n.bboxByVp[cw] ?? Object.values(n.bboxByVp)[0]; @@ -31,15 +32,9 @@ function box(n: IRNode, cw: number): { width: number; height: number } | undefin function yOf(n: IRNode, cw: number): number { return (n.bboxByVp[cw] ?? Object.values(n.bboxByVp)[0])?.y ?? 0; } -function visible(n: IRNode, cw: number): boolean { - return !!(n.visibleByVp[cw] ?? Object.values(n.visibleByVp)[0]); -} function elementChildren(n: IRNode): IRNode[] { return n.children.filter((c): c is IRNode => !isTextChild(c)); } -function significantChildren(n: IRNode, cw: number): IRNode[] { - return elementChildren(n).filter((c) => visible(c, cw) && (box(c, cw)?.height ?? 0) >= MIN_SECTION_H); -} function subtreeHasTag(n: IRNode, tag: string, depth = 4): boolean { if (depth < 0) return false; @@ -225,18 +220,11 @@ function recipeFallbackSections(ir: IR, recipes?: RecipeReport): { sections: IRN export function planSections(ir: IR, recipes?: RecipeReport): SectionPlan { const cw = ir.doc.canonicalViewport; - // Descend through the wrapper chain (single significant child) to the container - // whose children are the actual sections. - let node = ir.root; - for (let i = 0; i < 10; i++) { - const sig = significantChildren(node, cw); - if (sig.length >= 2) break; - if (sig.length === 1) { node = sig[0]!; continue; } - const kids = elementChildren(node); - if (kids.length === 1) { node = kids[0]!; continue; } - break; - } - // Exclude any child that is part of a REPEATED run (≥3 same-signature siblings): that's + const pageH = ir.doc.perViewport[cw]?.scrollHeight ?? 0; + // The shared recursive band decomposition (infer/sections) — the same roots the + // section gate validates, so each emitted file corresponds to a gate section. + const bands = detectSectionNodes(ir).filter((n) => n.id !== ir.root.id); + // Exclude any band that is part of a REPEATED run (≥3 same-signature siblings): that's // a component cluster (a card/logo grid), which component extraction should turn into a // `.map()` over a data array — not a wall of near-identical "section" files. Only the // distinct, one-off blocks become sections. @@ -246,29 +234,14 @@ export function planSections(ir: IR, recipes?: RecipeReport): SectionPlan { return list.filter((s) => (count.get(subtreeSignature(s)) ?? 0) < 3); }; - let candidates = significantChildren(node, cw); - let sections = distinctOf(candidates); - const fallback = recipeFallbackSections(ir, recipes); - // If the container yields too few sections, a dominant child is a wrapper (e.g.
) - // holding the real sections — expand such oversized children into their own significant - // children, keeping the other siblings (e.g. a sibling