fix(app): restore assistant text selection on web (#140)

This commit is contained in:
Mohamed Boudra
2026-03-24 12:01:08 +08:00
committed by GitHub
parent af8509d012
commit c6aff35db2
3 changed files with 57 additions and 1 deletions
+2 -1
View File
@@ -420,6 +420,7 @@ export const assistantMessageStylesheet = StyleSheet.create((theme) => ({
color: theme.colors.foreground, color: theme.colors.foreground,
fontFamily: Fonts.mono, fontFamily: Fonts.mono,
fontSize: 13, fontSize: 13,
userSelect: Platform.OS === "web" ? "text" : "auto",
}, },
})); }));
@@ -821,7 +822,7 @@ export const AssistantMessage = memo(function AssistantMessage({
<Text <Text
key={node.key} key={node.key}
onPress={() => parsed && onInlinePathPress?.(parsed)} onPress={() => parsed && onInlinePathPress?.(parsed)}
selectable={false} selectable={Platform.OS === "web" ? undefined : false}
style={[assistantMessageStylesheet.pathChip, assistantMessageStylesheet.pathChipText]} style={[assistantMessageStylesheet.pathChip, assistantMessageStylesheet.pathChipText]}
> >
{content} {content}
@@ -37,4 +37,36 @@ describe("createMarkdownStyles", () => {
overflowWrap: "anywhere", overflowWrap: "anywhere",
}); });
}); });
it("keeps assistant markdown text selectable on web", () => {
const styles = createMarkdownStyles(darkTheme);
expect(styles.body).toMatchObject({
userSelect: "text",
});
expect(styles.text).toMatchObject({
userSelect: "text",
});
expect(styles.heading1).toMatchObject({
userSelect: "text",
});
expect(styles.link).toMatchObject({
userSelect: "text",
});
expect(styles.code_inline).toMatchObject({
userSelect: "text",
});
expect(styles.code_block).toMatchObject({
userSelect: "text",
});
expect(styles.fence).toMatchObject({
userSelect: "text",
});
expect(styles.bullet_list_icon).toMatchObject({
userSelect: "text",
});
expect(styles.ordered_list_icon).toMatchObject({
userSelect: "text",
});
});
}); });
@@ -1,6 +1,9 @@
import { Platform } from "react-native";
import type { Theme } from "./theme"; import type { Theme } from "./theme";
import { Fonts } from "@/constants/theme"; import { Fonts } from "@/constants/theme";
const webSelectableTextStyle = Platform.OS === "web" ? { userSelect: "text" as const } : {};
/** /**
* Creates comprehensive markdown styles for react-native-markdown-display. * Creates comprehensive markdown styles for react-native-markdown-display.
* *
@@ -15,6 +18,7 @@ export function createMarkdownStyles(theme: Theme) {
// ========================================================================= // =========================================================================
body: { body: {
...webSelectableTextStyle,
color: theme.colors.foreground, color: theme.colors.foreground,
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
lineHeight: 22, lineHeight: 22,
@@ -24,6 +28,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
text: { text: {
...webSelectableTextStyle,
color: theme.colors.foreground, color: theme.colors.foreground,
flexShrink: 1, flexShrink: 1,
minWidth: 0, minWidth: 0,
@@ -47,6 +52,7 @@ export function createMarkdownStyles(theme: Theme) {
// ========================================================================= // =========================================================================
heading1: { heading1: {
...webSelectableTextStyle,
fontSize: theme.fontSize["3xl"], fontSize: theme.fontSize["3xl"],
fontWeight: theme.fontWeight.bold, fontWeight: theme.fontWeight.bold,
color: theme.colors.foreground, color: theme.colors.foreground,
@@ -59,6 +65,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
heading2: { heading2: {
...webSelectableTextStyle,
fontSize: theme.fontSize["2xl"], fontSize: theme.fontSize["2xl"],
fontWeight: theme.fontWeight.bold, fontWeight: theme.fontWeight.bold,
color: theme.colors.foreground, color: theme.colors.foreground,
@@ -71,6 +78,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
heading3: { heading3: {
...webSelectableTextStyle,
fontSize: theme.fontSize.xl, fontSize: theme.fontSize.xl,
fontWeight: theme.fontWeight.semibold, fontWeight: theme.fontWeight.semibold,
color: theme.colors.foreground, color: theme.colors.foreground,
@@ -80,6 +88,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
heading4: { heading4: {
...webSelectableTextStyle,
fontSize: theme.fontSize.lg, fontSize: theme.fontSize.lg,
fontWeight: theme.fontWeight.semibold, fontWeight: theme.fontWeight.semibold,
color: theme.colors.foreground, color: theme.colors.foreground,
@@ -89,6 +98,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
heading5: { heading5: {
...webSelectableTextStyle,
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold, fontWeight: theme.fontWeight.semibold,
color: theme.colors.foreground, color: theme.colors.foreground,
@@ -98,6 +108,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
heading6: { heading6: {
...webSelectableTextStyle,
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold, fontWeight: theme.fontWeight.semibold,
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
@@ -113,19 +124,23 @@ export function createMarkdownStyles(theme: Theme) {
// ========================================================================= // =========================================================================
strong: { strong: {
...webSelectableTextStyle,
fontWeight: theme.fontWeight.medium, fontWeight: theme.fontWeight.medium,
}, },
em: { em: {
...webSelectableTextStyle,
fontStyle: "italic" as const, fontStyle: "italic" as const,
}, },
s: { s: {
...webSelectableTextStyle,
textDecorationLine: "line-through" as const, textDecorationLine: "line-through" as const,
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
}, },
link: { link: {
...webSelectableTextStyle,
color: theme.colors.accentBright, color: theme.colors.accentBright,
textDecorationLine: "none" as const, textDecorationLine: "none" as const,
flexShrink: 1, flexShrink: 1,
@@ -134,6 +149,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
blocklink: { blocklink: {
...webSelectableTextStyle,
color: theme.colors.accentBright, color: theme.colors.accentBright,
textDecorationLine: "none" as const, textDecorationLine: "none" as const,
flexShrink: 1, flexShrink: 1,
@@ -146,6 +162,7 @@ export function createMarkdownStyles(theme: Theme) {
// ========================================================================= // =========================================================================
code_inline: { code_inline: {
...webSelectableTextStyle,
backgroundColor: theme.colors.surface2, backgroundColor: theme.colors.surface2,
color: theme.colors.foreground, color: theme.colors.foreground,
paddingHorizontal: theme.spacing[1], paddingHorizontal: theme.spacing[1],
@@ -157,6 +174,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
code_block: { code_block: {
...webSelectableTextStyle,
backgroundColor: theme.colors.surface2, backgroundColor: theme.colors.surface2,
color: theme.colors.foreground, color: theme.colors.foreground,
padding: theme.spacing[3], padding: theme.spacing[3],
@@ -167,6 +185,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
fence: { fence: {
...webSelectableTextStyle,
backgroundColor: theme.colors.surface2, backgroundColor: theme.colors.surface2,
color: theme.colors.foreground, color: theme.colors.foreground,
padding: theme.spacing[3], padding: theme.spacing[3],
@@ -200,6 +219,7 @@ export function createMarkdownStyles(theme: Theme) {
tbody: {}, tbody: {},
th: { th: {
...webSelectableTextStyle,
padding: theme.spacing[2], padding: theme.spacing[2],
borderBottomWidth: 1, borderBottomWidth: 1,
borderRightWidth: 1, borderRightWidth: 1,
@@ -218,6 +238,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
td: { td: {
...webSelectableTextStyle,
padding: theme.spacing[2], padding: theme.spacing[2],
borderRightWidth: 1, borderRightWidth: 1,
borderColor: theme.colors.border, borderColor: theme.colors.border,
@@ -260,6 +281,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
bullet_list_icon: { bullet_list_icon: {
...webSelectableTextStyle,
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
marginRight: 4, marginRight: 4,
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
@@ -267,6 +289,7 @@ export function createMarkdownStyles(theme: Theme) {
}, },
ordered_list_icon: { ordered_list_icon: {
...webSelectableTextStyle,
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
marginRight: 4, marginRight: 4,
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,