@affanhamid/markdown-renderer 2.2.0 → 2.3.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 +134 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# @affanhamid/markdown-renderer
|
|
2
2
|
|
|
3
|
-
A React markdown renderer built for AI-generated content.
|
|
3
|
+
> A React markdown renderer built for AI-generated content. One import. Math, code, diagrams, callouts — all handled.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## The Problem
|
|
6
6
|
|
|
7
7
|
If you've used `react-markdown` with `remark-math` and `rehype-katex` to render LLM output, you've hit these problems:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
**
|
|
9
|
+
| Problem | Details |
|
|
10
|
+
|---------|---------|
|
|
11
|
+
| **Dollar signs break everything** | `remark-math` uses `$` for math, but `$` is also currency. Their `singleDollarTextMath: false` disables single-dollar math entirely. This package disambiguates intelligently: `$20` → currency, `$x + y$` → math. |
|
|
12
|
+
| **Inconsistent math delimiters** | GPT, Claude, and Gemini variously output `$`, `$$`, `\(…\)`, and `\[…\]`. `remark-math` doesn't support `\(…\)` or `\[…\]`. This package normalizes all four formats automatically. |
|
|
13
|
+
| **Too many moving parts** | The standard setup: `react-markdown` + `remark-math` + `remark-gfm` + `rehype-katex` + KaTeX CSS + syntax highlighter + custom components. This package is **one import**. |
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
@@ -21,6 +21,8 @@ If you've used `react-markdown` with `remark-math` and `rehype-katex` to render
|
|
|
21
21
|
- **Executable code blocks** — optional `onRunCode` callback for running Python, R, etc.
|
|
22
22
|
- **Inline images** — `` works inside paragraphs, not just as standalone blocks
|
|
23
23
|
- **Semantic color tags** — `{color:important}text{/color}` for highlighting (important, definition, example, note, formula)
|
|
24
|
+
- **Callout blocks** — LaTeX-style `\begin{callout}{color}...\end{callout}` with any Tailwind color name
|
|
25
|
+
- **Mermaid diagrams** — fenced `mermaid` code blocks render as diagrams (client-side hydration with dynamic import)
|
|
24
26
|
- **Auto-scaling brackets** — `($x + y$)` automatically uses `\left(` and `\right)` for proper sizing
|
|
25
27
|
- **Prompt appendix** — exported `MATH_MARKDOWN_RULES_APPENDIX` string to append to your LLM system prompt, steering models toward consistent delimiter usage
|
|
26
28
|
|
|
@@ -98,6 +100,36 @@ import { MATH_MARKDOWN_RULES_APPENDIX } from "@affanhamid/markdown-renderer";
|
|
|
98
100
|
const systemPrompt = `You are a helpful assistant.\n\n${MATH_MARKDOWN_RULES_APPENDIX}`;
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
### Callout blocks
|
|
104
|
+
|
|
105
|
+
Use LaTeX-style syntax with any Tailwind color name:
|
|
106
|
+
|
|
107
|
+
```md
|
|
108
|
+
\begin{callout}{amber}
|
|
109
|
+
**Warning:** This operation is irreversible.
|
|
110
|
+
\end{callout}
|
|
111
|
+
|
|
112
|
+
\begin{callout}{green}
|
|
113
|
+
**Tip:** Use `Ctrl+S` to save quickly.
|
|
114
|
+
\end{callout}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Renders as styled callout boxes with `border-{color}-200`, `bg-{color}-50`, and `text-{color}-900` classes.
|
|
118
|
+
|
|
119
|
+
### Mermaid diagrams
|
|
120
|
+
|
|
121
|
+
Fenced code blocks with `mermaid` as the language render as diagrams:
|
|
122
|
+
|
|
123
|
+
````md
|
|
124
|
+
```mermaid
|
|
125
|
+
graph LR
|
|
126
|
+
A[Input] --> B[Process]
|
|
127
|
+
B --> C[Output]
|
|
128
|
+
```
|
|
129
|
+
````
|
|
130
|
+
|
|
131
|
+
On the client, mermaid is dynamically imported and renders after hydration. For server-side rendering (`renderMarkdownToHtml`), a `<pre>` fallback is output inside a `.md-mermaid` container with `data-mermaid-code` for later hydration.
|
|
132
|
+
|
|
101
133
|
## API
|
|
102
134
|
|
|
103
135
|
### `<MarkdownRenderer />` (default export)
|
|
@@ -105,6 +137,7 @@ const systemPrompt = `You are a helpful assistant.\n\n${MATH_MARKDOWN_RULES_APPE
|
|
|
105
137
|
| Prop | Type | Default | Description |
|
|
106
138
|
|------|------|---------|-------------|
|
|
107
139
|
| `markdown` | `string` | required | Markdown content to render |
|
|
140
|
+
| `className` | `string` | `undefined` | CSS class applied to the wrapper `<div>` |
|
|
108
141
|
| `onRunCode` | `(code: string, language: string) => Promise<CodeExecutionResult>` | `undefined` | Callback for executing code blocks |
|
|
109
142
|
| `executableLanguages` | `string[]` | `["python", "r"]` | Languages that get a "Run" button |
|
|
110
143
|
|
|
@@ -130,6 +163,100 @@ Normalizes `\(...\)` to `$...$` and `\[...\]` to `$$...$$`. Converts inline `$$.
|
|
|
130
163
|
|
|
131
164
|
A plain-text string with math formatting rules to append to LLM system prompts.
|
|
132
165
|
|
|
166
|
+
## Theming with Tailwind CSS
|
|
167
|
+
|
|
168
|
+
The `className` prop sets a class on the outer wrapper `<div>`, which you can use as a scoping selector for custom themes.
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
<MarkdownRenderer markdown={content} className="my-theme" />
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
This renders:
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<div class="my-theme">
|
|
178
|
+
<h1>...</h1>
|
|
179
|
+
<p>...</p>
|
|
180
|
+
<div class="md-callout ...">...</div>
|
|
181
|
+
<!-- etc. -->
|
|
182
|
+
</div>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Creating a theme file
|
|
186
|
+
|
|
187
|
+
Create a CSS file (e.g. `markdown-theme.css`) that uses descendant selectors scoped to your theme class. With Tailwind, use `@apply` for utility-based styling:
|
|
188
|
+
|
|
189
|
+
```css
|
|
190
|
+
/* markdown-theme.css */
|
|
191
|
+
|
|
192
|
+
.my-theme h1 {
|
|
193
|
+
@apply text-3xl font-bold mt-8 mb-4 text-gray-900;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.my-theme h2 {
|
|
197
|
+
@apply text-2xl font-semibold mt-6 mb-3 text-gray-800;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.my-theme p {
|
|
201
|
+
@apply text-base leading-7 mb-4 text-gray-700;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.my-theme code {
|
|
205
|
+
@apply bg-gray-100 text-sm px-1.5 py-0.5 rounded font-mono;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.my-theme pre {
|
|
209
|
+
@apply rounded-lg my-4 overflow-x-auto;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.my-theme .md-callout {
|
|
213
|
+
@apply border-l-4 border-blue-400 bg-blue-50 px-4 py-3 my-4 rounded-r-lg;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.my-theme table {
|
|
217
|
+
@apply w-full border-collapse my-4 text-sm;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.my-theme th {
|
|
221
|
+
@apply bg-gray-50 font-semibold text-left px-3 py-2 border border-gray-200;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.my-theme td {
|
|
225
|
+
@apply px-3 py-2 border border-gray-200;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Import the theme file in your app and pass the matching class name:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import "./markdown-theme.css";
|
|
233
|
+
|
|
234
|
+
<MarkdownRenderer markdown={content} className="my-theme" />
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Semantic CSS classes
|
|
238
|
+
|
|
239
|
+
The renderer outputs these classes that you can target in your theme:
|
|
240
|
+
|
|
241
|
+
| Class | Element |
|
|
242
|
+
|-------|---------|
|
|
243
|
+
| `md-callout` | Callout/admonition blocks (`> [!NOTE]`, etc.) |
|
|
244
|
+
| `md-code-block` | Executable code block wrapper |
|
|
245
|
+
| `md-code-block-header` | Header bar above executable code blocks |
|
|
246
|
+
| `md-code-output` | Code execution output area |
|
|
247
|
+
| `md-code-error` | Code execution error output |
|
|
248
|
+
| `md-mermaid` | Mermaid diagram container |
|
|
249
|
+
| `md-run-btn` | "Run" button on executable code blocks |
|
|
250
|
+
|
|
251
|
+
### Works with server-side rendering
|
|
252
|
+
|
|
253
|
+
`renderMarkdownToHtml()` produces the same HTML structure (wrapped in `<div class="prose max-w-none">`), so the same theme CSS applies to both client and server-rendered output. For server rendering, scope your selectors to the `prose` class or add a wrapper with your theme class:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const html = renderMarkdownToHtml(content);
|
|
257
|
+
const themed = `<div class="my-theme">${html}</div>`;
|
|
258
|
+
```
|
|
259
|
+
|
|
133
260
|
## License
|
|
134
261
|
|
|
135
262
|
MIT
|