Skip to content

feat: report the document's links through onLinksChange - #1088

Open
juliusmarminge wants to merge 1 commit into
software-mansion:mainfrom
pingdotgg:feat/link-occurrences
Open

juliusmarminge wants to merge 1 commit into
software-mansion:mainfrom
pingdotgg:feat/link-occurrences

Conversation

@juliusmarminge

Copy link
Copy Markdown
Contributor

What/Why?

linkPillContent, markdownStyle.linkVariants and linkContextMenuItems are all keyed by link URL, but an app has no way to learn which links a document contains. It can only configure links it predicts, or parse the Markdown a second time with its own rules, which disagree with md4c on bare URLs, www. and mailto: rewrites, escapes and code spans.

In our app, T3 Code, every file reference in an agent message is shown as a pill with the file's name and icon, and every link gets its own long-press menu. That needs the document's real link URLs.

This adds onLinksChange. It reports the links from the parse that #1026 already shares with renderers and resolveImageSource:

  • Collector: collect in BlockSlots.cpp also records every Link node, with URL, label and title, in document order. That covers paragraphs, headings, lists, tables, quotes and link blocks. It is the same walk, so there's no second parse.
  • Bridge: NativeEnrichedMarkdownParser.collect returns links.
  • Prop: onLinksChange(links) fires from a layout effect once on mount, then whenever the links change by value. A state update made in it lands before the frame is drawn. A streaming tick that adds no link reports nothing, and a new handler function doesn't report again, the same as resolveImageSource.
  • Web: web/blockSlots.ts collects the same links.

Links that linkRecognition adds are not reported. Recognition runs in the native converters as the text is drawn, not in the shared parse. Apps that use linkRecognition.text know their own patterns, so we match those ourselves. Including recognized links would mean running the recognizers inside collect, which seemed better as a separate discussion.

A side note: MarkdownLinkBlock.label is documented as "" for a bare URL or autolink, but both collectors return the URL as written, for example www.example.com. I documented MarkdownLinkOccurrence.label to match that behavior. I left the existing block doc alone.

Testing

  • scripts/test-core-parser.sh: the new block_slots_test.cpp checks cover links in headings, text, lists, tables and quotes, autolinks and bare URLs, labels, titles, code spans and fences (no links), and repeated links.
  • Jest: the new tests cover the web collector against the same document as the C++ test, and onLinksChange reporting once and then only on change, also across a handler change (1791 tests pass). tsc and yarn lint pass, and node scripts/test-web-bundle.mjs passes.
  • iOS: apps/react-native-example/scripts/test-ios.sh (27 tests) builds the regenerated parser spec.
  • Android: the example app assembles with the new parser spec.
  • Downstream: T3 Code's mobile app uses onLinksChange for its link pills and iOS link menus. In its build I verified file pills in text, a list and a table, a host icon on a heading link, and the iOS long-press menu keyed by the reported URL. The same message uses renderers.image for root and quoted images and resolveImageSource for list and inline images. iOS 27 and Android 17:
iOS iOS link menu Android

Building that app on top of #1027 also needed #1086 (iOS with video off) and #1087 (Android include collision), opened separately.

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable: onLinksChange in the API reference
  • Ran example app to verify changes, verified in a downstream app instead
  • E2E tests are passing, Maestro suite not run
  • Required E2E tests have been added (if applicable)

Model: Claude Opus 5.5. Harness: Claude Code in T3 Code.

linkPillContent, markdownStyle.linkVariants and linkContextMenuItems are
keyed by link URL, but an app had no way to learn which links a document
contains, so it could only configure links it predicted. The collector
behind renderers and resolveImageSource now also records every Link node
(URL, label, title) in document order, and onLinksChange reports them
from a layout effect when they change, so a state update made in it lands
before the frame is drawn. The web collector reports the same links.

Links that linkRecognition adds are not reported: recognition runs in the
native converters as the text is drawn, not in the shared parse.

This branch has not been deployed

No deployments
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.

1 participant