Proposing a new sibling package, not a feature of this one — see the reasoning below for why it doesn't belong here or in documents.js.
None of ContentDocument's wordprocessing shape groups content by heading. A section is blocks: ContentBlock[], a flat, ordered list — a heading paragraph sits in that list like any other, distinguished only by headingLevel (once #13 lands). Building an actual nested tree — everything under a Heading 2 becomes that heading's children, recursively — is a real, separate transform every consumer of this ecosystem currently has to write for itself if they need it (chunking a document for retrieval/search, generating a table of contents, structural diffing).
Proposed: a new package, document-outline (name open to bikeshedding), depending on document-schema.js only:
- An
OutlineNode schema — recursive (heading text/level, children: (OutlineNode | ContentBlock)[]). Worth trying z.lazy() for this from the start rather than the z.custom() type-guard workaround ContentBlock/ContentEmbeddedObject need — this package's own README documents a throwaway spike confirming z.lazy() works now (against the pinned zod version), with one annotation gotcha; that fix postdates when those two were built.
buildWordprocessingOutline(doc): OutlineNode[] — walks sections[].blocks, opens a new group at each headingLevel, and every non-heading block (paragraph, table, image) becomes a leaf child of whichever group is currently open.
- A small set of tree-walking helpers alongside it: flatten-to-leaves, a leaf's own text, a stable per-leaf content hash — the operations every consumer of a grouped tree ends up needing regardless of what they do with it.
Scope for v1 is wordprocessing only, deliberately. Presentation has no natural deep hierarchy the way headings give wordprocessing one — a slide isn't "under" another slide — so "one group per slide" vs. something else is a real product decision, not assumed here. Spreadsheet/drawing/formula don't have an obvious equivalent at all. Leaving those unaddressed rather than guessing at a shape nobody's validated yet.
Why a new package rather than adding this here or to documents.js:
- This package is a dependency of every codec and everything downstream of them. Its own sibling-release automation means every dependent gets an auto-bump PR on every release here — bundling in an algorithm that's still finding its own shape (open questions on presentation/spreadsheet scope, likely to grow toward TOC-extraction/heading-search later) means every iteration on it forces that cascade through the whole ecosystem for zero change to anyone's actual read/write logic.
documents.js is the wrong home for the opposite reason: it already depends on every codec (ooxml.js, odf.js, pdf-codec, markdown-codec, byte-codec) for its actual job, format conversion. This transform needs none of that — it only ever touches an already-produced ContentDocument, regardless of which codec made it. Forcing a consumer who wants only the outline builder to pull in every codec's dependency tree buys them nothing.
- A new package gets the same single-dependency shape as every existing codec here, and the same sibling-release wiring, for free.
Blocked by #13 — there's nothing to group by until headingLevel exists.
Proposing a new sibling package, not a feature of this one — see the reasoning below for why it doesn't belong here or in
documents.js.None of
ContentDocument's wordprocessing shape groups content by heading. A section isblocks: ContentBlock[], a flat, ordered list — a heading paragraph sits in that list like any other, distinguished only byheadingLevel(once #13 lands). Building an actual nested tree — everything under a Heading 2 becomes that heading's children, recursively — is a real, separate transform every consumer of this ecosystem currently has to write for itself if they need it (chunking a document for retrieval/search, generating a table of contents, structural diffing).Proposed: a new package,
document-outline(name open to bikeshedding), depending ondocument-schema.jsonly:OutlineNodeschema — recursive (headingtext/level,children: (OutlineNode | ContentBlock)[]). Worth tryingz.lazy()for this from the start rather than thez.custom()type-guard workaroundContentBlock/ContentEmbeddedObjectneed — this package's own README documents a throwaway spike confirmingz.lazy()works now (against the pinned zod version), with one annotation gotcha; that fix postdates when those two were built.buildWordprocessingOutline(doc): OutlineNode[]— walkssections[].blocks, opens a new group at eachheadingLevel, and every non-heading block (paragraph, table, image) becomes a leaf child of whichever group is currently open.Scope for v1 is wordprocessing only, deliberately. Presentation has no natural deep hierarchy the way headings give wordprocessing one — a slide isn't "under" another slide — so "one group per slide" vs. something else is a real product decision, not assumed here. Spreadsheet/drawing/formula don't have an obvious equivalent at all. Leaving those unaddressed rather than guessing at a shape nobody's validated yet.
Why a new package rather than adding this here or to
documents.js:documents.jsis the wrong home for the opposite reason: it already depends on every codec (ooxml.js,odf.js,pdf-codec,markdown-codec,byte-codec) for its actual job, format conversion. This transform needs none of that — it only ever touches an already-producedContentDocument, regardless of which codec made it. Forcing a consumer who wants only the outline builder to pull in every codec's dependency tree buys them nothing.Blocked by #13 — there's nothing to group by until
headingLevelexists.