fix(app): restore assistant text selection in iPhone Safari (#7360)

The Markdown DOM container bypasses React Native Web style prefixing. Safari inherits the gesture host selection suppression unless the selectable boundary also sets WebkitUserSelect. Keep container styling in the existing style configuration so its selection contract is covered by the renderer tests.
This commit is contained in:
Mohamed Boudra
2026-10-19 10:44:28 +07:00
committed by GitHub
parent 727d5eed59
commit b50acc9344
3 changed files with 12 additions and 3 deletions
@@ -718,3 +718,10 @@ it("replaces the native placeholder only after the resolved URL decodes", () =>
const prepared = prepareMarkdownSource("![image](shot.png)", undefined, { supportsSvg: false });
expect(prepared.render(new Map([["shot.png", url]]))).toBe(`![image](<${url}>)`);
});
it("makes the web Markdown container selectable in Safari as well as unprefixed browsers", () => {
expect(createMarkdownStyles(lightTheme, { blockGap: 12 }).containerStyle).toEqual({
userSelect: "text",
WebkitUserSelect: "text",
});
});
@@ -27,7 +27,6 @@ const markdownStyle = (compact: boolean) => (theme: Theme, rt: { breakpoint?: st
createMarkdownStyles(theme, { compact, blockGap: resolveDensity(rt, density.blockGap) });
const regularStyle = markdownStyle(false);
const compactStyle = markdownStyle(true);
const WEB_CONTAINER = { userSelect: "text" as const };
export interface MarkdownRendererProps extends MarkdownImageContext {
text: string;
@@ -99,7 +98,6 @@ const MarkdownPart = memo(function MarkdownPart({
<ThemedMarkdown
key={`${segment.kind}:${segment.id}`}
markdown={segment.source}
containerStyle={isWeb ? WEB_CONTAINER : undefined}
uniProps={compact ? compactStyle : regularStyle}
flavor="github"
selectable
@@ -2,10 +2,13 @@ import type { MarkdownStyle } from "react-native-enriched-markdown";
import type { Theme } from "@/styles/theme";
import { isWeb } from "@/constants/platform";
// iOS Safari needs the prefix to override the gesture host's selection suppression.
const WEB_CONTAINER = { userSelect: "text", WebkitUserSelect: "text" } as const;
export function createMarkdownStyles(
theme: Theme,
{ compact = false, blockGap }: { compact?: boolean; blockGap: number },
): { markdownStyle: MarkdownStyle; selectionColor: string } {
): { markdownStyle: MarkdownStyle; selectionColor: string; containerStyle?: typeof WEB_CONTAINER } {
const fontSize = compact ? theme.fontSize.base : theme.fontSize.content;
// Every block is followed by the same gap; the library drops it after the last block.
const block = { marginTop: 0, marginBottom: blockGap };
@@ -23,6 +26,7 @@ export function createMarkdownStyles(
fontWeight: "bold",
});
return {
containerStyle: isWeb ? WEB_CONTAINER : undefined,
selectionColor: theme.colors.terminal.selectionBackground,
markdownStyle: {
paragraph,