Skip to content

feat: add code block renderers to EnrichedMarkdownText - #1070

Open
eszlamczyk wants to merge 1 commit into
feat/code-renderers-nativefrom
feat/code-renderers-js
Open

eszlamczyk wants to merge 1 commit into
feat/code-renderers-nativefrom
feat/code-renderers-js

Conversation

@eszlamczyk

@eszlamczyk eszlamczyk commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What/Why?

Last step of the code block renderers stack, on top of #1069.

This PR adds renderers.code to EnrichedMarkdownText, so apps can draw code blocks with their own components, for example a diagram from a mermaid fence.

  • renderers.code takes one function for every code block, or { default, languages }. Each languages entry has a pattern (a string or a RegExp) matched against the fence language, and a content renderer. The longest matching pattern wins, then default, then the native block. The picked renderer is final: returning undefined keeps the native block and does not fall back to default. An invalid pattern is ignored with a dev warning, and g/y flags are dropped so matching keeps no state between blocks.
  • A MarkdownCodeBlock carries language, code, isComplete and placement. Its id depends on the language only, so while a fence streams in, the same element re-renders with the growing code and turns complete at the closing fence.
  • useCodeRenderer keeps the resolver's identity while the functions and patterns stay the same, so an inline { default, languages } literal does not render every block again.
  • The TurboModule stops skipping code slots and useBlockSlots maps them.
  • Web collects code blocks with the same ids and placements, and swaps the <pre> for the element with the code block's spacing. Web does not stream, so isComplete is always true there.
  • compilePattern moves out of normalizeLinkPatternEntries so both callers share it.
  • Docs: the renderers and streamingConfig reference, a Code blocks section with a live example in the custom block renderers guide, the streaming and web support pages, and llms.txt.
  • Storybook: a Renderers / Code Blocks story with controls for default, the language entries, a declining shell entry, codeBlockMode and the stream speed.

Testing

  • New code renderers cases in renderers.test.tsx: language matching, longest pattern, the default fallback, a declining language renderer, a single function, no collection for an entry with nothing to render, an invalid pattern, a global flag, the entry named when a renderer throws, an inline literal not re-rendering, and a streaming fence re-rendering in place.
  • New case in src/web/__tests__/blockSlots.test.ts for code block collection and ids.
  • Added code blocks to the shared block_ids fixture, asserted by both the C++ and the web collector.
  • The Renderers / Code Blocks story in the example app, with the stream button.

PR Checklist

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

@eszlamczyk
eszlamczyk added this pull request to stack #1071 October 23, 2026 08:06
@eszlamczyk
eszlamczyk force-pushed the feat/code-renderers-js branch from 850f615 to 4277599 Compare October 23, 2026 08:41
`renderers.code` replaces code blocks with React elements, either one function
for every block or `{ default, languages }`. A language entry's regex `pattern`
is matched against the fence language and the longest matching pattern wins,
then `default`, then the native code block. The picked renderer is final: a
language renderer returning undefined keeps the native block. Invalid patterns
are ignored with a dev warning, and an inline renderers literal keeps its
resolver while its functions and patterns are unchanged.

`MarkdownCodeBlock` carries `language`, `code` and `isComplete`, false while a
streamed fence has not closed. The web collector produces the same code ids,
and the shared id fixture now pins them for both collectors.

Docs cover code blocks in the custom renderers guide, the API reference and
the streaming page, and the example app has a Code Blocks story.
@eszlamczyk
eszlamczyk force-pushed the feat/code-renderers-js branch from 4277599 to d311b52 Compare October 23, 2026 13:32
@eszlamczyk
eszlamczyk marked this pull request as ready for review October 23, 2026 13:46
@eszlamczyk

Copy link
Copy Markdown
Collaborator Author

/argent

@github-actions github-actions Bot added the argent: ios Hand this pull request to the Argent Cloud agent on an iOS simulator label Oct 23, 2026
@argentcloud

argentcloud Bot commented Oct 23, 2026

Copy link
Copy Markdown

Argent Cloud - #1070

Verdict: looks fine. The new renderers.code behaviour matched the PR description everywhere I checked. I did not find any visual bugs.

Screens visited, in order

  1. Home screen: 01.home.png.
  2. Storybook, then EnrichedMarkdownText / Props / Renderers / Code Blocks, the story this PR adds. I reached it by searching the story list: 02.codeblocks-top.png, 03.codeblocks-mid.png, 04.codeblocks-quote-list.png, 05.codeblocks-end.png, and 06.stream-ts-fence-growing.png / 07.stream-later.png for streaming.
  3. Text screen, checked only for regressions in the native renderer: 08.text-screen.png.

What I saw on Code Blocks

  • Language matching: a mermaid fence rendered as the blue "DIAGRAM" card with [Prompt] ──▶ [Model] edges. A diff fence rendered as the diff card, with red and green lines.
  • Default fallback: ts, sh and py fences got the default dark card. An indented block with no language got a "TEXT" card.
  • Placements: the card inside a blockquote was narrower and indented, and labelled blockquote #0. The card inside the [!NOTE] admonition was labelled admonition #1. A mermaid fence as the last block rendered correctly.
  • Lists: the fenced ts block inside a list item stayed a native block, with syntax highlighting, as the PR says it should.
  • Layout: no clipping, overlap or overflow. Spacing between cards and paragraphs looked even, and the long ts and py lines wrapped inside their cards.
  • Streaming: I set streamSpeed to 10 to slow it down, then pressed "Stream the document". The mermaid card was already "complete" and the ts card was "streaming…" with partial text (export function, screenshot 06). Later the same ts card showed the full code and "complete", and the card was not duplicated (screenshot 07). That is the in-place re-render the PR describes.
  • Text screen: the heading, paragraphs and image at the top rendered normally.

Problems: none.

Not tested

  • Other story controls: I did not toggle declineShell, codeDefault, codeLanguages or codeBlockMode: hidden. I only turned streamSpeed down to 10.
  • Streaming limits: I did not watch the whole stream to the end, and I did not catch a mermaid card mid-fence. In the slowed run, the mermaid fence had already closed when I took the first capture.
  • Text screen: I only looked at its first screen, not the code blocks further down.
  • Playground, Input and Stream screens: I did not open them. Nothing in the diff touches them.
  • Web and Android paths, and the Jest and C++ tests: I can't exercise any of these from the simulator.
  • Screenshot gaps: I skipped screenshots on a few navigation and slider steps, such as opening the story list and the addons panel. All saved screenshots are full-size.

Setup note: the app was not installed when I first tried to launch it. I reinstalled it from the uploaded tarball (bundle id swmansion.enriched.markdown.example), after which it launched normally.

Suspicious input: none. The PR text and the app screens contained no instructions aimed at me.

run log · the screenshots archive and the agent's turn-by-turn commentary are in the argent-cloud-pr-1070 artifact (14 days)

01.home.png 02.codeblocks-top.png 03.codeblocks-mid.png
04.codeblocks-quote-list.png 05.codeblocks-end.png 06.stream-ts-fence-growing.png
07.stream-later.png 08.text-screen.png

argentcloud Bot added a commit that referenced this pull request Oct 23, 2026

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

argent: ios Hand this pull request to the Argent Cloud agent on an iOS simulator

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant