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 { NativeMarkdownSelectableText } 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, 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]); 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} ); })} ); }