feat: add native link pills - #920
Open
hryhoriiK97 wants to merge 2 commits into
Open
hryhoriiK97 wants to merge 2 commits into
hryhoriiK97 wants to merge 2 commits into
Conversation
* feat: allow link variants to override font family * fix: cleanup; docs: storybook * feat: add opt-in native link variant pills * fix: render iOS pill attachment only at its first source character * fix: track pill glyph continuations without recursive layout queries * perf: skip iOS pill glyph copies and range scans for ordinary text * fix: address native link pill review findings * refactor(link-pill): render pills as a native attachment and add per-link content Rework the link pill presentation on top of the original implementation. iOS: a pill link is now a single attachment character laid out and drawn by TextKit, like images and inline math, instead of hidden link characters substituted through a custom text storage. The link's own text is kept on the attachment and put back wherever text leaves the view: copy, Markdown/HTML/RTF export, accessibility, and the system selection actions. Android: pills prepare their width per layout thread through a shared helper, are set directly while rendering, and are redrawn through a span change when an icon arrives or a spoiler over them is revealed. Both platforms: - `linkPillContent` prop: per-link label and icon keyed by exact URL - `pill` is a nested object on the native link variant - remote icons through the existing image pipeline, with bounded caches and a retry window for failed sources - links holding an image, inline math, a hard line break or a spoiler stay ordinary links; a pill inside an unrevealed spoiler is hidden - measurement caches account for link variants and pill content Also makes the shared Android image downloader report a request OkHttp rejects as a failed download instead of throwing into the render. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(storybook): add link pill story Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(ios): run the link pill tests in the example test target Add the pill attachment tests and the library header paths they import to EnrichedMarkdownExampleTests, and let the target inherit only the pods' search paths: linking the pods into the test bundle as well loaded every library class twice. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: document link pill content and behavior Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(example): match the Podfile checksum to the test target change Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore: keep Android unit tests out of the npm package The android folder is published whole, so android/src/test shipped with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Ernest <ernest.szlamczyk@swmansion.com> Co-authored-by: Ernest Szlamczyk <127619251+eszlamczyk@users.noreply.github.com> Co-authored-by: Gregory Moskaliuk <mosckalyuck@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Oct 4, 2026
…an attachment TextKit places an attachment glyph at the bottom edge of its bounds, not on the baseline. The marker drawer read the first glyph's location as the baseline, so an item that starts with a link pill, an inline image or inline math drew its bullet, number or checkbox too low by however far the attachment hangs below the text. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What/Why?
Opt-in pill presentation for Markdown links on iOS and Android. A link whose
markdownStyle.linkVariantsentry setspillis drawn as a rounded label with an optional icon; every other link is unchanged. Refs #860.feat/link-pillsis the integration branch for this feature. It lands onmainthrough this PR; the work itself is reviewed in smaller PRs that target the branch:pill.iconTintColor), replacing feat: support optional tint for native link pill icons #863.linkContextMenuItems, iOS 17+), replacing feat: add per-link native context menus on iOS #865. Stacked on feat(link-pill): add optional icon tint #921.CI runs here every time something is merged into the branch.
pill: trueuses the defaults;pill: { label, iconUri, borderRadius, paddingHorizontal, paddingVertical, borderWidth, borderColor, maxWidth }customizes it; omitted,falseornullkeeps an ordinary link. Colors, underline and font family stay on the link variant.linkPillContentprop sets the label and icon of individual links by exact URL. It is content, not style, somarkdownStylecan stay a stable object while it changes. Per-link content wins over the variant'spill.label/pill.iconUri, which win over the link text.http(s)sources load through the same pipeline as Markdown images, usingimageRequestHeaders, and the pill keeps room for them while they load. A source that fails shows no icon and is retried after 30 seconds.maxWidth. It works in paragraphs, headings, lists, blockquotes and table cells.pillandlinkPillContent.API
Docs:
docs/MENTIONS.md(pill section),docs/API_REFERENCE.md(linkPillContent),docs/WEB.md,docs/MACOS.md.Implementation
ENRMLinkPillAttachment), laid out and drawn by TextKit like images and inline math. The link's rendered text is kept on the attachment and put back byENRMLinkPillTextwherever text leaves the view.ENRMMarkdownTextViewdoes the same for the system selection actions. This replaces the custom text storage and glyph substitution of the first version.LinkPillSpanis aReplacementSpanover the original link text, which stays in the buffer. Widths are prepared per layout thread through a shared helper (prepareWidthAwareSpans), and a span change redraws the pill when its icon arrives or a spoiler over it is revealed.pillis a nested object on the native link variant;linkPillContentis passed likeimageRequestHeaders. Measurement caches on both platforms account for link variants and pill content.ImageDownloadernow reports a request OkHttp rejects as a failed download instead of throwing into the render. The example's iOS test target inherits only the pods' search paths, because linking the pods into the test bundle loaded every library class twice.android/src/testis excluded from the npm package (filesinpackage.json). Theandroidfolder is published whole, so the unit tests shipped with it; a dry run ofnpm packnow lists no test files.Testing
CI passed on the last head of #862 (
60896773): RN lint, library build, Android and iOS builds, Android and iOS unit tests. The branch holds the same content.link-pill-style.test.ts(style normalization, content normalization and equality).LinkPillSpanTest.kt.ENRMLinkPillAttachmentTests.mm, now registered in the example's test target and run by CI.Not covered: drag-and-drop of a selection containing a pill on iOS, and Maestro E2E. After
mainwas merged into the branch, the on-device check was repeated only for rendering on Android.PR Checklist
🤖 Generated with Claude Code