Skip to content

fix(presentations): separate slides with a thematic break - #32

Open
Ready22Race wants to merge 1 commit into
firecrawl:mainfrom
Ready22Race:fix/presentation-slide-boundaries
Open

fix(presentations): separate slides with a thematic break#32
Ready22Race wants to merge 1 commit into
firecrawl:mainfrom
Ready22Race:fix/presentation-slide-boundaries

Conversation

@Ready22Race

@Ready22Race Ready22Race commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #31.

What

Emits Block::Rule between slides in the three presentation parsers (pptx, ppt, odp), which previously concatenated every slide's blocks with nothing in between.

A slide's title becomes a Heading, so decks that title every slide read correctly by accident. A slide with no title placeholder contributes no structural block, so its content is indistinguishable from a continuation of the previous slide — two untitled slides in a row produce two adjacent paragraphs, and bullet lists from different slides merge into one list.

Why a thematic break

No new concept and no page number: Block::Rule already exists, is already emitted for <hr> (src/shared/html.rs:432), and already renders as --- (src/render/markdown/mod.rs:201).

This is deliberately not the pagination #26 declined. That issue settled that a w:type="page" break is layout and dropping it is correct. A slide is a container in the source model rather than a layout artifact, so its boundary is document structure.

Shape of the change

Each parser now accumulates one slide into a local Vec<Block> and appends it only if non-empty, pushing a Rule first when output already exists. That gives two guarantees worth stating:

  • never leading — no --- before the first slide
  • never doubled — a slide that produces no blocks (empty layout, chrome-only placeholders) cannot leave a dangling or repeated break

ppt already accumulated per slide, so it only needed the guard.

Scope: core only, no binding changes

Block::Rule already exists and every binding already handles it, so nothing outside src/formats/ needs to move:

binding maps model::Block::Rule declares the rule kind
node node/src/document.rs:88 node/index.d.ts:60
python python/src/document.rs:83 python/anydoc/_anydoc.pyi:84
wasm wasm/src/document.rs:96 wasm/src/typescript.rs:47

No new Block variant, no public API change, no type-surface change. Confirmed end to end through the Python binding on the deck from the issue:

>>> anydoc.to_document(data).blocks
['heading', 'rule', 'paragraph', 'rule', 'paragraph']   # kinds

Tests

Three unit tests in src/formats/pptx/mod.rs, building minimal packages in memory in the style of the sheet module's tests — they pin the behaviour rather than just the output:

  • untitled_slides_are_separated — the defect itself: two title-less slides must not read as one
  • separator_never_leads — a deck must not open with ---, and a single slide yields only its own content
  • empty_slides_leave_no_dangling_separator — a slide contributing no blocks must not add a break, neither doubled between two real slides nor trailing

Verification

cargo fmt --all --check                                          clean
cargo clippy --workspace --all-targets --all-features -D warnings clean
cargo clippy -p anydoc-wasm --target wasm32-unknown-unknown       clean
cargo test --locked                                              194 passed, 0 failed
python -m unittest discover -s tests                             9 passed

Snapshot delta is 6 added --- lines and 6 added blank lines across 6 snapshots, with zero removals — no content changed, only separators appeared:

tests/snapshots/snapshots__odp__pres.odp.snap                  | 2 ++
tests/snapshots/snapshots__ppt__handmade-multimaster.ppt.snap  | 2 ++
tests/snapshots/snapshots__ppt__handmade-sparsenotes.ppt.snap  | 2 ++
tests/snapshots/snapshots__ppt__pres.ppt.snap                  | 2 ++
tests/snapshots/snapshots__pptx__handmade-links.pptx.snap      | 2 ++
tests/snapshots/snapshots__pptx__pres.pptx.snap                | 2 ++

Also checked by hand against a deck whose slides 2 and 3 have no title placeholder (the case in the issue) and against tests/fixtures/ppt/pres.ppt.

Note

Happy to take this in a different direction if you would rather the boundary be expressed some other way, or scope it to a subset of the three parsers. The behaviour change is visible in output, so it is your call whether it belongs in a minor bump.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

The three presentation parsers concatenate every slide's blocks into one
list with nothing between them. A slide's title becomes a heading, so decks
that title every slide read correctly by accident — but a slide with no
title placeholder contributes no structural block at all, and its content
becomes indistinguishable from the previous slide's.

Two title-less slides in a row produce two adjacent paragraphs, exactly as
if they were two paragraphs of one slide. Bullet lists from different slides
merge into one list.

This is not the pagination that "Markdown has no pages" rightly refuses. A
slide is a container in the source model, not a layout artifact, and losing
its boundary loses document structure. Block::Rule is the separator the
model already has (emitted for <hr>, rendered as ---), so no new concept is
introduced and no page number is implied.

Emitted BETWEEN slides only: never leading, and a slide that produces no
blocks never yields a doubled or dangling break. Applies to pptx, ppt and
odp, which all had the same shape.

Core-only: every binding already maps model::Block::Rule (node/src/
document.rs:88, python/src/document.rs:83, wasm/src/document.rs:96) and
already declares the `rule` kind (node/index.d.ts:60,
python/anydoc/_anydoc.pyi:84, wasm/src/typescript.rs:47), so no binding
source or type surface changes.
@H0rowitz

H0rowitz commented Aug 6, 2026

Copy link
Copy Markdown

Independent verification, for whatever it's worth — we hit #31 on a Pandoc-authored deck while evaluating anydoc as the conversion engine for our ingestion pipeline, before finding the issue already filed.

Checked out 4b0edae and ran it against that deck (3 slides; slide 2 has no title placeholder — exactly the pathological case from the issue):

  • cargo test --locked: 194 passed, 0 failed, matching the PR's own verification block.
  • The two separators appear at exactly the two slide boundaries, none leading, none doubled.
  • A diff of the full converted output against 8eecca5 is 4 added lines (two ---, two blanks), zero removals — and this is a producer (Pandoc) the fixture corpus doesn't currently cover, so it complements the PR's LibreOffice/handmade snapshot evidence rather than repeating it.

One note for anyone reading along, orthogonal to this PR: on that same deck the remaining structure loss is bullet formatting inherited from the layout (<p:ph idx="1"/> content placeholders lose their list markers) — the separate, bigger inheritance topic. The boundary fix makes such decks navigable regardless, and untitled slides stop merging into their predecessor.

H0rowitz added a commit to H0rowitz/anydoc that referenced this pull request Aug 6, 2026
…(independently verified)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Presentation slides are concatenated with no boundary, so untitled slides merge into the previous one

2 participants