From 90452fc0fd1b62c8bdbd571bbaaceb4283ab37a1 Mon Sep 17 00:00:00 2001 From: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:51:17 -0400 Subject: [PATCH] refactor(mobile): name shared markdown renderer without iOS suffixes --- .../modules/t3-markdown-text/package.json | 2 +- ...nBlock.ios.tsx => NativeMarkdownBlock.tsx} | 2 +- ...s.tsx => NativeMarkdownSelectableText.tsx} | 0 .../src/SelectableMarkdownText.ios.tsx | 117 ------------------ .../src/SelectableMarkdownText.tsx | 108 +++++++++++++++- 5 files changed, 107 insertions(+), 122 deletions(-) rename apps/mobile/modules/t3-markdown-text/src/{NativeMarkdownBlock.ios.tsx => NativeMarkdownBlock.tsx} (99%) rename apps/mobile/modules/t3-markdown-text/src/{NativeMarkdownSelectableText.ios.tsx => NativeMarkdownSelectableText.tsx} (100%) delete mode 100644 apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.ios.tsx diff --git a/apps/mobile/modules/t3-markdown-text/package.json b/apps/mobile/modules/t3-markdown-text/package.json index 8922c8868c44..376befbeb152 100644 --- a/apps/mobile/modules/t3-markdown-text/package.json +++ b/apps/mobile/modules/t3-markdown-text/package.json @@ -25,7 +25,7 @@ "./links": "./src/markdownLinks.ts", "./markdown": "./src/nativeMarkdownText.ts", "./primitive": "./src/MarkdownTextPrimitive.tsx", - "./renderer": "./src/SelectableMarkdownText.ios.tsx", + "./renderer": "./src/SelectableMarkdownText.tsx", "./types": "./src/SelectableMarkdownText.types.ts" }, "peerDependencies": { diff --git a/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.ios.tsx b/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.tsx similarity index 99% rename from apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.ios.tsx rename to apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.tsx index 348a3c489a2c..ab75eaf32a2c 100644 --- a/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.ios.tsx +++ b/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownBlock.tsx @@ -5,7 +5,7 @@ import type { MarkdownNode } from "react-native-nitro-markdown/headless"; import { CopyTextButton } from "./CopyTextButton"; import { MarkdownTextPrimitive } from "./MarkdownTextPrimitive"; import { nativeMarkdownDocumentRuns, nativeMarkdownListItemBlocks } from "./nativeMarkdownText"; -import { NativeMarkdownSelectableText } from "./NativeMarkdownSelectableText.ios"; +import { NativeMarkdownSelectableText } from "./NativeMarkdownSelectableText"; import type { MarkdownCodeHighlighter, MarkdownHighlightedToken, diff --git a/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.ios.tsx b/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx similarity index 100% rename from apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.ios.tsx rename to apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx diff --git a/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.ios.tsx b/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.ios.tsx deleted file mode 100644 index 2a231c603584..000000000000 --- a/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.ios.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useMemo } from "react"; -import { View } from "react-native"; -import { parseMarkdownWithOptions } from "react-native-nitro-markdown/headless"; - -import { - nativeMarkdownChunkSpacing, - nativeMarkdownDocumentChunks, - nativeMarkdownDocumentRuns, - nativeMarkdownWithPreservedSoftBreaks, -} from "./nativeMarkdownText"; -import { MarkdownImageRendererContext, NativeMarkdownBlock } from "./NativeMarkdownBlock.ios"; -import { - MarkdownFileContextMenuContext, - NativeMarkdownSelectableText, - type MarkdownFileContextMenuHandlers, -} from "./NativeMarkdownSelectableText.ios"; -import type { - SelectableMarkdownSkill, - SelectableMarkdownTextProps, -} from "./SelectableMarkdownText.types"; - -const EMPTY_SKILLS: ReadonlyArray = []; - -export type { - MarkdownCodeHighlighter, - MarkdownHighlightedToken, - MarkdownImageRenderer, - MarkdownImageRequest, - NativeMarkdownTextStyle, - SelectableMarkdownSkill, - SelectableMarkdownTextProps, -} from "./SelectableMarkdownText.types"; - -export function hasNativeSelectableMarkdownText(): boolean { - return true; -} - -export function SelectableMarkdownText({ - markdown, - skills = EMPTY_SKILLS, - textStyle, - highlightCode, - preserveSoftBreaks = false, - onLinkPress, - fileContextMenu, - onFileContextMenuAction, - renderImage, - marginTop = 0, - marginBottom = 0, -}: SelectableMarkdownTextProps) { - const chunks = useMemo(() => { - const parsedDocument = parseMarkdownWithOptions(markdown, { - gfm: true, - html: true, - math: false, - }); - const document = preserveSoftBreaks - ? nativeMarkdownWithPreservedSoftBreaks(parsedDocument) - : parsedDocument; - return nativeMarkdownDocumentChunks(document).map((chunk) => - chunk.kind === "selectable" - ? { - ...chunk, - runs: nativeMarkdownDocumentRuns(chunk.node, skills), - } - : chunk, - ); - }, [markdown, preserveSoftBreaks, skills]); - - const fileContextMenuHandlers = useMemo( - () => - fileContextMenu && onFileContextMenuAction - ? { fileContextMenu, onFileContextMenuAction } - : null, - [fileContextMenu, onFileContextMenuAction], - ); - - return ( - - - {/* A percentage width here creates a cyclic intrinsic measurement inside - shrink-to-fit containers such as user-message bubbles. Yoga then gives - the native text node an unbounded second pass and the parent only clips - the resulting single-line width instead of reflowing it. */} - - {chunks.map((chunk, index) => { - const content = - chunk.kind === "rich" ? ( - - ) : ( - - ); - - return ( - - {content} - - ); - })} - - - - ); -} diff --git a/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.tsx b/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.tsx index 006d33e7259d..188a93b30e12 100644 --- a/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.tsx +++ b/apps/mobile/modules/t3-markdown-text/src/SelectableMarkdownText.tsx @@ -1,4 +1,25 @@ -import type { SelectableMarkdownTextProps } from "./SelectableMarkdownText.types"; +import { useMemo } from "react"; +import { View } from "react-native"; +import { parseMarkdownWithOptions } from "react-native-nitro-markdown/headless"; + +import { + nativeMarkdownChunkSpacing, + nativeMarkdownDocumentChunks, + nativeMarkdownDocumentRuns, + nativeMarkdownWithPreservedSoftBreaks, +} from "./nativeMarkdownText"; +import { MarkdownImageRendererContext, NativeMarkdownBlock } from "./NativeMarkdownBlock"; +import { + MarkdownFileContextMenuContext, + NativeMarkdownSelectableText, + type MarkdownFileContextMenuHandlers, +} from "./NativeMarkdownSelectableText"; +import type { + SelectableMarkdownSkill, + SelectableMarkdownTextProps, +} from "./SelectableMarkdownText.types"; + +const EMPTY_SKILLS: ReadonlyArray = []; export type { MarkdownCodeHighlighter, @@ -10,6 +31,87 @@ export type { SelectableMarkdownTextProps, } from "./SelectableMarkdownText.types"; -export function SelectableMarkdownText(_props: SelectableMarkdownTextProps) { - return null; +export function hasNativeSelectableMarkdownText(): boolean { + return true; +} + +export function SelectableMarkdownText({ + markdown, + skills = EMPTY_SKILLS, + textStyle, + highlightCode, + preserveSoftBreaks = false, + onLinkPress, + fileContextMenu, + onFileContextMenuAction, + renderImage, + marginTop = 0, + marginBottom = 0, +}: SelectableMarkdownTextProps) { + const chunks = useMemo(() => { + const parsedDocument = parseMarkdownWithOptions(markdown, { + gfm: true, + html: true, + math: false, + }); + const document = preserveSoftBreaks + ? nativeMarkdownWithPreservedSoftBreaks(parsedDocument) + : parsedDocument; + return nativeMarkdownDocumentChunks(document).map((chunk) => + chunk.kind === "selectable" + ? { + ...chunk, + runs: nativeMarkdownDocumentRuns(chunk.node, skills), + } + : chunk, + ); + }, [markdown, preserveSoftBreaks, skills]); + + const fileContextMenuHandlers = useMemo( + () => + fileContextMenu && onFileContextMenuAction + ? { fileContextMenu, onFileContextMenuAction } + : null, + [fileContextMenu, onFileContextMenuAction], + ); + + return ( + + + {/* A percentage width here creates a cyclic intrinsic measurement inside + shrink-to-fit containers such as user-message bubbles. Yoga then gives + the native text node an unbounded second pass and the parent only clips + the resulting single-line width instead of reflowing it. */} + + {chunks.map((chunk, index) => { + const content = + chunk.kind === "rich" ? ( + + ) : ( + + ); + + return ( + + {content} + + ); + })} + + + + ); }