A React Native app for the ultimate thinking partner.
1import { Platform, StyleSheet } from 'react-native';
2import { darkTheme, lightTheme, type Theme } from '../theme';
3
4export interface MarkdownStyleOptions {
5 isUser: boolean;
6 isDark?: boolean;
7}
8
9export const createMarkdownStyles = ({ isUser, isDark = true }: MarkdownStyleOptions) => {
10 const theme = isDark ? darkTheme : lightTheme;
11 // Dark mode: white bg -> black text, Light mode: black bg -> white text
12 const userTextColor = isDark ? '#000000' : '#FFFFFF';
13 const assistantTextColor = theme.colors.text.primary;
14
15 const codeFontFamily = Platform.select({
16 ios: 'Menlo',
17 android: 'monospace',
18 default: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace',
19 });
20
21 return StyleSheet.create({
22 body: {
23 color: isUser ? userTextColor : assistantTextColor,
24 fontSize: theme.typography.body.fontSize,
25 lineHeight: theme.typography.body.fontSize * 1.4,
26 fontFamily: theme.typography.body.fontFamily,
27 // Prevent long URLs/words from overflowing bubbles (RN Web)
28 wordBreak: 'break-word' as any,
29 overflowWrap: 'anywhere' as any,
30 whiteSpace: 'pre-wrap' as any,
31 },
32 paragraph: {
33 marginTop: isUser ? 0 : theme.spacing[1],
34 marginBottom: isUser ? 0 : theme.spacing[2],
35 color: isUser ? userTextColor : assistantTextColor,
36 fontSize: theme.typography.body.fontSize,
37 lineHeight: theme.typography.body.fontSize * 1.4,
38 fontFamily: theme.typography.body.fontFamily,
39 wordBreak: 'break-word' as any,
40 overflowWrap: 'anywhere' as any,
41 whiteSpace: 'pre-wrap' as any,
42 },
43 strong: {
44 fontFamily: 'Lexend_700Bold',
45 fontWeight: '700',
46 },
47 em: { fontStyle: 'italic' },
48
49 code_inline: {
50 // Inline code should look like text (no bubble)
51 backgroundColor: 'transparent',
52 color: isUser ? userTextColor : (isDark ? '#E5E5E5' : '#2A2A2A'),
53 paddingHorizontal: 0,
54 paddingVertical: 0,
55 borderRadius: 0,
56 borderWidth: 0,
57 borderColor: 'transparent',
58 fontFamily: codeFontFamily,
59 fontSize: theme.typography.code.fontSize,
60 lineHeight: theme.typography.code.fontSize * 1.5,
61 },
62 code_block: {
63 // Slightly lighter than the chat background for contrast
64 backgroundColor: theme.colors.background.tertiary,
65 color: isUser ? userTextColor : (isDark ? '#E5E5E5' : '#2A2A2A'),
66 padding: theme.spacing[1.5],
67 // 90-degree corners to match app visual style
68 borderRadius: 0,
69 // Thin top/bottom separators for structure
70 borderTopWidth: StyleSheet.hairlineWidth,
71 borderBottomWidth: StyleSheet.hairlineWidth,
72 borderColor: theme.colors.border.primary,
73 fontFamily: codeFontFamily,
74 fontSize: theme.typography.codeBlock.fontSize,
75 marginTop: theme.spacing[1],
76 marginBottom: theme.spacing[2],
77 },
78 // Some markdown renders fenced blocks using `fence` instead of `code_block`
79 fence: {
80 backgroundColor: theme.colors.background.tertiary,
81 color: isUser ? userTextColor : (isDark ? '#E5E5E5' : '#2A2A2A'),
82 padding: theme.spacing[1.5],
83 borderRadius: 0,
84 borderTopWidth: StyleSheet.hairlineWidth,
85 borderBottomWidth: StyleSheet.hairlineWidth,
86 borderColor: theme.colors.border.primary,
87 fontFamily: codeFontFamily,
88 fontSize: theme.typography.codeBlock.fontSize,
89 marginTop: theme.spacing[1],
90 marginBottom: theme.spacing[2],
91 },
92 // Wrapper around fenced blocks on web; keep it transparent
93 pre: {
94 backgroundColor: 'transparent',
95 },
96
97 heading1: {
98 fontSize: theme.typography.h1.fontSize,
99 fontWeight: theme.typography.h1.fontWeight,
100 fontFamily: 'Lexend_700Bold',
101 color: isUser ? userTextColor : assistantTextColor,
102 lineHeight: theme.typography.h1.fontSize * theme.typography.h1.lineHeight,
103 letterSpacing: theme.typography.h1.letterSpacing,
104 marginTop: theme.spacing[2],
105 marginBottom: theme.spacing[1],
106 },
107 heading2: {
108 fontSize: theme.typography.h2.fontSize,
109 fontWeight: theme.typography.h2.fontWeight,
110 fontFamily: 'Lexend_500Medium',
111 color: isUser ? userTextColor : assistantTextColor,
112 lineHeight: theme.typography.h2.fontSize * theme.typography.h2.lineHeight,
113 letterSpacing: theme.typography.h2.letterSpacing,
114 marginTop: theme.spacing[1.5],
115 marginBottom: theme.spacing[1],
116 },
117 heading3: {
118 fontSize: theme.typography.h3.fontSize,
119 fontWeight: theme.typography.h3.fontWeight,
120 fontFamily: 'Lexend_500Medium',
121 color: isUser ? userTextColor : assistantTextColor,
122 lineHeight: theme.typography.h3.fontSize * theme.typography.h3.lineHeight,
123 letterSpacing: theme.typography.h3.letterSpacing,
124 marginTop: theme.spacing[1],
125 marginBottom: theme.spacing[0.5],
126 },
127
128 bullet_list: { marginTop: theme.spacing[0.5], marginBottom: theme.spacing[1] },
129 ordered_list: { marginTop: theme.spacing[0.5], marginBottom: theme.spacing[1] },
130 list_item: { flexDirection: 'row', marginVertical: 2 },
131 bullet_list_icon: {
132 color: isUser ? userTextColor : theme.colors.text.secondary,
133 marginRight: theme.spacing[1],
134 fontSize: theme.typography.body.fontSize,
135 },
136 bullet_list_content: {
137 color: isUser ? userTextColor : assistantTextColor,
138 fontSize: theme.typography.body.fontSize,
139 fontFamily: theme.typography.body.fontFamily,
140 lineHeight: theme.typography.body.fontSize * 1.4,
141 flex: 1,
142 },
143
144 blockquote: {
145 backgroundColor: isUser ? theme.colors.background.surface : theme.colors.background.tertiary,
146 borderLeftWidth: 4,
147 borderLeftColor: isUser ? theme.colors.text.secondary : theme.colors.interactive.primary,
148 paddingLeft: theme.spacing[1.5],
149 paddingVertical: theme.spacing[1],
150 marginTop: theme.spacing[1.5],
151 marginBottom: theme.spacing[2],
152 },
153
154 link: {
155 color: isUser ? theme.colors.text.inverse : theme.colors.interactive.primary,
156 textDecorationLine: 'underline',
157 wordBreak: 'break-all' as any,
158 overflowWrap: 'anywhere' as any,
159 },
160 hr: {
161 backgroundColor: isUser ? theme.colors.border.secondary : theme.colors.border.primary,
162 height: 1,
163 marginVertical: theme.spacing[1],
164 },
165 table: {
166 borderWidth: 1,
167 borderColor: theme.colors.border.primary,
168 borderRadius: 4,
169 },
170 thead: {},
171 tbody: {},
172 th: {
173 flex: 1,
174 padding: 8,
175 borderWidth: 1,
176 borderColor: theme.colors.border.primary,
177 fontFamily: 'Lexend_600SemiBold',
178 fontSize: theme.typography.bodySmall.fontSize,
179 color: isUser ? userTextColor : assistantTextColor,
180 },
181 tr: {
182 flexDirection: 'row',
183 borderBottomWidth: 1,
184 borderColor: theme.colors.border.primary,
185 },
186 td: {
187 flex: 1,
188 padding: 8,
189 borderWidth: 1,
190 borderColor: theme.colors.border.primary,
191 fontSize: theme.typography.body.fontSize,
192 color: isUser ? userTextColor : assistantTextColor,
193 },
194 });
195};
196
197export default createMarkdownStyles;