@codearcade/expo-markdown 1.0.6 → 1.1.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhishek Singh
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md CHANGED
@@ -1,128 +1,37 @@
1
- # @codearcade/expo-markdown
2
-
3
- A modern, feature-rich Markdown renderer for React applications. Built for **speed**, **customizability**, and **developer experience**.
4
-
5
- Packed with **syntax highlighting**, **copy-to-clipboard** functionality, and seamless **dark mode** support out of the box.
6
-
7
- ---
8
-
9
- ## Features
10
-
11
- - ⚡ **Powered by MarkdownIt**: Robust and reliable rendering using `markdown-it`.
12
- - 🎨 **Theming Support**: First-class support for Light and Dark modes with customizable colors.
13
- - 💅 **Syntax Highlighting**: Integrated Prism syntax highlighting (supports themes via toggling).
14
- - 📋 **Copy Code**: Automatic "Copy to Clipboard" button for all code blocks.
15
- - 🍭 **GFM Support**: GitHub Flavored Markdown (tables, autolinks, etc.) enabled by default.
16
- - 🔌 **Plug & Play**: Works instantly with minimal configuration.
17
-
18
- ---
19
-
20
- ## Installation
21
-
22
- Install the package via your preferred package manager:
23
-
24
- ```bash
25
- npm install @codearcade/expo-markdown
26
- # or
27
- yarn add @codearcade/expo-markdown
28
- # or
29
- pnpm add @codearcade/expo-markdown
30
- # or
31
- bun add @codearcade/expo-markdown
32
- ```
33
-
34
- > **Note**: This package requires `expo-clipboard` as a peer dependency for the copy-to-clipboard functionality.
35
-
36
- ```bash
37
- npx expo install expo-clipboard
38
- ```
39
-
40
- ## Usage
41
-
42
- Using `@codearcade/expo-markdown` is simple. Import the `Markdown` component and pass your content string.
43
-
44
- ### Basic Usage
45
-
46
- ```tsx
47
- import { Markdown } from "@codearcade/expo-markdown";
48
-
49
- const markdown = `
50
- # Hello World
51
-
52
- This is a **markdown** component.
53
-
54
- \`\`\`javascript
55
- console.log("Hello from CodeArcade!");
56
- \`\`\`
57
- `;
58
-
59
- export default function App() {
60
- return (
61
- <View style={{ flex: 1 }}>
62
- <Markdown content={markdown} />
63
- </View>
64
- );
65
- }
66
- ```
67
-
68
- ### Dark Mode & Custom Themes
69
-
70
- Easily switch between light and dark modes, and customize the color palette for each mode.
71
-
72
- ```tsx
73
- import { useState } from "react";
74
- import { View } from "react-native";
75
- import { Markdown } from "@codearcade/expo-markdown";
76
-
77
- const myDarkTheme = {
78
- backgroundColor: "#1e1e1e",
79
- textColor: "#e0e0e0",
80
- codeBackgroundColor: "#252526",
81
- primaryColor: "#569cd6",
82
- secondaryColor: "#4ec9b0",
83
- };
84
-
85
- export default function BlogPost() {
86
- const [isDarkMode, setIsDarkMode] = useState(false);
87
-
88
- return (
89
- <View style={{ flex: 1 }}>
90
- <Markdown
91
- content="# My Blog Post"
92
- theme={isDarkMode ? "dark" : "light"}
93
- // Optional: Customize themes
94
- defaultDarkTheme={myDarkTheme}
95
- />
96
- </View>
97
- );
98
- }
99
- ```
100
-
101
- ## API Reference
102
-
103
- ### `<Markdown />`
104
-
105
- | Prop | Type | Default | Description |
106
- | ------------------- | ------------------- | ----------------------- | --------------------------------------------- |
107
- | `content` | `string` | **Required** | The raw markdown string to render. |
108
- | `theme` | `"light" \| "dark"` | `"light"` | Controls the rendering mode of the component. |
109
- | `defaultLightTheme` | `ThemeConfig` | `Default Light Palette` | Custom colors for light mode. |
110
- | `defaultDarkTheme` | `ThemeConfig` | `Default Dark Palette` | Custom colors for dark mode. |
111
-
112
- ### ThemeConfig Interface
113
-
114
- The theme objects (`defaultLightTheme` and `defaultDarkTheme`) should follow this structure:
115
-
116
- ```typescript
117
- interface ThemeConfig {
118
- backgroundColor: string;
119
- textColor: string;
120
- codeBackgroundColor: string;
121
- primaryColor: string;
122
- secondaryColor: string;
123
- }
124
- ```
125
-
126
- ## License
127
-
128
- MIT © [CodeArcade](https://github.com/codearcade-io)
1
+ # @codearcade/expo-markdown
2
+
3
+ Markdown component to render markdown in expo with syntax highlighting and styling
4
+
5
+ ## Installation
6
+
7
+
8
+ ```sh
9
+ npm install @codearcade/expo-markdown
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+
16
+ ```js
17
+ import { multiply } from '@codearcade/expo-markdown';
18
+
19
+ // ...
20
+
21
+ const result = await multiply(3, 7);
22
+ ```
23
+
24
+
25
+ ## Contributing
26
+
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)
30
+
31
+ ## License
32
+
33
+ MIT
34
+
35
+ ---
36
+
37
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -1,4 +1,46 @@
1
- import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}from"react";import{ActivityIndicator as A,StyleSheet as H,View as P}from"react-native";import{WebView as U}from"react-native-webview";import{jsxDEV as J}from"react/jsx-dev-runtime";var I=new _({html:!0,linkify:!0,typographer:!0}),S=({content:K,theme:q="light",defaultDarkTheme:F={backgroundColor:"#111111ff",textColor:"#fff",codeBackgroundColor:"#2d2d2d",primaryColor:"#326fdfff",secondaryColor:"#39c927ff"},defaultLightTheme:G={backgroundColor:"#ffffffff",textColor:"#000",codeBackgroundColor:"#f3f3f3ff",primaryColor:"#326fdfff",secondaryColor:"#39c927ff"}})=>{let z=q==="dark"?F:G,O=$(()=>I.render(K),[K]),Q=q==="dark"?"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css":"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css",R=`
1
+ "use strict";
2
+
3
+ import * as Clipboard from 'expo-clipboard';
4
+ import MarkdownIt from 'markdown-it';
5
+ import React, { useMemo } from 'react';
6
+ import { ActivityIndicator, StyleSheet, View } from 'react-native';
7
+ import { WebView } from 'react-native-webview';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ const mdParser = new MarkdownIt({
10
+ html: true,
11
+ linkify: true,
12
+ typographer: true
13
+ });
14
+ export const Markdown = ({
15
+ content,
16
+ theme = 'light',
17
+ defaultDarkTheme = {
18
+ backgroundColor: '#111111ff',
19
+ textColor: '#fff',
20
+ codeBackgroundColor: '#2d2d2d',
21
+ primaryColor: '#326fdfff',
22
+ secondaryColor: '#39c927ff'
23
+ },
24
+ defaultLightTheme = {
25
+ backgroundColor: '#ffffffff',
26
+ textColor: '#000',
27
+ codeBackgroundColor: '#f3f3f3ff',
28
+ primaryColor: '#326fdfff',
29
+ secondaryColor: '#39c927ff'
30
+ }
31
+ }) => {
32
+ const activeTheme = theme === 'dark' ? defaultDarkTheme : defaultLightTheme;
33
+
34
+ // 1. Convert Markdown to HTML String
35
+ const htmlContent = useMemo(() => mdParser.render(content), [content]);
36
+
37
+ // 2. Select Prism Theme (CDN URLs)
38
+ // You can swap these URLs for any theme from the list you provided
39
+ const prismThemeUrl = theme === 'dark' ? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css' : 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
40
+
41
+ // 3. Your Custom CSS (Converted to String)
42
+ // I pasted your exact CSS here, plus some WebView specific tweaks
43
+ const userStyles = `
2
44
 
3
45
  * { box-sizing: border-box; }
4
46
 
@@ -8,8 +50,8 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
8
50
  font-size: 16px; /* Body text size */
9
51
  line-height: 1.6;
10
52
  margin: 0;
11
- background-color: ${z.backgroundColor} !important;
12
- color: ${z.textColor} !important;
53
+ background-color: ${activeTheme.backgroundColor} !important;
54
+ color: ${activeTheme.textColor} !important;
13
55
  }
14
56
 
15
57
  /* =========================
@@ -40,7 +82,7 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
40
82
  .markdownBody p { margin-bottom: 1rem; }
41
83
 
42
84
  .markdownBody a {
43
- color: ${z.primaryColor};
85
+ color: ${activeTheme.primaryColor};
44
86
  text-decoration: none;
45
87
  }
46
88
  .markdownBody a:hover { text-decoration: underline; }
@@ -122,8 +164,8 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
122
164
  position: relative; /* Anchor for the absolute button */
123
165
  margin: 1.5rem 0;
124
166
  border-radius: 0.5rem;
125
- background-color: ${z.codeBackgroundColor} !important;
126
- border: 1px solid ${q==="dark"?"#3e3e3e":"#e0e0e0"};
167
+ background-color: ${activeTheme.codeBackgroundColor} !important;
168
+ border: 1px solid ${theme === 'dark' ? '#3e3e3e' : '#e0e0e0'};
127
169
  overflow: hidden; /* Ensures rounded corners */
128
170
  }
129
171
 
@@ -132,9 +174,9 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
132
174
  position: absolute;
133
175
  top: 6px;
134
176
  right: 6px;
135
- background: ${q==="dark"?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.05)"};
136
- color: ${q==="dark"?"#ccc":"#666"};
137
- border: 1px solid ${q==="dark"?"#555":"#ddd"};
177
+ background: ${theme === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'};
178
+ color: ${theme === 'dark' ? '#ccc' : '#666'};
179
+ border: 1px solid ${theme === 'dark' ? '#555' : '#ddd'};
138
180
  border-radius: 4px;
139
181
  padding: 4px 10px;
140
182
  font-size: 12px;
@@ -146,8 +188,8 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
146
188
 
147
189
  /* Code Block Container */
148
190
  pre {
149
- background-color: ${q==="dark"?F.codeBackgroundColor:G.codeBackgroundColor} !important;
150
- color: ${q==="dark"?F.textColor:G.textColor} !important;
191
+ background-color: ${theme === 'dark' ? defaultDarkTheme.codeBackgroundColor : defaultLightTheme.codeBackgroundColor} !important;
192
+ color: ${theme === 'dark' ? defaultDarkTheme.textColor : defaultLightTheme.textColor} !important;
151
193
  position: relative;
152
194
  padding: 1rem;
153
195
  border-radius: 0.5rem;
@@ -169,16 +211,17 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
169
211
  font-size: 13px;
170
212
  }
171
213
 
172
- `,X=`
214
+ `;
215
+ const htmlSource = `
173
216
  <!DOCTYPE html>
174
217
  <html>
175
218
  <head>
176
219
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
177
- <link href="${Q}" rel="stylesheet" />
178
- <style>${R}</style>
220
+ <link href="${prismThemeUrl}" rel="stylesheet" />
221
+ <style>${userStyles}</style>
179
222
  </head>
180
223
  <body class="markdownBody">
181
- ${O}
224
+ ${htmlContent}
182
225
 
183
226
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
184
227
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
@@ -217,8 +260,51 @@ import*as N from"expo-clipboard";import _ from"markdown-it";import{useMemo as $}
217
260
  });
218
261
 
219
262
  wrapper.appendChild(button);
263
+
264
+ if (window.Prism) {
265
+ window.Prism.highlightAll();
266
+ }
220
267
  });
221
268
  </script>
222
269
  </body>
223
270
  </html>
224
- `,Y=async(Z)=>{try{let B=JSON.parse(Z.nativeEvent.data);if(B.type==="copy")await N.setStringAsync(B.payload)}catch(B){console.error("Error parsing webview message",B)}};return J(P,{style:W.container,children:J(U,{originWhitelist:["*"],source:{html:X},onMessage:Y,javaScriptEnabled:!0,domStorageEnabled:!0,startInLoadingState:!0,renderLoading:()=>J(A,{size:"large"},void 0,!1,void 0,this),style:{flex:1,backgroundColor:"transparent"}},void 0,!1,void 0,this)},void 0,!1,void 0,this)},W=H.create({container:{flex:1}});export{S as Markdown};
271
+ `;
272
+
273
+ // 5. Handle Messages from WebView (Copy to Clipboard)
274
+ const handleMessage = async event => {
275
+ try {
276
+ const data = JSON.parse(event.nativeEvent.data);
277
+ if (data.type === 'copy') {
278
+ await Clipboard.setStringAsync(data.payload);
279
+ }
280
+ } catch (error) {
281
+ console.error('Error parsing webview message', error);
282
+ }
283
+ };
284
+ return /*#__PURE__*/_jsx(View, {
285
+ style: styles.container,
286
+ children: /*#__PURE__*/_jsx(WebView, {
287
+ originWhitelist: ['*'],
288
+ source: {
289
+ html: htmlSource
290
+ },
291
+ onMessage: handleMessage,
292
+ javaScriptEnabled: true,
293
+ domStorageEnabled: true,
294
+ startInLoadingState: true,
295
+ renderLoading: () => /*#__PURE__*/_jsx(ActivityIndicator, {
296
+ size: "large"
297
+ }),
298
+ style: {
299
+ flex: 1,
300
+ backgroundColor: 'transparent'
301
+ }
302
+ })
303
+ });
304
+ };
305
+ const styles = StyleSheet.create({
306
+ container: {
307
+ flex: 1
308
+ }
309
+ });
310
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Clipboard","MarkdownIt","React","useMemo","ActivityIndicator","StyleSheet","View","WebView","jsx","_jsx","mdParser","html","linkify","typographer","Markdown","content","theme","defaultDarkTheme","backgroundColor","textColor","codeBackgroundColor","primaryColor","secondaryColor","defaultLightTheme","activeTheme","htmlContent","render","prismThemeUrl","userStyles","htmlSource","handleMessage","event","data","JSON","parse","nativeEvent","type","setStringAsync","payload","error","console","style","styles","container","children","originWhitelist","source","onMessage","javaScriptEnabled","domStorageEnabled","startInLoadingState","renderLoading","size","flex","create"],"sourceRoot":"..\\..\\..\\src","sources":["components/markdown.tsx"],"mappings":";;AAAA,OAAO,KAAKA,SAAS,MAAM,gBAAgB;AAC3C,OAAOC,UAAU,MAAM,aAAa;AACpC,OAAOC,KAAK,IAAIC,OAAO,QAAQ,OAAO;AACtC,SAASC,iBAAiB,EAAEC,UAAU,EAAEC,IAAI,QAAQ,cAAc;AAElE,SAASC,OAAO,QAAQ,sBAAsB;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE/C,MAAMC,QAAQ,GAAG,IAAIT,UAAU,CAAC;EAC9BU,IAAI,EAAE,IAAI;EACVC,OAAO,EAAE,IAAI;EACbC,WAAW,EAAE;AACf,CAAC,CAAC;AAqBF,OAAO,MAAMC,QAAyB,GAAGA,CAAC;EACxCC,OAAO;EACPC,KAAK,GAAG,OAAO;EACfC,gBAAgB,GAAG;IACjBC,eAAe,EAAE,WAAW;IAC5BC,SAAS,EAAE,MAAM;IACjBC,mBAAmB,EAAE,SAAS;IAC9BC,YAAY,EAAE,WAAW;IACzBC,cAAc,EAAE;EAClB,CAAC;EACDC,iBAAiB,GAAG;IAClBL,eAAe,EAAE,WAAW;IAC5BC,SAAS,EAAE,MAAM;IACjBC,mBAAmB,EAAE,WAAW;IAChCC,YAAY,EAAE,WAAW;IACzBC,cAAc,EAAE;EAClB;AACF,CAAC,KAAK;EACJ,MAAME,WAAW,GAAGR,KAAK,KAAK,MAAM,GAAGC,gBAAgB,GAAGM,iBAAiB;;EAE3E;EACA,MAAME,WAAW,GAAGtB,OAAO,CAAC,MAAMO,QAAQ,CAACgB,MAAM,CAACX,OAAO,CAAC,EAAE,CAACA,OAAO,CAAC,CAAC;;EAEtE;EACA;EACA,MAAMY,aAAa,GACjBX,KAAK,KAAK,MAAM,GACZ,mFAAmF,GACnF,0EAA0E;;EAEhF;EACA;EACA,MAAMY,UAAU,GAAG;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0BJ,WAAW,CAACN,eAAe;AACrD,eAAeM,WAAW,CAACL,SAAS;AACpC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeK,WAAW,CAACH,YAAY;AACvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BAA0BG,WAAW,CAACJ,mBAAmB;AACzD,0BAA0BJ,KAAK,KAAK,MAAM,GAAG,SAAS,GAAG,SAAS;AAClE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBACQA,KAAK,KAAK,MAAM,GAAG,uBAAuB,GAAG,kBAAkB;AACvE,eACeA,KAAK,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM;AACjD,0BAA0BA,KAAK,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,0BACQA,KAAK,KAAK,MAAM,GACZC,gBAAgB,CAACG,mBAAmB,GACpCG,iBAAiB,CAACH,mBAAmB;AACjD,eAEQJ,KAAK,KAAK,MAAM,GACZC,gBAAgB,CAACE,SAAS,GAC1BI,iBAAiB,CAACJ,SAAS;AACvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GACG;EAED,MAAMU,UAAU,GAAG;AACrB;AACA;AACA;AACA;AACA,gBAAgBF,aAAa;AAC7B,WAAWC,UAAU;AACrB;AACA;AACA,IAAIH,WAAW;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;EAED;EACA,MAAMK,aAAa,GAAG,MAAOC,KAA0B,IAAK;IAC1D,IAAI;MACF,MAAMC,IAAI,GAAGC,IAAI,CAACC,KAAK,CAACH,KAAK,CAACI,WAAW,CAACH,IAAI,CAAC;MAC/C,IAAIA,IAAI,CAACI,IAAI,KAAK,MAAM,EAAE;QACxB,MAAMpC,SAAS,CAACqC,cAAc,CAACL,IAAI,CAACM,OAAO,CAAC;MAC9C;IACF,CAAC,CAAC,OAAOC,KAAK,EAAE;MACdC,OAAO,CAACD,KAAK,CAAC,+BAA+B,EAAEA,KAAK,CAAC;IACvD;EACF,CAAC;EAED,oBACE9B,IAAA,CAACH,IAAI;IAACmC,KAAK,EAAEC,MAAM,CAACC,SAAU;IAAAC,QAAA,eAC5BnC,IAAA,CAACF,OAAO;MACNsC,eAAe,EAAE,CAAC,GAAG,CAAE;MACvBC,MAAM,EAAE;QAAEnC,IAAI,EAAEkB;MAAW,CAAE;MAC7BkB,SAAS,EAAEjB,aAAc;MACzBkB,iBAAiB,EAAE,IAAK;MACxBC,iBAAiB,EAAE,IAAK;MACxBC,mBAAmB,EAAE,IAAK;MAC1BC,aAAa,EAAEA,CAAA,kBAAM1C,IAAA,CAACL,iBAAiB;QAACgD,IAAI,EAAC;MAAO,CAAE,CAAE;MACxDX,KAAK,EAAE;QAAEY,IAAI,EAAE,CAAC;QAAEnC,eAAe,EAAE;MAAc;IAAE,CACpD;EAAC,CACE,CAAC;AAEX,CAAC;AAED,MAAMwB,MAAM,GAAGrC,UAAU,CAACiD,MAAM,CAAC;EAC/BX,SAAS,EAAE;IACTU,IAAI,EAAE;EACR;AACF,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export * from "./components/markdown.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;AAAA,cAAc,0BAAuB","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -1,7 +1,7 @@
1
- import React from "react";
1
+ import React from 'react';
2
2
  interface Props {
3
3
  content: string;
4
- theme?: "light" | "dark";
4
+ theme?: 'light' | 'dark';
5
5
  defaultDarkTheme?: {
6
6
  backgroundColor: string;
7
7
  textColor: string;
@@ -19,3 +19,4 @@ interface Props {
19
19
  }
20
20
  export declare const Markdown: React.FC<Props>;
21
21
  export {};
22
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAWvC,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE;QACjB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,iBAAiB,CAAC,EAAE;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA2SpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./components/markdown";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,60 +1,123 @@
1
1
  {
2
2
  "name": "@codearcade/expo-markdown",
3
- "type": "module",
4
- "version": "1.0.6",
5
- "module": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
7
- "license": "MIT",
3
+ "version": "1.1.0",
4
+ "description": "Markdown component to render markdown in expo with syntax highlighting and styling",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
8
7
  "exports": {
9
8
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "default": "./dist/index.js"
12
- }
13
- },
14
- "scripts": {
15
- "build": "bun ./builder.ts && bun run generate-types",
16
- "prepublishOnly": "bun run build",
17
- "generate-types": "bunx tsc --emitDeclarationOnly --declaration --outDir dist",
18
- "clear": "rm -rf dist"
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
19
14
  },
20
15
  "files": [
21
- "dist",
22
- "README.md",
23
- "package.json"
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
24
33
  ],
34
+ "scripts": {
35
+ "example": "yarn workspace @codearcade/expo-markdown-example",
36
+ "clean": "del-cli lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\""
40
+ },
25
41
  "keywords": [
26
- "expo",
27
- "markdown",
28
- "markdown-expo",
29
42
  "react-native",
30
- "react-native-markdown",
31
- "expo-markdown",
32
- "@codearcade/expo-markdown"
43
+ "ios",
44
+ "android"
33
45
  ],
34
- "publishConfig": {
35
- "access": "public"
36
- },
37
- "homepage": "https://github.com/codearcade-io/expo-markdown.git#readme",
38
46
  "repository": {
39
47
  "type": "git",
40
48
  "url": "git+https://github.com/codearcade-io/expo-markdown.git"
41
49
  },
42
- "bugs": "https://github.com/codearcade-io/expo-markdown/issues",
43
- "author": "CodeArcade <abhi@codearcade.io>",
50
+ "author": "Abhishek Singh <official.6packprogrammer@gmail.com> (https://github.com/meabhisingh)",
51
+ "license": "MIT",
52
+ "bugs": {
53
+ "url": "https://github.com/codearcade-io/expo-markdown/issues"
54
+ },
55
+ "homepage": "https://github.com/codearcade-io/expo-markdown#readme",
56
+ "publishConfig": {
57
+ "registry": "https://registry.npmjs.org/"
58
+ },
44
59
  "devDependencies": {
45
- "@types/bun": "^1.2.20",
46
- "@types/markdown-it": "^14.1.2",
47
- "tslib": "^2.8.1",
48
- "typedoc": "^0.28.10",
49
- "typescript": ">=5.0.0",
50
- "@types/react": ">=18.0.0"
60
+ "@eslint/compat": "^1.3.2",
61
+ "@eslint/eslintrc": "^3.3.1",
62
+ "@eslint/js": "^9.35.0",
63
+ "@react-native/babel-preset": "0.83.0",
64
+ "@react-native/eslint-config": "0.83.0",
65
+ "@types/markdown-it": "^14",
66
+ "@types/react": "^19.1.12",
67
+ "del-cli": "^6.0.0",
68
+ "eslint": "^9.35.0",
69
+ "eslint-config-prettier": "^10.1.8",
70
+ "eslint-plugin-prettier": "^5.5.4",
71
+ "expo-clipboard": "^8.0.8",
72
+ "prettier": "^2.8.8",
73
+ "react": "19.1.0",
74
+ "react-native": "0.81.5",
75
+ "react-native-builder-bob": "^0.40.13",
76
+ "react-native-webview": "^13.16.0",
77
+ "typescript": "^5.9.2"
51
78
  },
52
79
  "peerDependencies": {
53
- "expo": ">=52.0.0",
54
- "expo-clipboard": ">=6.0.0",
55
- "react": ">=18.0.0",
56
- "react-native": ">=0.72.0",
57
- "react-native-webview": ">=13.0.0"
80
+ "expo-clipboard": "*",
81
+ "react": "*",
82
+ "react-native": "*",
83
+ "react-native-webview": "*"
84
+ },
85
+ "workspaces": [
86
+ "example"
87
+ ],
88
+ "packageManager": "yarn@4.11.0",
89
+ "react-native-builder-bob": {
90
+ "source": "src",
91
+ "output": "lib",
92
+ "targets": [
93
+ [
94
+ "module",
95
+ {
96
+ "esm": true
97
+ }
98
+ ],
99
+ [
100
+ "typescript",
101
+ {
102
+ "project": "tsconfig.build.json"
103
+ }
104
+ ]
105
+ ]
106
+ },
107
+ "prettier": {
108
+ "quoteProps": "consistent",
109
+ "singleQuote": true,
110
+ "tabWidth": 2,
111
+ "trailingComma": "es5",
112
+ "useTabs": false
113
+ },
114
+ "create-react-native-library": {
115
+ "type": "library",
116
+ "languages": "js",
117
+ "tools": [
118
+ "eslint"
119
+ ],
120
+ "version": "0.57.0"
58
121
  },
59
122
  "dependencies": {
60
123
  "markdown-it": "^14.1.0"
@@ -0,0 +1,338 @@
1
+ import * as Clipboard from 'expo-clipboard';
2
+ import MarkdownIt from 'markdown-it';
3
+ import React, { useMemo } from 'react';
4
+ import { ActivityIndicator, StyleSheet, View } from 'react-native';
5
+ import type { WebViewMessageEvent } from 'react-native-webview';
6
+ import { WebView } from 'react-native-webview';
7
+
8
+ const mdParser = new MarkdownIt({
9
+ html: true,
10
+ linkify: true,
11
+ typographer: true,
12
+ });
13
+
14
+ interface Props {
15
+ content: string;
16
+ theme?: 'light' | 'dark';
17
+ defaultDarkTheme?: {
18
+ backgroundColor: string;
19
+ textColor: string;
20
+ codeBackgroundColor: string;
21
+ primaryColor: string;
22
+ secondaryColor: string;
23
+ };
24
+ defaultLightTheme?: {
25
+ backgroundColor: string;
26
+ textColor: string;
27
+ codeBackgroundColor: string;
28
+ primaryColor: string;
29
+ secondaryColor: string;
30
+ };
31
+ }
32
+
33
+ export const Markdown: React.FC<Props> = ({
34
+ content,
35
+ theme = 'light',
36
+ defaultDarkTheme = {
37
+ backgroundColor: '#111111ff',
38
+ textColor: '#fff',
39
+ codeBackgroundColor: '#2d2d2d',
40
+ primaryColor: '#326fdfff',
41
+ secondaryColor: '#39c927ff',
42
+ },
43
+ defaultLightTheme = {
44
+ backgroundColor: '#ffffffff',
45
+ textColor: '#000',
46
+ codeBackgroundColor: '#f3f3f3ff',
47
+ primaryColor: '#326fdfff',
48
+ secondaryColor: '#39c927ff',
49
+ },
50
+ }) => {
51
+ const activeTheme = theme === 'dark' ? defaultDarkTheme : defaultLightTheme;
52
+
53
+ // 1. Convert Markdown to HTML String
54
+ const htmlContent = useMemo(() => mdParser.render(content), [content]);
55
+
56
+ // 2. Select Prism Theme (CDN URLs)
57
+ // You can swap these URLs for any theme from the list you provided
58
+ const prismThemeUrl =
59
+ theme === 'dark'
60
+ ? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'
61
+ : 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css';
62
+
63
+ // 3. Your Custom CSS (Converted to String)
64
+ // I pasted your exact CSS here, plus some WebView specific tweaks
65
+ const userStyles = `
66
+
67
+ * { box-sizing: border-box; }
68
+
69
+ .markdownBody {
70
+ padding: 1rem;
71
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
72
+ font-size: 16px; /* Body text size */
73
+ line-height: 1.6;
74
+ margin: 0;
75
+ background-color: ${activeTheme.backgroundColor} !important;
76
+ color: ${activeTheme.textColor} !important;
77
+ }
78
+
79
+ /* =========================
80
+ Headings
81
+ ========================= */
82
+ .markdownBody h1, .markdownBody h2, .markdownBody h3,
83
+ .markdownBody h4, .markdownBody h5, .markdownBody h6 {
84
+ font-weight: 600;
85
+ margin-top: 2rem;
86
+ margin-bottom: 1rem;
87
+ line-height: 1.25;
88
+ color: inherit;
89
+ }
90
+
91
+ .markdownBody h1 { font-size: 1.875rem; padding-bottom: 0.3em; }
92
+ .markdownBody h2 { font-size: 1.5rem; padding-bottom: 0.3em; }
93
+ .markdownBody h3 { font-size: 1.25rem; }
94
+
95
+ .markdownBody h1:first-child,
96
+ .markdownBody h2:first-child,
97
+ .markdownBody h3:first-child {
98
+ margin-top: 0;
99
+ }
100
+
101
+ /* =========================
102
+ Paragraphs & Links
103
+ ========================= */
104
+ .markdownBody p { margin-bottom: 1rem; }
105
+
106
+ .markdownBody a {
107
+ color: ${activeTheme.primaryColor};
108
+ text-decoration: none;
109
+ }
110
+ .markdownBody a:hover { text-decoration: underline; }
111
+
112
+ /* =========================
113
+ Lists (UL, OL, LI) - Added!
114
+ ========================= */
115
+ .markdownBody ul,
116
+ .markdownBody ol {
117
+ padding-left: 1.5rem; /* Indentation */
118
+ margin-bottom: 1rem;
119
+ }
120
+
121
+ .markdownBody ul { list-style-type: disc; }
122
+ .markdownBody ol { list-style-type: decimal; }
123
+
124
+ .markdownBody li {
125
+ margin-bottom: 0.25rem;
126
+ }
127
+
128
+ /* Nested Lists */
129
+ .markdownBody li > ul,
130
+ .markdownBody li > ol {
131
+ margin-top: 0.25rem;
132
+ margin-bottom: 0;
133
+ }
134
+
135
+ /* =========================
136
+ Blockquotes
137
+ ========================= */
138
+ .markdownBody blockquote {
139
+ border-left: 4px solid rgba(0,0,0,0.02);
140
+ padding-left: 1rem;
141
+ font-style: italic;
142
+ color: rgba(0,0,0,0.5);
143
+ margin: 1rem 0;
144
+ margin-left: 0;
145
+ }
146
+
147
+ /* =========================
148
+ Tables
149
+ ========================= */
150
+ .markdownBody table {
151
+ width: 100%;
152
+ border-collapse: collapse;
153
+ margin-bottom: 1rem;
154
+ display: block;
155
+ overflow-x: auto;
156
+ }
157
+
158
+ .markdownBody th,
159
+ .markdownBody td {
160
+ border: 1px solid rgba(0,0,0,0.02);
161
+ padding: 0.5rem;
162
+ text-align: left;
163
+ }
164
+
165
+ .markdownBody th { font-weight: 600; background-color: rgba(0,0,0,0.02) }
166
+
167
+ /* =========================
168
+ Images & HR
169
+ ========================= */
170
+ .markdownBody img { max-width: 100%; height: auto; border-radius: 0.375rem; }
171
+
172
+ .markdownBody hr {
173
+ border: none;
174
+ border-top: 2px solid rgba(0,0,0,0.02);
175
+ margin: 2rem 0;
176
+ }
177
+
178
+ /* =========================
179
+ Text Emphasis
180
+ ========================= */
181
+ .markdownBody strong { font-weight: 700; }
182
+ .markdownBody em { font-style: italic; }
183
+ .markdownBody del { text-decoration: line-through; opacity: 0.7; }
184
+
185
+ .code-wrapper {
186
+ position: relative; /* Anchor for the absolute button */
187
+ margin: 1.5rem 0;
188
+ border-radius: 0.5rem;
189
+ background-color: ${activeTheme.codeBackgroundColor} !important;
190
+ border: 1px solid ${theme === 'dark' ? '#3e3e3e' : '#e0e0e0'};
191
+ overflow: hidden; /* Ensures rounded corners */
192
+ }
193
+
194
+ /* 2. The Copy Button (Stays fixed in top right of container) */
195
+ .copy-btn {
196
+ position: absolute;
197
+ top: 6px;
198
+ right: 6px;
199
+ background: ${
200
+ theme === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)'
201
+ };
202
+ color: ${theme === 'dark' ? '#ccc' : '#666'};
203
+ border: 1px solid ${theme === 'dark' ? '#555' : '#ddd'};
204
+ border-radius: 4px;
205
+ padding: 4px 10px;
206
+ font-size: 12px;
207
+ font-weight: 600;
208
+ cursor: pointer;
209
+ z-index: 10;
210
+ }
211
+ .copy-btn:active { transform: scale(0.95); opacity: 0.8; }
212
+
213
+ /* Code Block Container */
214
+ pre {
215
+ background-color: ${
216
+ theme === 'dark'
217
+ ? defaultDarkTheme.codeBackgroundColor
218
+ : defaultLightTheme.codeBackgroundColor
219
+ } !important;
220
+ color: ${
221
+ theme === 'dark'
222
+ ? defaultDarkTheme.textColor
223
+ : defaultLightTheme.textColor
224
+ } !important;
225
+ position: relative;
226
+ padding: 1rem;
227
+ border-radius: 0.5rem;
228
+ overflow-x: auto;
229
+ }
230
+
231
+ /* 4. The Code Font (Smaller & Sharper) */
232
+ code {
233
+ font-family: "Menlo", "Monaco", "Courier New", monospace !important;
234
+ font-size: 13px !important;
235
+ line-height: 1.5;
236
+ }
237
+
238
+ /* Inline code styling (not blocks) */
239
+ p > code {
240
+ background-color: rgba(0, 0, 0, 0.13);
241
+ padding: 2px 4px;
242
+ border-radius: 4px;
243
+ font-size: 13px;
244
+ }
245
+
246
+ `;
247
+
248
+ const htmlSource = `
249
+ <!DOCTYPE html>
250
+ <html>
251
+ <head>
252
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
253
+ <link href="${prismThemeUrl}" rel="stylesheet" />
254
+ <style>${userStyles}</style>
255
+ </head>
256
+ <body class="markdownBody">
257
+ ${htmlContent}
258
+
259
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
260
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
261
+
262
+ <script>
263
+ // 1. Add Copy Buttons to all <pre> blocks
264
+ document.querySelectorAll('pre').forEach((pre) => {
265
+
266
+
267
+ const wrapper = document.createElement('div');
268
+ wrapper.className = 'code-wrapper';
269
+
270
+ pre.parentNode.insertBefore(wrapper, pre);
271
+ wrapper.appendChild(pre);
272
+
273
+ // Create button
274
+ const button = document.createElement('button');
275
+ button.innerText = 'Copy';
276
+ button.className = 'copy-btn';
277
+
278
+ // Add click event
279
+ button.addEventListener('click', () => {
280
+ // Get code text
281
+ const code = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText;
282
+
283
+ // Send to React Native
284
+ window.ReactNativeWebView.postMessage(JSON.stringify({
285
+ type: 'copy',
286
+ payload: code
287
+ }));
288
+
289
+ // Visual feedback
290
+ const originalText = button.innerText;
291
+ button.innerText = 'Copied!';
292
+ setTimeout(() => { button.innerText = originalText; }, 2000);
293
+ });
294
+
295
+ wrapper.appendChild(button);
296
+
297
+ if (window.Prism) {
298
+ window.Prism.highlightAll();
299
+ }
300
+ });
301
+ </script>
302
+ </body>
303
+ </html>
304
+ `;
305
+
306
+ // 5. Handle Messages from WebView (Copy to Clipboard)
307
+ const handleMessage = async (event: WebViewMessageEvent) => {
308
+ try {
309
+ const data = JSON.parse(event.nativeEvent.data);
310
+ if (data.type === 'copy') {
311
+ await Clipboard.setStringAsync(data.payload);
312
+ }
313
+ } catch (error) {
314
+ console.error('Error parsing webview message', error);
315
+ }
316
+ };
317
+
318
+ return (
319
+ <View style={styles.container}>
320
+ <WebView
321
+ originWhitelist={['*']}
322
+ source={{ html: htmlSource }}
323
+ onMessage={handleMessage}
324
+ javaScriptEnabled={true}
325
+ domStorageEnabled={true}
326
+ startInLoadingState={true}
327
+ renderLoading={() => <ActivityIndicator size="large" />}
328
+ style={{ flex: 1, backgroundColor: 'transparent' }}
329
+ />
330
+ </View>
331
+ );
332
+ };
333
+
334
+ const styles = StyleSheet.create({
335
+ container: {
336
+ flex: 1,
337
+ },
338
+ });
@@ -1 +1 @@
1
- export * from "./components/markdown";
1
+ export * from "./components/markdown";