Skip to content

fix(ios): preserve script semantics when copying Markdown - #849

Merged
eszlamczyk merged 2 commits into
software-mansion:mainfrom
pingdotgg:fix/ios-script-copy-semantics
Sep 22, 2026
Merged

eszlamczyk merged 2 commits into
software-mansion:mainfrom
pingdotgg:fix/ios-script-copy-semantics

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

What/Why?

Closes #847.

iOS partial-selection "Copy as Markdown" inferred script formatting from the sign of NSBaselineOffsetAttributeName. Paragraph line-height alignment also uses that attribute, so ordinary bold text could copy as **^bold^** rather than **bold**.

Add the private ENRMScriptAttributeName to the existing baseline-shift attributes, mark spans in SubscriptRenderer and SuperscriptRenderer, and read those marks in MarkdownExtractor. Visual baseline calculations are unchanged. The implementation is the five-file semantic fix from the downstream integration draft, adapted to the upstream monorepo.

Coverage stays with the existing examples and tests:

  • A React Native Maestro flow selects only the first word, copies via the native menu, pastes into the raw Markdown field, and checks exact clipboard text for plain, bold, superscript, and subscript content. The playground enables the existing script flags so these samples render as script spans.
  • The standalone Swift extractor already uses semantic script marks. Its existing XCTest suite now checks positive, zero, and negative visual offsets for plain/bold text and actual script spans, including bold superscript. These tests call attribute reconstruction directly so source slicing cannot hide a baseline-inference regression. They protect native parity; they do not execute the React Native Objective-C implementation.

No parser isolation, public API, or product-specific native behavior is included.

Testing

Passed locally on Linux:

  • yarn typecheck
  • yarn lint, with one existing inline-style warning in the web renderer
  • yarn test --runInBand: 6 suites, 39 tests
  • yarn prepare: module, types, and native codegen
  • clang-format --dry-run --Werror for the five changed Objective-C files
  • Prettier and YAML parsing for both new Maestro flows
  • Pre-commit lint/format and conventional commit checks

Additional example TypeScript diagnostics encounter existing example configuration/type errors. A targeted compiler diagnostic with the original playground source substituted produced the same 11 errors as the changed source. That separate diagnostic is not claimed green; the required library typecheck above passes.

The initial upstream CI run requires maintainer authorization for this fork contribution and has not run its checks. Native XCTest, SwiftLint, and the new Maestro flow have not been run from this upstream checkout. They require macOS/a coordinated simulator. No device, server, or remote build was started for this PR. The downstream patch was previously built on iOS and Android, and iOS partial-selection copy of bold was verified as **bold**, as recorded in the linked integration draft. That evidence is for the downstream patch, not a fresh run of this PR.

To run the focused native cases on a coordinated simulator:

cd packages/enriched-markdown-ios
xcodebuild test -scheme EnrichedMarkdown-Package \
  -destination "platform=iOS Simulator,id=$IOS_SIMULATOR_UDID" \
  -only-testing:EnrichedMarkdownTests/MarkdownExtractorTests/testTypographyBaselineOffsetsDoNotProduceScriptMarkdown \
  -only-testing:EnrichedMarkdownTests/MarkdownExtractorTests/testScriptMarkdownDoesNotDependOnVisualBaselineSign

After rebuilding the React Native example with this change, run the Objective-C clipboard flow:

maestro test --device "$IOS_SIMULATOR_UDID" .maestro/enrichedMarkdownText/flows/advanced/inline_elements/copy_markdown_script_test.yaml

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable, no API change
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added

Model: GPT-6. Harness: Codex.

@eszlamczyk eszlamczyk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi @juliusmarminge thanks for the pull request! Confirmed the root cause and the fix as working. This matches now the Android/Swift implementation and will be included in next release.

Approving this, few non-blocking notes below though.

Comment thread packages/react-native-enriched-markdown/ios/renderer/SuperscriptRenderer.m Outdated
@eszlamczyk
eszlamczyk merged commit 6c969a6 into software-mansion:main Sep 22, 2026
12 checks passed
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.

iOS Copy as Markdown mistakes typography baseline offsets for script spans

2 participants