@codearcade/expo-markdown-native 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,37 +1,152 @@
1
- # @codearcade/expo-markdown-native-markdown
2
1
 
3
- Native Markdown rendering for Expo & React Native with built-in syntax highlighting.
2
+ # @codearcade/expo-markdown-native 🚀
4
3
 
5
- ## Installation
4
+ A powerful, fully customizable, pure JavaScript/TypeScript Markdown renderer for React Native and Expo.
6
5
 
6
+ Featuring beautiful native syntax highlighting, interactive code blocks, copy-to-clipboard functionality, and dark mode support out of the box.
7
7
 
8
- ```sh
9
- npm install @codearcade/expo-markdown-native-markdown
8
+ ## ✨ Features (v2.0)
9
+
10
+ * **100% Pure JS/TS:** No native linking required! Works seamlessly with Expo Go and bare React Native.
11
+ * **Beautiful Code Blocks:** Built-in syntax highlighting using highlight.js.
12
+ * **Standalone CodeBlock:** Exported <CodeBlock /> component for when you just need to highlight a snippet without parsing full markdown.
13
+ * **Highly Customizable:** Style every single markdown node, plus granular control over code block containers, headers, and padding via the new codeStyle prop.
14
+ * **Interactive:** Built-in support for intercepting links and copying code to the clipboard.
15
+
16
+ ## 📦 Installation
17
+
18
+ ```bash
19
+ npm install @codearcade/expo-markdown-native
20
+ # or
21
+ yarn add @codearcade/expo-markdown-native
22
+ ```
23
+
24
+ _Note: If you haven't already, you may need to install peer dependencies depending on your package manager: react-native-svg and react-native-safe-area-context._
25
+
26
+ ## 🚀 Quick Start
27
+ ```ts
28
+ import React from 'react';
29
+ import { SafeAreaView, Alert } from 'react-native';
30
+ import { Markdown } from '@codearcade/expo-markdown-native';
31
+
32
+ const content = \`
33
+ \# Hello World
34
+ This is a \*\*bold\*\* statement.
35
+
36
+ Here is some code:
37
+ \\\`\\\`\\\`javascript
38
+ const greeting = "Hello, React Native!";
39
+ console.log(greeting);
40
+ \\\`\\\`\\\`
41
+ \`;
42
+
43
+ export default function App() {
44
+ return (
45
+ <SafeAreaView style={{ flex: 1, padding: 20 }}>
46
+ <Markdown
47
+ content={content}
48
+ theme="dark"
49
+ onCopy={(text) => Alert.alert('Copied to clipboard!')}
50
+ onLinkPress={(url) => {
51
+ console.log(\`Navigating to ${url}\`);
52
+ return true; // Return true to prevent default opening behavior
53
+ }}
54
+ />
55
+ </SafeAreaView>
56
+ );
57
+ }
10
58
  ```
11
59
 
60
+ ## 🎨 Styling Guide
61
+
62
+ ### 1\. Styling Code Blocks (`codeStyle`)
63
+
64
+ In v2, we introduced a single, powerful `codeStyle` object to give you granular control over code blocks without prop-clutter.
65
+ ```ts
66
+ <Markdown
67
+ content={content}
68
+ codeStyle={{
69
+ // 1. Outer Box: Border radius, margins, border colors
70
+ container: {
71
+ borderRadius: 16,
72
+ borderColor: '#444',
73
+ borderWidth: 1,
74
+ },
75
+ // 2. Header Bar: Height, background color, padding
76
+ header: {
77
+ backgroundColor: '#1e1e1e',
78
+ paddingVertical: 10,
79
+ },
80
+ // 3. Header Text: The "JAVASCRIPT" label
81
+ headerText: {
82
+ color: '#a0a0a0',
83
+ },
84
+ // 4. Inner Padding: Give the code some breathing room
85
+ content: {
86
+ padding: 20,
87
+ },
88
+ // 5. Code Text: Font size, line height
89
+ text: {
90
+ fontSize: 14,
91
+ }
92
+ }}
93
+ />
94
+ ```
12
95
 
13
- ## Usage
96
+ ### 2\. Styling Markdown Nodes (`styles`)
97
+
98
+ You can override the default text and layout styles by passing a styles object. The keys correspond to the Markdown nodes.
99
+ ```ts
100
+ <Markdown
101
+ content={content}
102
+ styles={{
103
+ body: { fontSize: 16, color: '#333' },
104
+ heading1: { fontSize: 32, fontWeight: 'bold' },
105
+ code\_inline: { backgroundColor: '#f0f0f0', color: '#e01e5a', borderRadius: 4 },
106
+ link: { color: '#007AFF', textDecorationLine: 'underline' },
107
+ }}
108
+ />
109
+ ```
14
110
 
111
+ **Available Style Keys:**
15
112
 
16
- ```js
17
- import { ExpoMarkdownNativeMarkdownView } from "@codearcade/expo-markdown-native-markdown";
113
+ `body`, `heading1` through `heading6`, `hr,` `strong`, `em`, `s` (strikethrough), `blockquote`, `bullet_list`, `ordered_list`, `list_item`, `code_inline`, `code_block`, `fence`, `table`, `thead`, `tbody`, `th`, `tr`, `td`, `link`, `image`.
18
114
 
19
- // ...
115
+ ## 🧩 Standalone `<CodeBlock />`
20
116
 
21
- <ExpoMarkdownNativeMarkdownView color="tomato" />
117
+ Don't need a full markdown parser? You can use our syntax highlighter completely standalone!
118
+ ```ts
119
+ import { CodeBlock } from '@codearcade/expo-markdown-native';
120
+
121
+ <CodeBlock
122
+ content="console.log('Just a standalone snippet!');"
123
+ language="javascript"
124
+ appTheme="dark"
125
+ onCopy={() => console.log('Copied!')}
126
+ />
22
127
  ```
23
128
 
129
+ ## 📖 API Reference
24
130
 
25
- ## Contributing
131
+ ### <Markdown /> Props
26
132
 
27
- - [Development workflow](CONTRIBUTING.md#development-workflow)
28
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
- - [Code of conduct](CODE_OF_CONDUCT.md)
133
+ | Prop | Type | Default | Description |
134
+ | --- | --- | --- | --- |
135
+ | content | string | Required | The raw markdown string to render. |
136
+ | theme | 'light' | 'dark' | 'light' | Base theme for background/text colors. |
137
+ | codeTheme | ReactStyle | github / monokai | Highlight.js theme object for syntax. |
138
+ | styles | Record<string, Style> | {} | Override styles for specific markdown nodes. |
139
+ | codeStyle | MarkdownCodeStyle | {} | Advanced styling for code block containers. |
140
+ | onLinkPress | (url) => boolean | undefined | Fired when a link is pressed. |
141
+ | onCopy | (text) => void | undefined | Fired when the code block copy button is pressed. |
30
142
 
31
- ## License
143
+ ## ⚠️ Upgrading to v2.0
32
144
 
33
- MIT
145
+ Version 2.0 is a complete rewrite to 100% TypeScript/JavaScript.
34
146
 
35
- ---
147
+ * **Native Modules Removed:** You no longer need to rebuild your native app or deal with auto-linking.
148
+ * **Prop Changes:** Individual props like codePadding, codeRadius, and codeHeaderHeight have been removed. Please use the new unified codeStyle prop instead.
36
149
 
37
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
150
+ ## 📄 License
151
+
152
+ MIT
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+
3
+ import { memo, useMemo, useState } from 'react';
4
+ import { Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
5
+ import CodeHighlighter from 'react-native-code-highlighter';
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ export const CodeBlock = /*#__PURE__*/memo(({
8
+ content,
9
+ language,
10
+ syntaxTheme,
11
+ appTheme,
12
+ codeStyle,
13
+ onCopy
14
+ }) => {
15
+ const [copied, setCopied] = useState(false);
16
+ const handleCopy = async () => {
17
+ if (!onCopy) return;
18
+ try {
19
+ const textToCopy = content.replace(/\n$/, '');
20
+ await onCopy(textToCopy);
21
+ setCopied(true);
22
+ setTimeout(() => setCopied(false), 2000);
23
+ } catch (e) {
24
+ console.warn('[Markdown] Copy failed', e);
25
+ }
26
+ };
27
+
28
+ // Dynamic Styles based on theme
29
+ const isDark = appTheme === 'dark';
30
+ const containerBg = isDark ? '#2d2d2d' : '#f5f5f5';
31
+ const borderColor = isDark ? 'rgba(255,255,255,0.1)' : '#e0e0e0';
32
+ const headerBg = isDark ? 'rgba(255,255,255,0.05)' : '#eaeaea';
33
+ const textColor = isDark ? '#fff' : '#333';
34
+ const textStyle = useMemo(() => [staticStyles.codeText, codeStyle.text], [codeStyle.text]);
35
+ const scrollProps = useMemo(() => ({
36
+ contentContainerStyle: [staticStyles.syntaxContainer, {
37
+ backgroundColor: 'transparent'
38
+ }, codeStyle.content]
39
+ }), [codeStyle.content]);
40
+ return /*#__PURE__*/_jsxs(View, {
41
+ style: [staticStyles.codeContainer, {
42
+ backgroundColor: containerBg,
43
+ borderColor
44
+ }, codeStyle.container],
45
+ children: [/*#__PURE__*/_jsxs(View, {
46
+ style: [staticStyles.codeHeader, {
47
+ backgroundColor: headerBg,
48
+ borderBottomColor: borderColor
49
+ }, codeStyle.header],
50
+ children: [/*#__PURE__*/_jsx(Text, {
51
+ style: [staticStyles.languageLabel, codeStyle.headerText],
52
+ children: language || 'Code'
53
+ }), onCopy && /*#__PURE__*/_jsx(TouchableOpacity, {
54
+ onPress: handleCopy,
55
+ style: staticStyles.copyButton,
56
+ children: /*#__PURE__*/_jsx(Text, {
57
+ style: [staticStyles.copyText, {
58
+ color: textColor
59
+ }],
60
+ children: copied ? 'Copied!' : 'Copy'
61
+ })
62
+ })]
63
+ }), /*#__PURE__*/_jsx(CodeHighlighter, {
64
+ language: language ?? 'javascript',
65
+ hljsStyle: syntaxTheme,
66
+ textStyle: textStyle,
67
+ scrollViewProps: scrollProps,
68
+ customStyle: {
69
+ backgroundColor: 'transparent'
70
+ },
71
+ children: content
72
+ })]
73
+ });
74
+ });
75
+ const staticStyles = StyleSheet.create({
76
+ codeContainer: {
77
+ marginVertical: 10,
78
+ borderRadius: 8,
79
+ overflow: 'hidden',
80
+ borderWidth: 1
81
+ },
82
+ codeHeader: {
83
+ flexDirection: 'row',
84
+ justifyContent: 'space-between',
85
+ alignItems: 'center',
86
+ paddingHorizontal: 12,
87
+ paddingVertical: 6,
88
+ borderBottomWidth: 1
89
+ },
90
+ languageLabel: {
91
+ color: '#888',
92
+ fontSize: 11,
93
+ fontWeight: '600',
94
+ textTransform: 'uppercase'
95
+ },
96
+ copyButton: {
97
+ padding: 4
98
+ },
99
+ copyText: {
100
+ fontSize: 11,
101
+ fontWeight: '600'
102
+ },
103
+ codeText: {
104
+ fontFamily: Platform.select({
105
+ ios: 'Menlo',
106
+ android: 'monospace',
107
+ default: 'monospace'
108
+ }),
109
+ lineHeight: 20
110
+ },
111
+ syntaxContainer: {
112
+ padding: 12,
113
+ margin: 0,
114
+ minWidth: '100%'
115
+ }
116
+ });
117
+ //# sourceMappingURL=codeblock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["memo","useMemo","useState","Platform","StyleSheet","Text","TouchableOpacity","View","CodeHighlighter","jsx","_jsx","jsxs","_jsxs","CodeBlock","content","language","syntaxTheme","appTheme","codeStyle","onCopy","copied","setCopied","handleCopy","textToCopy","replace","setTimeout","e","console","warn","isDark","containerBg","borderColor","headerBg","textColor","textStyle","staticStyles","codeText","text","scrollProps","contentContainerStyle","syntaxContainer","backgroundColor","style","codeContainer","container","children","codeHeader","borderBottomColor","header","languageLabel","headerText","onPress","copyButton","copyText","color","hljsStyle","scrollViewProps","customStyle","create","marginVertical","borderRadius","overflow","borderWidth","flexDirection","justifyContent","alignItems","paddingHorizontal","paddingVertical","borderBottomWidth","fontSize","fontWeight","textTransform","padding","fontFamily","select","ios","android","default","lineHeight","margin","minWidth"],"sourceRoot":"..\\..\\src","sources":["codeblock.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
 
3
3
  export * from "./markdown.js";
4
+ export * from "./codeblock.js";
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA,cAAc,eAAY","ignoreList":[]}
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA,cAAc,eAAY;AAC1B,cAAc,gBAAa","ignoreList":[]}
@@ -1,19 +1,18 @@
1
1
  "use strict";
2
2
 
3
- import React, { memo, useMemo, useState } from 'react';
4
- import { Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
5
- import CodeHighlighter from 'react-native-code-highlighter';
3
+ import React, { useMemo } from 'react';
4
+ import { Text, View } from 'react-native';
6
5
  import MarkdownDisplay from 'react-native-markdown-display';
7
6
  import { github, monokai } from 'react-syntax-highlighter/dist/esm/styles/hljs';
8
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { CodeBlock } from "./codeblock.js";
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
9
  export const Markdown = ({
10
10
  content,
11
11
  theme = 'light',
12
12
  codeTheme,
13
13
  styles = {},
14
- codeContainerStyle,
14
+ codeStyle = {},
15
15
  onLinkPress,
16
- codeFontSize = 13,
17
16
  onCopy
18
17
  }) => {
19
18
  // 1. Select default Syntax Theme
@@ -21,6 +20,21 @@ export const Markdown = ({
21
20
 
22
21
  // 2. Define Custom Rules
23
22
  const rules = useMemo(() => ({
23
+ code_inline: (node, _children, _parent, style) => {
24
+ return /*#__PURE__*/_jsx(Text, {
25
+ children: /*#__PURE__*/_jsx(View, {
26
+ style: style,
27
+ children: /*#__PURE__*/_jsx(Text, {
28
+ style: [style, {
29
+ backgroundColor: 'transparent',
30
+ padding: 0,
31
+ margin: 0
32
+ }],
33
+ children: node.content
34
+ })
35
+ })
36
+ }, node.key);
37
+ },
24
38
  fence: (node, _children, _parent, _style) => {
25
39
  const fenceNode = node;
26
40
  return /*#__PURE__*/_jsx(CodeBlock, {
@@ -28,8 +42,7 @@ export const Markdown = ({
28
42
  language: fenceNode.sourceInfo,
29
43
  syntaxTheme: activeCodeTheme,
30
44
  appTheme: theme,
31
- codeFontSize: codeFontSize,
32
- codeContainerStyle: codeContainerStyle,
45
+ codeStyle: codeStyle,
33
46
  onCopy: onCopy
34
47
  }, node.key);
35
48
  },
@@ -38,13 +51,12 @@ export const Markdown = ({
38
51
  language: null,
39
52
  syntaxTheme: activeCodeTheme,
40
53
  appTheme: theme,
41
- codeFontSize: codeFontSize,
42
- codeContainerStyle: codeContainerStyle,
54
+ codeStyle: codeStyle,
43
55
  onCopy: onCopy
44
56
  }, node.key)
45
- }), [activeCodeTheme, theme, codeFontSize, onCopy]);
46
- const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
57
+ }), [activeCodeTheme, theme, codeStyle, onCopy]);
47
58
  const mergedStyles = useMemo(() => {
59
+ const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
48
60
  const result = {
49
61
  ...baseStyles
50
62
  };
@@ -62,7 +74,7 @@ export const Markdown = ({
62
74
  }
63
75
  }
64
76
  return result;
65
- }, [baseStyles, styles]);
77
+ }, [theme, styles]);
66
78
  return /*#__PURE__*/_jsx(MarkdownDisplay, {
67
79
  style: mergedStyles,
68
80
  rules: rules,
@@ -70,107 +82,6 @@ export const Markdown = ({
70
82
  children: content
71
83
  });
72
84
  };
73
- // --- Sub-Component: Code Block ---
74
- const CodeBlock = /*#__PURE__*/memo(({
75
- content,
76
- language,
77
- syntaxTheme,
78
- appTheme,
79
- codeFontSize,
80
- codeContainerStyle,
81
- onCopy
82
- }) => {
83
- const [copied, setCopied] = useState(false);
84
- const handleCopy = async () => {
85
- if (!onCopy) return;
86
- try {
87
- const textToCopy = content.replace(/\n$/, '');
88
- await onCopy(textToCopy);
89
- setCopied(true);
90
- setTimeout(() => setCopied(false), 2000);
91
- } catch (e) {
92
- console.warn('[Markdown] Copy failed', e);
93
- }
94
- };
95
-
96
- // Dynamic Styles based on theme
97
- const isDark = appTheme === 'dark';
98
- const containerBg = isDark ? '#2d2d2d' : '#f5f5f5';
99
- const borderColor = isDark ? 'rgba(255,255,255,0.1)' : '#e0e0e0';
100
- const headerBg = isDark ? 'rgba(255,255,255,0.05)' : '#eaeaea';
101
- const textColor = isDark ? '#fff' : '#333';
102
- const codeStyle = useMemo(() => ({
103
- ...staticStyles.codeText,
104
- fontSize: codeFontSize
105
- }), [codeFontSize]);
106
- return /*#__PURE__*/_jsxs(View, {
107
- style: [staticStyles.codeContainer, {
108
- backgroundColor: containerBg,
109
- borderColor
110
- }, codeContainerStyle],
111
- children: [/*#__PURE__*/_jsxs(View, {
112
- style: [staticStyles.codeHeader, {
113
- backgroundColor: headerBg,
114
- borderBottomColor: borderColor
115
- }],
116
- children: [/*#__PURE__*/_jsx(Text, {
117
- style: staticStyles.languageLabel,
118
- children: language || 'Code'
119
- }), onCopy && /*#__PURE__*/_jsx(TouchableOpacity, {
120
- onPress: handleCopy,
121
- style: staticStyles.copyButton,
122
- children: /*#__PURE__*/_jsx(Text, {
123
- style: [staticStyles.copyText, {
124
- color: textColor
125
- }],
126
- children: copied ? 'Copied!' : 'Copy'
127
- })
128
- })]
129
- }), /*#__PURE__*/_jsx(CodeHighlighter, {
130
- language: language ?? undefined,
131
- hljsStyle: syntaxTheme,
132
- textStyle: codeStyle,
133
- children: content
134
- })]
135
- });
136
- });
137
- const staticStyles = StyleSheet.create({
138
- codeContainer: {
139
- marginVertical: 10,
140
- borderRadius: 8,
141
- overflow: 'hidden',
142
- borderWidth: 1
143
- },
144
- codeHeader: {
145
- flexDirection: 'row',
146
- justifyContent: 'space-between',
147
- alignItems: 'center',
148
- paddingHorizontal: 12,
149
- paddingVertical: 6,
150
- borderBottomWidth: 1
151
- },
152
- languageLabel: {
153
- color: '#888',
154
- fontSize: 11,
155
- fontWeight: '600',
156
- textTransform: 'uppercase'
157
- },
158
- copyButton: {
159
- padding: 4
160
- },
161
- copyText: {
162
- fontSize: 11,
163
- fontWeight: '600'
164
- },
165
- codeText: {
166
- fontFamily: Platform.select({
167
- ios: 'Menlo',
168
- android: 'monospace',
169
- default: 'monospace'
170
- }),
171
- lineHeight: 20
172
- }
173
- });
174
85
  const markdownLight = {
175
86
  body: {
176
87
  color: '#222',
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","useMemo","useState","Platform","StyleSheet","Text","TouchableOpacity","View","CodeHighlighter","MarkdownDisplay","github","monokai","jsx","_jsx","jsxs","_jsxs","Markdown","content","theme","codeTheme","styles","codeContainerStyle","onLinkPress","codeFontSize","onCopy","activeCodeTheme","rules","fence","node","_children","_parent","_style","fenceNode","CodeBlock","language","sourceInfo","syntaxTheme","appTheme","key","code_block","baseStyles","markdownDark","markdownLight","mergedStyles","result","userStyle","Array","isArray","style","children","copied","setCopied","handleCopy","textToCopy","replace","setTimeout","e","console","warn","isDark","containerBg","borderColor","headerBg","textColor","codeStyle","staticStyles","codeText","fontSize","codeContainer","backgroundColor","codeHeader","borderBottomColor","languageLabel","onPress","copyButton","copyText","color","undefined","hljsStyle","textStyle","create","marginVertical","borderRadius","overflow","borderWidth","flexDirection","justifyContent","alignItems","paddingHorizontal","paddingVertical","borderBottomWidth","fontWeight","textTransform","padding","fontFamily","select","ios","android","default","lineHeight","body","heading1","code_inline","blockquote","borderLeftColor"],"sourceRoot":"..\\..\\src","sources":["markdown.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","useMemo","Text","View","MarkdownDisplay","github","monokai","CodeBlock","jsx","_jsx","Markdown","content","theme","codeTheme","styles","codeStyle","onLinkPress","onCopy","activeCodeTheme","rules","code_inline","node","_children","_parent","style","children","backgroundColor","padding","margin","key","fence","_style","fenceNode","language","sourceInfo","syntaxTheme","appTheme","code_block","mergedStyles","baseStyles","markdownDark","markdownLight","result","userStyle","Array","isArray","body","color","fontSize","lineHeight","heading1","fontWeight","marginVertical","borderRadius","blockquote","borderLeftColor"],"sourceRoot":"..\\..\\src","sources":["markdown.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=codeblock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["types/codeblock.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ export * from "./markdown.js";
4
+ export * from "./codeblock.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["types/index.ts"],"mappings":";;AAAA,cAAc,eAAY;AAC1B,cAAc,gBAAa","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["types/markdown.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ import type { CodeBlockProps } from './types';
2
+ export declare const CodeBlock: import("react").MemoExoticComponent<({ content, language, syntaxTheme, appTheme, codeStyle, onCopy, }: CodeBlockProps) => import("react/jsx-runtime").JSX.Element>;
3
+ //# sourceMappingURL=codeblock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeblock.d.ts","sourceRoot":"","sources":["../../../src/codeblock.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,SAAS,yGAQjB,cAAc,6CAkFlB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './markdown';
2
+ export * from './codeblock';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
@@ -1,16 +1,4 @@
1
1
  import React from 'react';
2
- import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
3
- import type { ReactStyle } from 'react-native-code-highlighter';
4
- interface Props {
5
- content: string;
6
- theme?: 'light' | 'dark';
7
- codeTheme?: ReactStyle;
8
- styles?: Record<string, StyleProp<ViewStyle | TextStyle>>;
9
- codeContainerStyle?: StyleProp<ViewStyle>;
10
- codeFontSize?: number;
11
- onLinkPress?: (url: string) => boolean;
12
- onCopy?: (text: string) => Promise<void> | void;
13
- }
14
- export declare const Markdown: React.FC<Props>;
15
- export {};
2
+ import type { MarkdownProps } from './types';
3
+ export declare const Markdown: React.FC<MarkdownProps>;
16
4
  //# sourceMappingURL=markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAMhE,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC;IAC1D,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAsFpC,CAAC"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAMvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAwG5C,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { ReactStyle } from 'react-native-code-highlighter';
2
+ import type { MarkdownCodeStyle } from './markdown';
3
+ /**
4
+ * Props for the internal `CodeBlock` component.
5
+ * This component handles the rendering of fenced code blocks with syntax highlighting,
6
+ * a header, and an optional copy button.
7
+ */
8
+ interface CodeBlockProps {
9
+ /**
10
+ * The raw code string to be highlighted and displayed.
11
+ * This is the content inside the markdown fence.
12
+ */
13
+ content: string;
14
+ /**
15
+ * The programming language identifier specified in the markdown fence (e.g., 'js', 'python').
16
+ * If null or undefined, the highlighter will attempt to auto-detect or default to text.
17
+ */
18
+ language?: string | null;
19
+ /**
20
+ * The Highlight.js theme object used for syntax coloring.
21
+ * Examples: `monokai`, `github`, `dracula`.
22
+ */
23
+ syntaxTheme: ReactStyle;
24
+ /**
25
+ * Custom style object to override the look of the code block.
26
+ * Allows styling the outer container, header, inner content padding, and font text.
27
+ */
28
+ codeStyle: MarkdownCodeStyle;
29
+ /**
30
+ * The current theme of the application.
31
+ * Used to determine default background colors and border colors if no overrides are provided.
32
+ */
33
+ appTheme: 'light' | 'dark';
34
+ /**
35
+ * Optional callback triggered when the "Copy" button is pressed.
36
+ * @param text The raw code content that was copied.
37
+ */
38
+ onCopy?: (text: string) => Promise<void> | void;
39
+ }
40
+ export type { CodeBlockProps };
41
+ //# sourceMappingURL=codeblock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeblock.d.ts","sourceRoot":"","sources":["../../../../src/types/codeblock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;GAIG;AACH,UAAU,cAAc;IACtB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB;;;OAGG;IACH,WAAW,EAAE,UAAU,CAAC;IAExB;;;OAGG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAE7B;;;OAGG;IACH,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAE3B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './markdown';
2
+ export * from './codeblock';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,77 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
2
+ import type { ReactStyle } from 'react-native-code-highlighter';
3
+ /**
4
+ * Configuration object for customizing the appearance of code blocks.
5
+ * Use this to style the container, header, scrollable content area, and the code text itself.
6
+ */
7
+ interface MarkdownCodeStyle {
8
+ /**
9
+ * Styles the outer container of the code block.
10
+ * Useful for setting `borderRadius`, `borderColor`, `borderWidth`, and `margin`.
11
+ */
12
+ container?: StyleProp<ViewStyle>;
13
+ /**
14
+ * Styles the header bar (where the language label and copy button reside).
15
+ * Useful for setting `backgroundColor`, `height`, `padding`, or `borderBottomWidth`.
16
+ */
17
+ header?: StyleProp<ViewStyle>;
18
+ /**
19
+ * Styles the language label text (e.g., "JAVASCRIPT").
20
+ * Useful for setting `fontSize`, `color`, or `fontWeight`.
21
+ */
22
+ headerText?: StyleProp<TextStyle>;
23
+ /**
24
+ * Styles the internal scrollable content container.
25
+ * **Crucial for padding:** Use this to set the `padding` around the code text inside the scroll view.
26
+ */
27
+ content?: StyleProp<ViewStyle>;
28
+ /**
29
+ * Styles the code text itself.
30
+ * Useful for setting `fontSize`, `fontFamily`, and `lineHeight`.
31
+ * Note: Colors are typically handled by the `syntaxTheme`, but you can override base text styles here.
32
+ */
33
+ text?: StyleProp<TextStyle>;
34
+ }
35
+ /**
36
+ * Props for the main `Markdown` component.
37
+ */
38
+ interface MarkdownProps {
39
+ /**
40
+ * The raw markdown string to be parsed and rendered.
41
+ */
42
+ content: string;
43
+ /**
44
+ * The base theme for the markdown content.
45
+ * Determines the default background colors and text colors for non-code elements.
46
+ * @default 'light'
47
+ */
48
+ theme?: 'light' | 'dark';
49
+ /**
50
+ * A specific Highlight.js theme object for syntax highlighting in code blocks.
51
+ * If not provided, defaults to `github` (light) or `monokai` (dark) based on the `theme` prop.
52
+ */
53
+ codeTheme?: ReactStyle;
54
+ /**
55
+ * A generic style object to override standard markdown elements.
56
+ * Keys correspond to markdown nodes (e.g., `body`, `heading1`, `link`, `blockquote`).
57
+ */
58
+ styles?: Record<string, StyleProp<ViewStyle | TextStyle>>;
59
+ /**
60
+ * Advanced styling configuration for code blocks.
61
+ * Allows granular control over the code container, header, and inner content.
62
+ */
63
+ codeStyle?: MarkdownCodeStyle;
64
+ /**
65
+ * Callback triggered when a link is pressed.
66
+ * @param url The URL of the link.
67
+ * @returns `true` to prevent the default behavior (opening the link), `false` or `undefined` to let it open.
68
+ */
69
+ onLinkPress?: (url: string) => boolean;
70
+ /**
71
+ * Callback triggered when the "Copy" button in a code block is pressed.
72
+ * @param text The raw code content that was copied.
73
+ */
74
+ onCopy?: (text: string) => Promise<void> | void;
75
+ }
76
+ export type { MarkdownCodeStyle, MarkdownProps };
77
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/types/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE;;;GAGG;AACH,UAAU,iBAAiB;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE9B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAElC;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE/B;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,UAAU,aAAa;IACrB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,EAAE,UAAU,CAAC;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC;IAE1D;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAE9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAEvC;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codearcade/expo-markdown-native",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Native Markdown rendering for Expo & React Native with built-in syntax highlighting.",
5
5
  "main": "./lib/module/index.js",
6
6
  "private": false,
@@ -0,0 +1,145 @@
1
+ import { memo, useMemo, useState } from 'react';
2
+ import {
3
+ Platform,
4
+ StyleSheet,
5
+ Text,
6
+ TouchableOpacity,
7
+ View,
8
+ } from 'react-native';
9
+ import CodeHighlighter from 'react-native-code-highlighter';
10
+ import type { CodeBlockProps } from './types';
11
+
12
+ export const CodeBlock = memo(
13
+ ({
14
+ content,
15
+ language,
16
+ syntaxTheme,
17
+ appTheme,
18
+ codeStyle,
19
+ onCopy,
20
+ }: CodeBlockProps) => {
21
+ const [copied, setCopied] = useState(false);
22
+
23
+ const handleCopy = async () => {
24
+ if (!onCopy) return;
25
+
26
+ try {
27
+ const textToCopy = content.replace(/\n$/, '');
28
+ await onCopy(textToCopy);
29
+ setCopied(true);
30
+ setTimeout(() => setCopied(false), 2000);
31
+ } catch (e) {
32
+ console.warn('[Markdown] Copy failed', e);
33
+ }
34
+ };
35
+
36
+ // Dynamic Styles based on theme
37
+ const isDark = appTheme === 'dark';
38
+ const containerBg = isDark ? '#2d2d2d' : '#f5f5f5';
39
+ const borderColor = isDark ? 'rgba(255,255,255,0.1)' : '#e0e0e0';
40
+ const headerBg = isDark ? 'rgba(255,255,255,0.05)' : '#eaeaea';
41
+ const textColor = isDark ? '#fff' : '#333';
42
+
43
+ const textStyle = useMemo(
44
+ () => [staticStyles.codeText, codeStyle.text],
45
+ [codeStyle.text]
46
+ );
47
+
48
+ const scrollProps = useMemo(
49
+ () => ({
50
+ contentContainerStyle: [
51
+ staticStyles.syntaxContainer,
52
+ { backgroundColor: 'transparent' },
53
+ codeStyle.content,
54
+ ],
55
+ }),
56
+ [codeStyle.content]
57
+ );
58
+
59
+ return (
60
+ <View
61
+ style={[
62
+ staticStyles.codeContainer,
63
+ { backgroundColor: containerBg, borderColor },
64
+ codeStyle.container,
65
+ ]}
66
+ >
67
+ <View
68
+ style={[
69
+ staticStyles.codeHeader,
70
+ { backgroundColor: headerBg, borderBottomColor: borderColor },
71
+ codeStyle.header,
72
+ ]}
73
+ >
74
+ <Text style={[staticStyles.languageLabel, codeStyle.headerText]}>
75
+ {language || 'Code'}
76
+ </Text>
77
+
78
+ {onCopy && (
79
+ <TouchableOpacity
80
+ onPress={handleCopy}
81
+ style={staticStyles.copyButton}
82
+ >
83
+ <Text style={[staticStyles.copyText, { color: textColor }]}>
84
+ {copied ? 'Copied!' : 'Copy'}
85
+ </Text>
86
+ </TouchableOpacity>
87
+ )}
88
+ </View>
89
+
90
+ <CodeHighlighter
91
+ language={language ?? 'javascript'}
92
+ hljsStyle={syntaxTheme}
93
+ textStyle={textStyle}
94
+ scrollViewProps={scrollProps}
95
+ customStyle={{ backgroundColor: 'transparent' }}
96
+ >
97
+ {content}
98
+ </CodeHighlighter>
99
+ </View>
100
+ );
101
+ }
102
+ );
103
+
104
+ const staticStyles = StyleSheet.create({
105
+ codeContainer: {
106
+ marginVertical: 10,
107
+ borderRadius: 8,
108
+ overflow: 'hidden',
109
+ borderWidth: 1,
110
+ },
111
+ codeHeader: {
112
+ flexDirection: 'row',
113
+ justifyContent: 'space-between',
114
+ alignItems: 'center',
115
+ paddingHorizontal: 12,
116
+ paddingVertical: 6,
117
+ borderBottomWidth: 1,
118
+ },
119
+ languageLabel: {
120
+ color: '#888',
121
+ fontSize: 11,
122
+ fontWeight: '600',
123
+ textTransform: 'uppercase',
124
+ },
125
+ copyButton: {
126
+ padding: 4,
127
+ },
128
+ copyText: {
129
+ fontSize: 11,
130
+ fontWeight: '600',
131
+ },
132
+ codeText: {
133
+ fontFamily: Platform.select({
134
+ ios: 'Menlo',
135
+ android: 'monospace',
136
+ default: 'monospace',
137
+ }),
138
+ lineHeight: 20,
139
+ },
140
+ syntaxContainer: {
141
+ padding: 12,
142
+ margin: 0,
143
+ minWidth: '100%',
144
+ },
145
+ });
package/src/index.tsx CHANGED
@@ -1 +1,2 @@
1
1
  export * from './markdown';
2
+ export * from './codeblock';
package/src/markdown.tsx CHANGED
@@ -1,37 +1,18 @@
1
- import React, { memo, useMemo, useState } from 'react';
2
- import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
3
- import {
4
- Platform,
5
- StyleSheet,
6
- Text,
7
- TouchableOpacity,
8
- View,
9
- } from 'react-native';
10
- import type { ReactStyle } from 'react-native-code-highlighter';
11
- import CodeHighlighter from 'react-native-code-highlighter';
1
+ import React, { useMemo } from 'react';
2
+ import { Text, View } from 'react-native';
12
3
  import type { ASTNode } from 'react-native-markdown-display';
13
4
  import MarkdownDisplay from 'react-native-markdown-display';
14
5
  import { github, monokai } from 'react-syntax-highlighter/dist/esm/styles/hljs';
6
+ import { CodeBlock } from './codeblock';
7
+ import type { MarkdownProps } from './types';
15
8
 
16
- interface Props {
17
- content: string;
18
- theme?: 'light' | 'dark';
19
- codeTheme?: ReactStyle;
20
- styles?: Record<string, StyleProp<ViewStyle | TextStyle>>;
21
- codeContainerStyle?: StyleProp<ViewStyle>;
22
- codeFontSize?: number;
23
- onLinkPress?: (url: string) => boolean;
24
- onCopy?: (text: string) => Promise<void> | void;
25
- }
26
-
27
- export const Markdown: React.FC<Props> = ({
9
+ export const Markdown: React.FC<MarkdownProps> = ({
28
10
  content,
29
11
  theme = 'light',
30
12
  codeTheme,
31
13
  styles = {},
32
- codeContainerStyle,
14
+ codeStyle = {},
33
15
  onLinkPress,
34
- codeFontSize = 13,
35
16
  onCopy,
36
17
  }) => {
37
18
  // 1. Select default Syntax Theme
@@ -40,6 +21,28 @@ export const Markdown: React.FC<Props> = ({
40
21
  // 2. Define Custom Rules
41
22
  const rules = useMemo(
42
23
  () => ({
24
+ code_inline: (
25
+ node: ASTNode,
26
+ _children: any,
27
+ _parent: any,
28
+ style: any
29
+ ) => {
30
+ return (
31
+ <Text key={node.key}>
32
+ <View style={style}>
33
+ <Text
34
+ style={[
35
+ style,
36
+ { backgroundColor: 'transparent', padding: 0, margin: 0 },
37
+ ]}
38
+ >
39
+ {node.content}
40
+ </Text>
41
+ </View>
42
+ </Text>
43
+ );
44
+ },
45
+
43
46
  fence: (node: ASTNode, _children: any, _parent: any, _style: any) => {
44
47
  const fenceNode = node as ASTNode & { sourceInfo: string };
45
48
 
@@ -50,8 +53,7 @@ export const Markdown: React.FC<Props> = ({
50
53
  language={fenceNode.sourceInfo}
51
54
  syntaxTheme={activeCodeTheme}
52
55
  appTheme={theme}
53
- codeFontSize={codeFontSize}
54
- codeContainerStyle={codeContainerStyle}
56
+ codeStyle={codeStyle}
55
57
  onCopy={onCopy}
56
58
  />
57
59
  );
@@ -68,18 +70,16 @@ export const Markdown: React.FC<Props> = ({
68
70
  language={null}
69
71
  syntaxTheme={activeCodeTheme}
70
72
  appTheme={theme}
71
- codeFontSize={codeFontSize}
72
- codeContainerStyle={codeContainerStyle}
73
+ codeStyle={codeStyle}
73
74
  onCopy={onCopy}
74
75
  />
75
76
  ),
76
77
  }),
77
- [activeCodeTheme, theme, codeFontSize, onCopy]
78
+ [activeCodeTheme, theme, codeStyle, onCopy]
78
79
  );
79
80
 
80
- const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
81
-
82
81
  const mergedStyles = useMemo(() => {
82
+ const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
83
83
  const result: Record<string, any> = { ...baseStyles };
84
84
 
85
85
  for (const key in styles) {
@@ -99,7 +99,7 @@ export const Markdown: React.FC<Props> = ({
99
99
  }
100
100
 
101
101
  return result;
102
- }, [baseStyles, styles]);
102
+ }, [theme, styles]);
103
103
 
104
104
  return (
105
105
  <MarkdownDisplay
@@ -112,135 +112,6 @@ export const Markdown: React.FC<Props> = ({
112
112
  );
113
113
  };
114
114
 
115
- interface CodeBlockProps {
116
- content: string;
117
- language?: string | null;
118
- syntaxTheme: ReactStyle;
119
- codeContainerStyle?: StyleProp<ViewStyle>;
120
- appTheme: 'light' | 'dark';
121
- codeFontSize: number;
122
- onCopy?: (text: string) => Promise<void> | void;
123
- }
124
-
125
- // --- Sub-Component: Code Block ---
126
- const CodeBlock = memo(
127
- ({
128
- content,
129
- language,
130
- syntaxTheme,
131
- appTheme,
132
- codeFontSize,
133
- codeContainerStyle,
134
- onCopy,
135
- }: CodeBlockProps) => {
136
- const [copied, setCopied] = useState(false);
137
-
138
- const handleCopy = async () => {
139
- if (!onCopy) return;
140
-
141
- try {
142
- const textToCopy = content.replace(/\n$/, '');
143
- await onCopy(textToCopy);
144
- setCopied(true);
145
- setTimeout(() => setCopied(false), 2000);
146
- } catch (e) {
147
- console.warn('[Markdown] Copy failed', e);
148
- }
149
- };
150
-
151
- // Dynamic Styles based on theme
152
- const isDark = appTheme === 'dark';
153
- const containerBg = isDark ? '#2d2d2d' : '#f5f5f5';
154
- const borderColor = isDark ? 'rgba(255,255,255,0.1)' : '#e0e0e0';
155
- const headerBg = isDark ? 'rgba(255,255,255,0.05)' : '#eaeaea';
156
- const textColor = isDark ? '#fff' : '#333';
157
-
158
- const codeStyle = useMemo(
159
- () => ({
160
- ...staticStyles.codeText,
161
- fontSize: codeFontSize,
162
- }),
163
- [codeFontSize]
164
- );
165
-
166
- return (
167
- <View
168
- style={[
169
- staticStyles.codeContainer,
170
- { backgroundColor: containerBg, borderColor },
171
- codeContainerStyle,
172
- ]}
173
- >
174
- <View
175
- style={[
176
- staticStyles.codeHeader,
177
- { backgroundColor: headerBg, borderBottomColor: borderColor },
178
- ]}
179
- >
180
- <Text style={staticStyles.languageLabel}>{language || 'Code'}</Text>
181
-
182
- {onCopy && (
183
- <TouchableOpacity
184
- onPress={handleCopy}
185
- style={staticStyles.copyButton}
186
- >
187
- <Text style={[staticStyles.copyText, { color: textColor }]}>
188
- {copied ? 'Copied!' : 'Copy'}
189
- </Text>
190
- </TouchableOpacity>
191
- )}
192
- </View>
193
-
194
- <CodeHighlighter
195
- language={language ?? undefined}
196
- hljsStyle={syntaxTheme}
197
- textStyle={codeStyle}
198
- >
199
- {content}
200
- </CodeHighlighter>
201
- </View>
202
- );
203
- }
204
- );
205
-
206
- const staticStyles = StyleSheet.create({
207
- codeContainer: {
208
- marginVertical: 10,
209
- borderRadius: 8,
210
- overflow: 'hidden',
211
- borderWidth: 1,
212
- },
213
- codeHeader: {
214
- flexDirection: 'row',
215
- justifyContent: 'space-between',
216
- alignItems: 'center',
217
- paddingHorizontal: 12,
218
- paddingVertical: 6,
219
- borderBottomWidth: 1,
220
- },
221
- languageLabel: {
222
- color: '#888',
223
- fontSize: 11,
224
- fontWeight: '600',
225
- textTransform: 'uppercase',
226
- },
227
- copyButton: {
228
- padding: 4,
229
- },
230
- copyText: {
231
- fontSize: 11,
232
- fontWeight: '600',
233
- },
234
- codeText: {
235
- fontFamily: Platform.select({
236
- ios: 'Menlo',
237
- android: 'monospace',
238
- default: 'monospace',
239
- }),
240
- lineHeight: 20,
241
- },
242
- });
243
-
244
115
  const markdownLight = {
245
116
  body: { color: '#222', fontSize: 16, lineHeight: 24 },
246
117
  heading1: {
@@ -0,0 +1,47 @@
1
+ import type { ReactStyle } from 'react-native-code-highlighter';
2
+ import type { MarkdownCodeStyle } from './markdown';
3
+
4
+ /**
5
+ * Props for the internal `CodeBlock` component.
6
+ * This component handles the rendering of fenced code blocks with syntax highlighting,
7
+ * a header, and an optional copy button.
8
+ */
9
+ interface CodeBlockProps {
10
+ /**
11
+ * The raw code string to be highlighted and displayed.
12
+ * This is the content inside the markdown fence.
13
+ */
14
+ content: string;
15
+
16
+ /**
17
+ * The programming language identifier specified in the markdown fence (e.g., 'js', 'python').
18
+ * If null or undefined, the highlighter will attempt to auto-detect or default to text.
19
+ */
20
+ language?: string | null;
21
+
22
+ /**
23
+ * The Highlight.js theme object used for syntax coloring.
24
+ * Examples: `monokai`, `github`, `dracula`.
25
+ */
26
+ syntaxTheme: ReactStyle;
27
+
28
+ /**
29
+ * Custom style object to override the look of the code block.
30
+ * Allows styling the outer container, header, inner content padding, and font text.
31
+ */
32
+ codeStyle: MarkdownCodeStyle;
33
+
34
+ /**
35
+ * The current theme of the application.
36
+ * Used to determine default background colors and border colors if no overrides are provided.
37
+ */
38
+ appTheme: 'light' | 'dark';
39
+
40
+ /**
41
+ * Optional callback triggered when the "Copy" button is pressed.
42
+ * @param text The raw code content that was copied.
43
+ */
44
+ onCopy?: (text: string) => Promise<void> | void;
45
+ }
46
+
47
+ export type { CodeBlockProps };
@@ -0,0 +1,2 @@
1
+ export * from './markdown';
2
+ export * from './codeblock';
@@ -0,0 +1,89 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
2
+ import type { ReactStyle } from 'react-native-code-highlighter';
3
+
4
+ /**
5
+ * Configuration object for customizing the appearance of code blocks.
6
+ * Use this to style the container, header, scrollable content area, and the code text itself.
7
+ */
8
+ interface MarkdownCodeStyle {
9
+ /**
10
+ * Styles the outer container of the code block.
11
+ * Useful for setting `borderRadius`, `borderColor`, `borderWidth`, and `margin`.
12
+ */
13
+ container?: StyleProp<ViewStyle>;
14
+
15
+ /**
16
+ * Styles the header bar (where the language label and copy button reside).
17
+ * Useful for setting `backgroundColor`, `height`, `padding`, or `borderBottomWidth`.
18
+ */
19
+ header?: StyleProp<ViewStyle>;
20
+
21
+ /**
22
+ * Styles the language label text (e.g., "JAVASCRIPT").
23
+ * Useful for setting `fontSize`, `color`, or `fontWeight`.
24
+ */
25
+ headerText?: StyleProp<TextStyle>;
26
+
27
+ /**
28
+ * Styles the internal scrollable content container.
29
+ * **Crucial for padding:** Use this to set the `padding` around the code text inside the scroll view.
30
+ */
31
+ content?: StyleProp<ViewStyle>;
32
+
33
+ /**
34
+ * Styles the code text itself.
35
+ * Useful for setting `fontSize`, `fontFamily`, and `lineHeight`.
36
+ * Note: Colors are typically handled by the `syntaxTheme`, but you can override base text styles here.
37
+ */
38
+ text?: StyleProp<TextStyle>;
39
+ }
40
+
41
+ /**
42
+ * Props for the main `Markdown` component.
43
+ */
44
+ interface MarkdownProps {
45
+ /**
46
+ * The raw markdown string to be parsed and rendered.
47
+ */
48
+ content: string;
49
+
50
+ /**
51
+ * The base theme for the markdown content.
52
+ * Determines the default background colors and text colors for non-code elements.
53
+ * @default 'light'
54
+ */
55
+ theme?: 'light' | 'dark';
56
+
57
+ /**
58
+ * A specific Highlight.js theme object for syntax highlighting in code blocks.
59
+ * If not provided, defaults to `github` (light) or `monokai` (dark) based on the `theme` prop.
60
+ */
61
+ codeTheme?: ReactStyle;
62
+
63
+ /**
64
+ * A generic style object to override standard markdown elements.
65
+ * Keys correspond to markdown nodes (e.g., `body`, `heading1`, `link`, `blockquote`).
66
+ */
67
+ styles?: Record<string, StyleProp<ViewStyle | TextStyle>>;
68
+
69
+ /**
70
+ * Advanced styling configuration for code blocks.
71
+ * Allows granular control over the code container, header, and inner content.
72
+ */
73
+ codeStyle?: MarkdownCodeStyle;
74
+
75
+ /**
76
+ * Callback triggered when a link is pressed.
77
+ * @param url The URL of the link.
78
+ * @returns `true` to prevent the default behavior (opening the link), `false` or `undefined` to let it open.
79
+ */
80
+ onLinkPress?: (url: string) => boolean;
81
+
82
+ /**
83
+ * Callback triggered when the "Copy" button in a code block is pressed.
84
+ * @param text The raw code content that was copied.
85
+ */
86
+ onCopy?: (text: string) => Promise<void> | void;
87
+ }
88
+
89
+ export type { MarkdownCodeStyle, MarkdownProps };