fix(app): render inline links via UITextView span on iOS (#1257)

AssistantMarkdownLink's native branch wrapped its children in a plain RN
<Text>. On iOS that <Text> sits inside the paragraph's native UITextView
and is not hoisted into a UITextViewChild, so link text (markdown links,
bare URLs, and inline-code path links) was silently dropped — the same
root cause as the bold/italic/strikethrough fix in this PR.

Route the native link through MarkdownTextSpan so it composes as a
UITextViewChild and stays visible + selectable. MarkdownTextSpan now
forwards onPress/accessibilityRole across platforms: tappable on
Android/web; on iOS react-native-uitextview nulls onPress on the root
native view, so reliable tap-to-open is still tracked by #21 — but
visible, selectable, copyable link text strictly beats an invisible one.

Refs #1253

Co-authored-by: Geoffrey <geoffrey.marc@consulting-for.edenred.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Geoffrey Marc
2026-06-01 11:29:59 +08:00
committed by GitHub
co-authored by Geoffrey Claude Opus 4.8
parent 632c48fde3
commit a3b0af9ac0
4 changed files with 76 additions and 13 deletions
@@ -1,11 +1,23 @@
import { useMemo, type ReactNode } from "react";
import { Text, View, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
import {
Text,
View,
type StyleProp,
type TextProps,
type TextStyle,
type ViewStyle,
} from "react-native";
import { CODE_SURFACE_DATASET } from "@/styles/code-surface";
interface MarkdownTextSpanProps {
style?: StyleProp<TextStyle>;
monoSurface?: boolean;
children: ReactNode;
// Web links use the <a>/Pressable path in link.tsx, not this span, so these
// are accepted for prop-shape parity with the native variants and forwarded
// harmlessly.
onPress?: TextProps["onPress"];
accessibilityRole?: TextProps["accessibilityRole"];
}
// react-native-web renders Text as <span>/<div> with `user-select: text`
@@ -13,9 +25,20 @@ interface MarkdownTextSpanProps {
// react-native-uitextview: its transitive import of codegenNativeComponent
// pulls in setUpReactDevTools, which doesn't resolve under Metro's web
// target in dev mode.
export function MarkdownTextSpan({ style, monoSurface, children }: MarkdownTextSpanProps) {
export function MarkdownTextSpan({
style,
monoSurface,
children,
onPress,
accessibilityRole,
}: MarkdownTextSpanProps) {
return (
<Text dataSet={monoSurface ? CODE_SURFACE_DATASET : undefined} style={style}>
<Text
dataSet={monoSurface ? CODE_SURFACE_DATASET : undefined}
style={style}
onPress={onPress}
accessibilityRole={accessibilityRole}
>
{children}
</Text>
);