Skip to content

Pages: render bullet and numbered lists as Markdown#41

Open
ronaldmannak wants to merge 3 commits into
mainfrom
claude/pages-lists
Open

Pages: render bullet and numbered lists as Markdown#41
ronaldmannak wants to merge 3 commits into
mainfrom
claude/pages-lists

Conversation

@ronaldmannak

Copy link
Copy Markdown
Contributor

Body paragraphs whose list style (StorageArchive field 7 → TSWP.ListStyleArchive)
marks them as list items now render as Markdown lists instead of flat paragraphs.
The marker comes from the list style's per-level marker-type array (field 11), read
at the base level: 2 = bullet (-), 3 = ordered (1. with a running counter that
resets when the list style changes or a non-list paragraph interrupts it); other
values render as plain body text.

BodyStorage gains the field-7 list-style runs and a pre-resolved marker per style.
renderParagraph now reports heading vs body so renderParagraphs (which holds the
counter) can apply markers; consecutive items of one list render tight (single
newline) while lists are separated from surrounding blocks by a blank line.

List membership follows the file's styles, so "Plain paragraph before list", which
the fixture styles as List Bullet, is a bullet. Nested-list indentation isn't
resolved yet (items render flat); noted as a follow-up. Validated against
sample.pages: a 2-item bullet list and a 3-item numbered list.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Mxnj6jvuLX9VS9JsJJbjmf

Body paragraphs whose list style (StorageArchive field 7 → TSWP.ListStyleArchive)
marks them as list items now render as Markdown lists instead of flat paragraphs.
The marker comes from the list style's per-level marker-type array (field 11), read
at the base level: 2 = bullet (`-`), 3 = ordered (`1.` with a running counter that
resets when the list style changes or a non-list paragraph interrupts it); other
values render as plain body text.

BodyStorage gains the field-7 list-style runs and a pre-resolved marker per style.
renderParagraph now reports heading vs body so renderParagraphs (which holds the
counter) can apply markers; consecutive items of one list render tight (single
newline) while lists are separated from surrounding blocks by a blank line.

List membership follows the file's styles, so "Plain paragraph before list", which
the fixture styles as List Bullet, is a bullet. Nested-list indentation isn't
resolved yet (items render flat); noted as a follow-up. Validated against
sample.pages: a 2-item bullet list and a 3-item numbered list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mxnj6jvuLX9VS9JsJJbjmf

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for rendering bulleted and numbered lists from Apple iWork Pages documents into Markdown, including tight spacing for consecutive list items. The feedback highlights two issues: first, the Protobuf parser should handle both packed and unpacked representations of field 11 to ensure robust list marker detection; second, empty plain paragraphs should reset the ordered list counter to prevent consecutive lists of the same style from incorrectly merging their counters.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread Sources/PicoDocs/Converters/Pages/IWATable.swift
Comment thread Sources/PicoDocs/Converters/Pages/IWATable.swift Outdated
Two fixes from review:

- listMarker only handled an unpacked field 11 (one varint per level). A packed
  encoding (all levels in one length-delimited field) would arrive as .length and
  be skipped, silently dropping list markers. Handle both wire forms, reading
  level 0 either from the first varint field or the first varint of the packed
  bytes (firstPackedVarint).

- An empty non-list paragraph (a blank line) between two ordered lists of the same
  style left the ordered run active, so the second list continued the first's
  numbering. Reset the counter when a skipped (empty) paragraph isn't itself a list
  item; an empty list item still preserves the run.

Both are defensive against inputs the sample fixture doesn't exercise (packed
field 11, blank-line-separated same-style lists); existing tests are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mxnj6jvuLX9VS9JsJJbjmf

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2fabbaf669

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/PicoDocs/Converters/Pages/IWATable.swift Outdated
Comment on lines +261 to +265
parts.append(("- " + text, listStyle))
case .ordered?:
if listStyle != orderedList { counter = 0; orderedList = listStyle }
counter += 1
parts.append(("\(counter). " + text, listStyle))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Indent wrapped list item lines

These prefixes are applied to the rendered paragraph string as a whole, but renderInline intentionally preserves soft line breaks for PagesConverter.normalize to turn into \n. For a Pages list item containing a manual line break, only the first line gets the Markdown marker; the following line is unindented, and the repo parser only attaches continuation lines that start with two spaces (DocumentRenderer.swift lines 511-512), so HTML/plaintext rendering splits one list item into a list item plus a separate paragraph.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, but I'm deferring this one rather than half-fixing it. A 2-space continuation indent won't survive PagesConverter.normalize, which trims every line's leading whitespace — making multi-line items stick would need normalize to preserve list continuations, a broader change. The fixture's items are all single-line, so I've noted "multi-line list items" as a follow-up in the scope comment. Happy to prioritize it if you'd like it in this PR.


Generated by Claude Code

Comment thread Sources/PicoDocs/Converters/Pages/IWATable.swift Outdated
Comment on lines +263 to +265
if listStyle != orderedList { counter = 0; orderedList = listStyle }
counter += 1
parts.append(("\(counter). " + text, listStyle))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve explicit ordered-list starts

The ordered number is synthesized from a local counter that resets only when the list-style id changes. For Pages ordered lists that explicitly restart or start from a non-1 value while reusing the same list style, the converter rewrites the visible numbering because it never consults the document's per-paragraph numbering state; for example, a second adjacent list restarted at 1 is emitted as the next count from the previous list.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid limitation — the counter is synthesized and ignores explicit start/restart values. Reproducing the document's numbering needs the per-paragraph numbering state, which I haven't decoded yet (the fixture's list starts at 1, so it isn't exercised). Noted "explicit list-start numbers" as a follow-up in the scope comment; can tackle it separately if it matters for your inputs.


Generated by Claude Code

Two further list fixes from review:

- An empty paragraph between two same-style lists reset the ordered counter but
  the join still merged the items tight (1. a + blank + 1. b rendered as one
  list). renderParagraphs now records per-paragraph tightness at build time from
  the immediately preceding list paragraph, and an empty non-list paragraph clears
  it — so adjacent same-style lists restart their numbering and are set off by a
  blank line.

- listMarker scanned only the child ListStyle. A local override that inherits its
  marker array (field 11) from a parent lost its marker and fell back to plain
  text. It now resolves through the parent chain, the same visited-guarded walk
  styleName/characterTraits use.

Multi-line list-item continuation indentation and explicit list-start numbers are
noted as follow-ups (normalize trims continuation indents; per-paragraph numbering
state isn't decoded — neither is exercised by the fixture). Existing tests and the
fixture output are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mxnj6jvuLX9VS9JsJJbjmf

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf179ce0e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/PicoDocs/Converters/Pages/IWATable.swift
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.

2 participants