@codearcade/expo-markdown-native 1.0.0 → 2.0.1
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 +133 -18
- package/lib/module/codeblock.js +117 -0
- package/lib/module/codeblock.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown.js +22 -114
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/types/codeblock.js +4 -0
- package/lib/module/types/codeblock.js.map +1 -0
- package/lib/module/types/index.js +5 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/types/markdown.js +4 -0
- package/lib/module/types/markdown.js.map +1 -0
- package/lib/typescript/src/codeblock.d.ts +3 -0
- package/lib/typescript/src/codeblock.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/markdown.d.ts +2 -14
- package/lib/typescript/src/markdown.d.ts.map +1 -1
- package/lib/typescript/src/types/codeblock.d.ts +41 -0
- package/lib/typescript/src/types/codeblock.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +3 -0
- package/lib/typescript/src/types/index.d.ts.map +1 -0
- package/lib/typescript/src/types/markdown.d.ts +77 -0
- package/lib/typescript/src/types/markdown.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/codeblock.tsx +145 -0
- package/src/index.tsx +1 -0
- package/src/markdown.tsx +36 -162
- package/src/types/codeblock.ts +47 -0
- package/src/types/index.ts +2 -0
- package/src/types/markdown.ts +89 -0
package/README.md
CHANGED
|
@@ -1,37 +1,152 @@
|
|
|
1
|
-
# @codearcade/expo-markdown-native-markdown
|
|
2
1
|
|
|
3
|
-
|
|
2
|
+
# @codearcade/expo-markdown-native 🚀
|
|
4
3
|
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
+
### <Markdown /> Props
|
|
26
132
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
##
|
|
143
|
+
## ⚠️ Upgrading to v2.0
|
|
32
144
|
|
|
33
|
-
|
|
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
|
-
|
|
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":[]}
|
package/lib/module/index.js
CHANGED
package/lib/module/index.js.map
CHANGED
|
@@ -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":[]}
|
package/lib/module/markdown.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
4
|
-
import { Platform,
|
|
5
|
-
import CodeHighlighter from 'react-native-code-highlighter';
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import { Platform, Text } 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 {
|
|
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
|
-
|
|
14
|
+
codeStyle = {},
|
|
15
15
|
onLinkPress,
|
|
16
|
-
codeFontSize = 13,
|
|
17
16
|
onCopy
|
|
18
17
|
}) => {
|
|
19
18
|
// 1. Select default Syntax Theme
|
|
@@ -21,6 +20,18 @@ 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
|
+
style: [style, {
|
|
26
|
+
overflow: 'hidden',
|
|
27
|
+
fontFamily: Platform.select({
|
|
28
|
+
ios: 'Menlo',
|
|
29
|
+
default: 'monospace'
|
|
30
|
+
})
|
|
31
|
+
}],
|
|
32
|
+
children: node.content
|
|
33
|
+
}, node.key);
|
|
34
|
+
},
|
|
24
35
|
fence: (node, _children, _parent, _style) => {
|
|
25
36
|
const fenceNode = node;
|
|
26
37
|
return /*#__PURE__*/_jsx(CodeBlock, {
|
|
@@ -28,8 +39,7 @@ export const Markdown = ({
|
|
|
28
39
|
language: fenceNode.sourceInfo,
|
|
29
40
|
syntaxTheme: activeCodeTheme,
|
|
30
41
|
appTheme: theme,
|
|
31
|
-
|
|
32
|
-
codeContainerStyle: codeContainerStyle,
|
|
42
|
+
codeStyle: codeStyle,
|
|
33
43
|
onCopy: onCopy
|
|
34
44
|
}, node.key);
|
|
35
45
|
},
|
|
@@ -38,13 +48,12 @@ export const Markdown = ({
|
|
|
38
48
|
language: null,
|
|
39
49
|
syntaxTheme: activeCodeTheme,
|
|
40
50
|
appTheme: theme,
|
|
41
|
-
|
|
42
|
-
codeContainerStyle: codeContainerStyle,
|
|
51
|
+
codeStyle: codeStyle,
|
|
43
52
|
onCopy: onCopy
|
|
44
53
|
}, node.key)
|
|
45
|
-
}), [activeCodeTheme, theme,
|
|
46
|
-
const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
|
|
54
|
+
}), [activeCodeTheme, theme, codeStyle, onCopy]);
|
|
47
55
|
const mergedStyles = useMemo(() => {
|
|
56
|
+
const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
|
|
48
57
|
const result = {
|
|
49
58
|
...baseStyles
|
|
50
59
|
};
|
|
@@ -62,7 +71,7 @@ export const Markdown = ({
|
|
|
62
71
|
}
|
|
63
72
|
}
|
|
64
73
|
return result;
|
|
65
|
-
}, [
|
|
74
|
+
}, [theme, styles]);
|
|
66
75
|
return /*#__PURE__*/_jsx(MarkdownDisplay, {
|
|
67
76
|
style: mergedStyles,
|
|
68
77
|
rules: rules,
|
|
@@ -70,107 +79,6 @@ export const Markdown = ({
|
|
|
70
79
|
children: content
|
|
71
80
|
});
|
|
72
81
|
};
|
|
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
82
|
const markdownLight = {
|
|
175
83
|
body: {
|
|
176
84
|
color: '#222',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","
|
|
1
|
+
{"version":3,"names":["React","useMemo","Platform","Text","MarkdownDisplay","github","monokai","CodeBlock","jsx","_jsx","Markdown","content","theme","codeTheme","styles","codeStyle","onLinkPress","onCopy","activeCodeTheme","rules","code_inline","node","_children","_parent","style","overflow","fontFamily","select","ios","default","children","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","backgroundColor","borderRadius","blockquote","borderLeftColor"],"sourceRoot":"..\\..\\src","sources":["markdown.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["types/codeblock.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["types/index.ts"],"mappings":";;AAAA,cAAc,eAAY;AAC1B,cAAc,gBAAa","ignoreList":[]}
|
|
@@ -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 +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 {
|
|
3
|
-
|
|
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,
|
|
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,CA2G5C,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 @@
|
|
|
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
|
@@ -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
package/src/markdown.tsx
CHANGED
|
@@ -1,37 +1,18 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import
|
|
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 { Platform, Text } 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
|
-
|
|
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
|
-
|
|
14
|
+
codeStyle = {},
|
|
33
15
|
onLinkPress,
|
|
34
|
-
codeFontSize = 13,
|
|
35
16
|
onCopy,
|
|
36
17
|
}) => {
|
|
37
18
|
// 1. Select default Syntax Theme
|
|
@@ -40,6 +21,31 @@ 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
|
|
32
|
+
key={node.key}
|
|
33
|
+
style={[
|
|
34
|
+
style,
|
|
35
|
+
{
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
fontFamily: Platform.select({
|
|
38
|
+
ios: 'Menlo',
|
|
39
|
+
default: 'monospace',
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
44
|
+
{node.content}
|
|
45
|
+
</Text>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
|
|
43
49
|
fence: (node: ASTNode, _children: any, _parent: any, _style: any) => {
|
|
44
50
|
const fenceNode = node as ASTNode & { sourceInfo: string };
|
|
45
51
|
|
|
@@ -50,8 +56,7 @@ export const Markdown: React.FC<Props> = ({
|
|
|
50
56
|
language={fenceNode.sourceInfo}
|
|
51
57
|
syntaxTheme={activeCodeTheme}
|
|
52
58
|
appTheme={theme}
|
|
53
|
-
|
|
54
|
-
codeContainerStyle={codeContainerStyle}
|
|
59
|
+
codeStyle={codeStyle}
|
|
55
60
|
onCopy={onCopy}
|
|
56
61
|
/>
|
|
57
62
|
);
|
|
@@ -68,18 +73,16 @@ export const Markdown: React.FC<Props> = ({
|
|
|
68
73
|
language={null}
|
|
69
74
|
syntaxTheme={activeCodeTheme}
|
|
70
75
|
appTheme={theme}
|
|
71
|
-
|
|
72
|
-
codeContainerStyle={codeContainerStyle}
|
|
76
|
+
codeStyle={codeStyle}
|
|
73
77
|
onCopy={onCopy}
|
|
74
78
|
/>
|
|
75
79
|
),
|
|
76
80
|
}),
|
|
77
|
-
[activeCodeTheme, theme,
|
|
81
|
+
[activeCodeTheme, theme, codeStyle, onCopy]
|
|
78
82
|
);
|
|
79
83
|
|
|
80
|
-
const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
|
|
81
|
-
|
|
82
84
|
const mergedStyles = useMemo(() => {
|
|
85
|
+
const baseStyles = theme === 'dark' ? markdownDark : markdownLight;
|
|
83
86
|
const result: Record<string, any> = { ...baseStyles };
|
|
84
87
|
|
|
85
88
|
for (const key in styles) {
|
|
@@ -99,7 +102,7 @@ export const Markdown: React.FC<Props> = ({
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
return result;
|
|
102
|
-
}, [
|
|
105
|
+
}, [theme, styles]);
|
|
103
106
|
|
|
104
107
|
return (
|
|
105
108
|
<MarkdownDisplay
|
|
@@ -112,135 +115,6 @@ export const Markdown: React.FC<Props> = ({
|
|
|
112
115
|
);
|
|
113
116
|
};
|
|
114
117
|
|
|
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
118
|
const markdownLight = {
|
|
245
119
|
body: { color: '#222', fontSize: 16, lineHeight: 24 },
|
|
246
120
|
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,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 };
|