firecrawl / firecrawl/anydoc

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

Open
#31 0 comments 0 reactions 0 assignees View on GitHub
bug P2
Dominant language
Rust
Stars
21.5k
Forks
1.3k
Avg merge
42m
Merged PRs (30d)
17

Description

## Summary

The three presentation parsers concatenate every slide's blocks into one list with nothing between them, so a slide with no title placeholder is indistinguishable from a continuation of the previous slide.

A slide's title becomes a `Heading`, so decks that title every slide read correctly by accident. A slide with no title contributes no structural block at all — two such slides in a row produce two adjacent paragraphs, exactly as if they were two paragraphs of one slide, and bullet lists from different slides merge into one list.

## Reproduction

Tested with `firecrawl-anydoc==0.1.6`.

```python
from pptx import Presentation
from pptx.util import Inches
import anydoc

prs = Presentation()
s1 = prs.slides.add_slide(prs.slide_layouts[5])
s1.shapes.title.text = "Titled slide"
for text in ("Second slide, no title placeholder.", "Third slide, also untitled."):
slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank layout
slide.shapes.add_textbox(Inches(1), Inches(1), Inches(8), Inches(2)).text_frame.text = text
prs.save("untitled.pptx")

print(anydoc.to_markdown("untitled.pptx"))
```

## Observed

```
## Titled slide

Second slide, no title placeholder.

Third slide, also untitled.
```

Nothing marks where slide 2 ends and slide 3 begins.

## Expected

A separator between slides:

```
## Titled slide

---

Second slide, no title placeholder.

---

Third slide, also untitled.
```

## Why this is not the pagination you already declined

#26 settled that "Markdown has no pages, so dropping the break itself is the right call", and that framing is right for a `w:type="page"` break — that is layout. A slide is different: it is a container in the source model, and every one of its shapes belongs to it. Losing the boundary loses document structure, not pagination.

This asks for no page number and no new concept. `Block::Rule` already exists, is already emitted (`src/shared/html.rs:432`, for `


`), and already renders as `---` (`src/render/markdown/mod.rs:201`).

## Root cause

All three presentation parsers have the same shape — accumulate per slide, append, no separator:

- `src/formats/pptx/mod.rs` — `parse_shapes(sp_tree, &ctx, &mut blocks)?` writes straight into the shared `blocks`
- `src/formats/odf/mod.rs` — `parse_presentation` does `blocks.append(&mut title); blocks.append(&mut body);` per `draw:page`
- `src/formats/ppt/mod.rs` — `for (sid, blocks) in slides { out.extend(blocks); ... }`

## Scope

I have not scanned a corpus, so I will not claim a frequency. The structural argument is what I am confident in: any deck with a section divider, a full-bleed image slide, a quote slide, or a continuation slide has slides with no title placeholder, and those are the slides that merge.

Where every slide is titled, the current output is already correct and a separator only makes the boundary explicit.

## Proposed fix

Emit `Block::Rule` between slides — never leading, and never doubled by a slide that produces no blocks.

This is 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 and no new block variant.

PR follows.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the per-slide loops in src/formats/pptx/mod.rs, src/formats/odf/mod.rs, and src/formats/ppt/mod.rs, tracing how each appends parsed blocks. Check the existing Block::Rule handling in src/shared/html.rs and src/render/markdown/mod.rs. Done means slide boundaries produce single, non-leading, non-doubled --- separators while existing titled-slide output remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.