Skip to content

feat(ios): render native Markdown heading hierarchy#103404

Merged
steipete merged 1 commit into
mainfrom
codex/ios-markdown-headings
Jul 10, 2026
Merged

feat(ios): render native Markdown heading hierarchy#103404
steipete merged 1 commit into
mainfrom
codex/ios-markdown-headings

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Fixes #98290

What Problem This Solves

Fixes an issue where Markdown headings in native Apple chat rendered at the same visual weight as ordinary paragraph text, so structured responses lost their heading hierarchy. Native table rendering from the same issue already landed in #100207.

Why This Change Was Made

The shared Apple Markdown segmenter now extracts only top-level CommonMark headings as dedicated blocks while preserving their complete ATX or Setext source for the existing Markdown parser. The renderer applies branded H1–H6 Dynamic Type styles and the accessibility header trait, while nested headings remain on the prose path and document-scoped reference links retain the existing whole-document fallback.

This avoids regex-based heading inference, preserves inline emphasis, links, code, and math behavior, and leaves the existing native table path unchanged.

User Impact

iOS and macOS chat responses now show a clear, accessible heading hierarchy that scales with Dynamic Type and remains compatible with inline formatting, streaming updates, containers, reference links, and adjacent tables.

Evidence

  • Exact head: 6e8eef750b9e2dd2f0dbda0e0e723c8fabd86fed.
  • Added focused coverage for ATX H1–H6, Setext H1/H2, inline attributes, surrounding prose, nested containers, reference fallback, streaming source, and unchanged table composition.
  • Added SwiftUI render smoke coverage at normal and accessibility Dynamic Type sizes with inline formatting and an adjacent native table.
  • swiftformat --lint --config config/swiftformat, native i18n, and git diff --check passed after rebasing onto current main.
  • Blacksmith Testbox-through-Crabbox tbx_01kx56fkq9h9pbyk99x9sncw69: remote pnpm check:changed passed.
  • Fresh full-branch Codex autoreview reported no accepted or actionable findings.

@openclaw-barnacle openclaw-barnacle Bot added app: ios App: ios size: S maintainer Maintainer-authored PR labels Jul 10, 2026

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

Copy link
Copy Markdown
Contributor

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: 6e8eef750b

ℹ️ 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".

.modifier(ChatInlineMathAccessibilityModifier(label: prose.inlineAccessibilityText))
case let .heading(level, prose):
self.proseText(prose, index: index)
.font(OpenClawChatTypography.heading(level: level))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Preserve thinking-segment typography for headings

When showsAssistantTrace is enabled, thinking segments deliberately pass OpenClawChatTypography.callout.italic() into ChatMarkdownRenderer from ChatMessageViews.swift:920 and ChatMessageViews.swift:1007; this heading path ignores that injected font and always applies large response heading typography. As a result, a visible trace like <think># Plan</think> now renders as a prominent assistant heading instead of the subdued italic thinking style, unlike the pre-change prose path. Please keep heading hierarchy relative to the caller-provided segment typography or pass the segment kind through before overriding it.

Useful? React with 👍 / 👎.

@steipete

Copy link
Copy Markdown
Contributor Author

Land-ready review completed for exact head 6e8eef750b9e2dd2f0dbda0e0e723c8fabd86fed.

What changed:

  • Native Apple chat now extracts top-level ATX and Setext headings through the existing CommonMark AST.
  • H1-H6 receive branded Dynamic Type typography and the accessibility header trait.
  • Nested list/blockquote headings remain on the prose path, reference-link whole-document fallback is preserved, and the existing native table path is unchanged.

Proof:

  • Blacksmith Testbox-through-Crabbox lease tbx_01kx56fkq9h9pbyk99x9sncw69: remote pnpm check:changed passed.
  • GitHub CI run 29070619261 completed successfully on the exact head, including ios-build, macos-swift, and native-i18n.
  • iOS Periphery run 29070619260 completed successfully on the exact head.
  • Focused coverage includes ATX H1-H6, Setext H1/H2, inline emphasis/link/code, surrounding prose, nested containers, reference fallback, streaming, adjacent native tables, and normal/accessibility Dynamic Type render smoke.
  • SwiftFormat, native i18n, git diff --check, review artifacts, and fresh full-branch autoreview passed with no findings.

Best-fix verdict: yes. The parser continues to own CommonMark structure and inline semantics; the SwiftUI renderer owns visual hierarchy and accessibility without adding a regex or a second Markdown interpretation path.

@steipete
steipete merged commit 0e4c7cd into main Jul 10, 2026
68 of 72 checks passed
@steipete
steipete deleted the codex/ios-markdown-headings branch July 10, 2026 05:21
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: ios App: ios maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: iOS app does not render markdown tables or headers (raw pipes shown)

1 participant