@affanhamid/markdown-renderer 2.0.1 → 2.2.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/dist/index.cjs +67 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +67 -1
- package/package.json +10 -3
package/dist/index.cjs
CHANGED
|
@@ -820,6 +820,13 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
820
820
|
const isExecutable = options?.executableLanguages && language && options.executableLanguages.includes(language.toLowerCase());
|
|
821
821
|
const currentIndex = codeBlockIndex;
|
|
822
822
|
codeBlockIndex++;
|
|
823
|
+
if (language === "mermaid") {
|
|
824
|
+
parts.push(
|
|
825
|
+
`<div class="md-mermaid" data-mermaid-code="${escapeHtml(codeContent)}"><pre style="overflow-x:auto;background:#f7f7f7;padding:0.75rem;border-radius:0.375rem;font-size:0.8rem;color:#666"><code>${escapedCode}</code></pre></div>`
|
|
826
|
+
);
|
|
827
|
+
i++;
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
823
830
|
if (isExecutable) {
|
|
824
831
|
parts.push(
|
|
825
832
|
`<div class="md-code-block" data-language="${escapedLang}" data-code-index="${currentIndex}" data-executable="true"><div class="md-code-block-header" style="display:flex;align-items:center;justify-content:space-between;padding:0.25rem 0.75rem;background:#f0f0f0;border-radius:0.375rem 0.375rem 0 0;border:1px solid #e0e0e0;border-bottom:none"><span style="font-size:0.75rem;color:#666;font-family:monospace">${escapedLang}</span><button class="md-run-btn" data-code-index="${currentIndex}" style="padding:0.2rem 0.6rem;font-size:0.75rem;border-radius:0.25rem;border:1px solid #ccc;background:#fff;cursor:pointer;font-family:inherit">Run</button></div><pre style="overflow-x:auto;border-radius:0 0 0.375rem 0.375rem;background:#f7f7f7;color:#1f2937;padding:0.75rem;font-size:0.875rem;margin:0;border:1px solid #e0e0e0;border-top:none"><code class="language-${escapedLang}" data-executable="true">${escapedCode}</code></pre><div class="md-code-output" data-output-for="${currentIndex}" style="display:none"></div></div>`
|
|
@@ -847,6 +854,28 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
847
854
|
i++;
|
|
848
855
|
continue;
|
|
849
856
|
}
|
|
857
|
+
const calloutMatch = trimmed.match(/^\\begin\{callout\}\{(\w+)\}$/);
|
|
858
|
+
if (calloutMatch && calloutMatch[1]) {
|
|
859
|
+
const color = calloutMatch[1];
|
|
860
|
+
const contentLines = [];
|
|
861
|
+
i++;
|
|
862
|
+
while (i < lines.length) {
|
|
863
|
+
const calloutLine = (lines[i] || "").trim();
|
|
864
|
+
if (calloutLine === "\\end{callout}") {
|
|
865
|
+
i++;
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
contentLines.push(lines[i] || "");
|
|
869
|
+
i++;
|
|
870
|
+
}
|
|
871
|
+
const innerHtml = renderMarkdownToHtml(contentLines.join("\n"), options);
|
|
872
|
+
const innerMatch = innerHtml.match(/<div class="prose[^"]*">(.*)<\/div>/s);
|
|
873
|
+
const innerContent = innerMatch?.[1] ?? escapeHtml(contentLines.join("\n"));
|
|
874
|
+
parts.push(
|
|
875
|
+
`<div class="md-callout border-${color}-200 bg-${color}-50 text-${color}-900 dark:border-${color}-700/40 dark:bg-${color}-900/10 dark:text-${color}-200 my-4 rounded-lg border px-4 py-3 text-sm leading-relaxed [&>p]:mb-0 [&>p:last-child]:mb-0">${innerContent}</div>`
|
|
876
|
+
);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
850
879
|
const imageMatch = trimmed.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
851
880
|
if (imageMatch && imageMatch[2]) {
|
|
852
881
|
const alt = escapeHtml(imageMatch[1] ?? "");
|
|
@@ -893,6 +922,7 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
893
922
|
}
|
|
894
923
|
var MarkdownRenderer = ({
|
|
895
924
|
markdown,
|
|
925
|
+
className,
|
|
896
926
|
onRunCode,
|
|
897
927
|
executableLanguages = ["python", "r"]
|
|
898
928
|
}) => {
|
|
@@ -1038,7 +1068,43 @@ var MarkdownRenderer = ({
|
|
|
1038
1068
|
);
|
|
1039
1069
|
};
|
|
1040
1070
|
}, [html, handleRun]);
|
|
1041
|
-
|
|
1071
|
+
(0, import_react.useEffect)(() => {
|
|
1072
|
+
const container = containerRef.current;
|
|
1073
|
+
if (!container) return;
|
|
1074
|
+
const mermaidBlocks = container.querySelectorAll(".md-mermaid");
|
|
1075
|
+
if (mermaidBlocks.length === 0) return;
|
|
1076
|
+
let alive = true;
|
|
1077
|
+
void (async () => {
|
|
1078
|
+
try {
|
|
1079
|
+
const mermaid = (await import("mermaid")).default;
|
|
1080
|
+
mermaid.initialize({
|
|
1081
|
+
startOnLoad: false,
|
|
1082
|
+
securityLevel: "antiscript",
|
|
1083
|
+
theme: "neutral",
|
|
1084
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
1085
|
+
fontSize: 13,
|
|
1086
|
+
htmlLabels: false,
|
|
1087
|
+
flowchart: { useMaxWidth: true, htmlLabels: false },
|
|
1088
|
+
sequence: { useMaxWidth: true }
|
|
1089
|
+
});
|
|
1090
|
+
for (const block of Array.from(mermaidBlocks)) {
|
|
1091
|
+
if (!alive) break;
|
|
1092
|
+
const code = block.getAttribute("data-mermaid-code") || "";
|
|
1093
|
+
const id = `mermaid-${Math.random().toString(36).slice(2, 9)}`;
|
|
1094
|
+
try {
|
|
1095
|
+
const { svg } = await mermaid.render(id, code.trim());
|
|
1096
|
+
block.innerHTML = `<div style="display:flex;justify-content:center;overflow:auto;padding:1rem">${svg}</div>`;
|
|
1097
|
+
} catch {
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
} catch {
|
|
1101
|
+
}
|
|
1102
|
+
})();
|
|
1103
|
+
return () => {
|
|
1104
|
+
alive = false;
|
|
1105
|
+
};
|
|
1106
|
+
}, [html]);
|
|
1107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className, dangerouslySetInnerHTML: { __html: html } });
|
|
1042
1108
|
};
|
|
1043
1109
|
var markdown_renderer_default = MarkdownRenderer;
|
|
1044
1110
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -7,13 +7,14 @@ interface CodeExecutionResult {
|
|
|
7
7
|
}
|
|
8
8
|
interface MarkdownRendererProps {
|
|
9
9
|
markdown: string;
|
|
10
|
+
className?: string;
|
|
10
11
|
onRunCode?: (code: string, language: string) => Promise<CodeExecutionResult>;
|
|
11
12
|
executableLanguages?: string[];
|
|
12
13
|
}
|
|
13
14
|
declare function renderMarkdownToHtml(markdown: string, options?: {
|
|
14
15
|
executableLanguages?: string[];
|
|
15
16
|
}): string;
|
|
16
|
-
declare const MarkdownRenderer: ({ markdown, onRunCode, executableLanguages, }: MarkdownRendererProps) => react_jsx_runtime.JSX.Element;
|
|
17
|
+
declare const MarkdownRenderer: ({ markdown, className, onRunCode, executableLanguages, }: MarkdownRendererProps) => react_jsx_runtime.JSX.Element;
|
|
17
18
|
|
|
18
19
|
declare function normalizeMathMarkdownDelimiters(markdown: string): string;
|
|
19
20
|
declare const MATH_MARKDOWN_RULES_APPENDIX = "Math formatting rules (must follow):\n- Inline math: use single-dollar delimiters like $...$.\n- Display math: use $$ delimiters on their own lines with nothing else on those lines.\n- Do not use \\(...\\) or \\[...\\] delimiters.\n- Do not place display-math $$...$$ inside bullets or table cells; use inline $...$ there.\n- Escape non-math currency dollars as \\$.";
|
package/dist/index.d.ts
CHANGED
|
@@ -7,13 +7,14 @@ interface CodeExecutionResult {
|
|
|
7
7
|
}
|
|
8
8
|
interface MarkdownRendererProps {
|
|
9
9
|
markdown: string;
|
|
10
|
+
className?: string;
|
|
10
11
|
onRunCode?: (code: string, language: string) => Promise<CodeExecutionResult>;
|
|
11
12
|
executableLanguages?: string[];
|
|
12
13
|
}
|
|
13
14
|
declare function renderMarkdownToHtml(markdown: string, options?: {
|
|
14
15
|
executableLanguages?: string[];
|
|
15
16
|
}): string;
|
|
16
|
-
declare const MarkdownRenderer: ({ markdown, onRunCode, executableLanguages, }: MarkdownRendererProps) => react_jsx_runtime.JSX.Element;
|
|
17
|
+
declare const MarkdownRenderer: ({ markdown, className, onRunCode, executableLanguages, }: MarkdownRendererProps) => react_jsx_runtime.JSX.Element;
|
|
17
18
|
|
|
18
19
|
declare function normalizeMathMarkdownDelimiters(markdown: string): string;
|
|
19
20
|
declare const MATH_MARKDOWN_RULES_APPENDIX = "Math formatting rules (must follow):\n- Inline math: use single-dollar delimiters like $...$.\n- Display math: use $$ delimiters on their own lines with nothing else on those lines.\n- Do not use \\(...\\) or \\[...\\] delimiters.\n- Do not place display-math $$...$$ inside bullets or table cells; use inline $...$ there.\n- Escape non-math currency dollars as \\$.";
|
package/dist/index.js
CHANGED
|
@@ -781,6 +781,13 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
781
781
|
const isExecutable = options?.executableLanguages && language && options.executableLanguages.includes(language.toLowerCase());
|
|
782
782
|
const currentIndex = codeBlockIndex;
|
|
783
783
|
codeBlockIndex++;
|
|
784
|
+
if (language === "mermaid") {
|
|
785
|
+
parts.push(
|
|
786
|
+
`<div class="md-mermaid" data-mermaid-code="${escapeHtml(codeContent)}"><pre style="overflow-x:auto;background:#f7f7f7;padding:0.75rem;border-radius:0.375rem;font-size:0.8rem;color:#666"><code>${escapedCode}</code></pre></div>`
|
|
787
|
+
);
|
|
788
|
+
i++;
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
784
791
|
if (isExecutable) {
|
|
785
792
|
parts.push(
|
|
786
793
|
`<div class="md-code-block" data-language="${escapedLang}" data-code-index="${currentIndex}" data-executable="true"><div class="md-code-block-header" style="display:flex;align-items:center;justify-content:space-between;padding:0.25rem 0.75rem;background:#f0f0f0;border-radius:0.375rem 0.375rem 0 0;border:1px solid #e0e0e0;border-bottom:none"><span style="font-size:0.75rem;color:#666;font-family:monospace">${escapedLang}</span><button class="md-run-btn" data-code-index="${currentIndex}" style="padding:0.2rem 0.6rem;font-size:0.75rem;border-radius:0.25rem;border:1px solid #ccc;background:#fff;cursor:pointer;font-family:inherit">Run</button></div><pre style="overflow-x:auto;border-radius:0 0 0.375rem 0.375rem;background:#f7f7f7;color:#1f2937;padding:0.75rem;font-size:0.875rem;margin:0;border:1px solid #e0e0e0;border-top:none"><code class="language-${escapedLang}" data-executable="true">${escapedCode}</code></pre><div class="md-code-output" data-output-for="${currentIndex}" style="display:none"></div></div>`
|
|
@@ -808,6 +815,28 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
808
815
|
i++;
|
|
809
816
|
continue;
|
|
810
817
|
}
|
|
818
|
+
const calloutMatch = trimmed.match(/^\\begin\{callout\}\{(\w+)\}$/);
|
|
819
|
+
if (calloutMatch && calloutMatch[1]) {
|
|
820
|
+
const color = calloutMatch[1];
|
|
821
|
+
const contentLines = [];
|
|
822
|
+
i++;
|
|
823
|
+
while (i < lines.length) {
|
|
824
|
+
const calloutLine = (lines[i] || "").trim();
|
|
825
|
+
if (calloutLine === "\\end{callout}") {
|
|
826
|
+
i++;
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
contentLines.push(lines[i] || "");
|
|
830
|
+
i++;
|
|
831
|
+
}
|
|
832
|
+
const innerHtml = renderMarkdownToHtml(contentLines.join("\n"), options);
|
|
833
|
+
const innerMatch = innerHtml.match(/<div class="prose[^"]*">(.*)<\/div>/s);
|
|
834
|
+
const innerContent = innerMatch?.[1] ?? escapeHtml(contentLines.join("\n"));
|
|
835
|
+
parts.push(
|
|
836
|
+
`<div class="md-callout border-${color}-200 bg-${color}-50 text-${color}-900 dark:border-${color}-700/40 dark:bg-${color}-900/10 dark:text-${color}-200 my-4 rounded-lg border px-4 py-3 text-sm leading-relaxed [&>p]:mb-0 [&>p:last-child]:mb-0">${innerContent}</div>`
|
|
837
|
+
);
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
811
840
|
const imageMatch = trimmed.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
812
841
|
if (imageMatch && imageMatch[2]) {
|
|
813
842
|
const alt = escapeHtml(imageMatch[1] ?? "");
|
|
@@ -854,6 +883,7 @@ function renderMarkdownToHtml(markdown, options) {
|
|
|
854
883
|
}
|
|
855
884
|
var MarkdownRenderer = ({
|
|
856
885
|
markdown,
|
|
886
|
+
className,
|
|
857
887
|
onRunCode,
|
|
858
888
|
executableLanguages = ["python", "r"]
|
|
859
889
|
}) => {
|
|
@@ -999,7 +1029,43 @@ var MarkdownRenderer = ({
|
|
|
999
1029
|
);
|
|
1000
1030
|
};
|
|
1001
1031
|
}, [html, handleRun]);
|
|
1002
|
-
|
|
1032
|
+
useEffect(() => {
|
|
1033
|
+
const container = containerRef.current;
|
|
1034
|
+
if (!container) return;
|
|
1035
|
+
const mermaidBlocks = container.querySelectorAll(".md-mermaid");
|
|
1036
|
+
if (mermaidBlocks.length === 0) return;
|
|
1037
|
+
let alive = true;
|
|
1038
|
+
void (async () => {
|
|
1039
|
+
try {
|
|
1040
|
+
const mermaid = (await import("mermaid")).default;
|
|
1041
|
+
mermaid.initialize({
|
|
1042
|
+
startOnLoad: false,
|
|
1043
|
+
securityLevel: "antiscript",
|
|
1044
|
+
theme: "neutral",
|
|
1045
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
1046
|
+
fontSize: 13,
|
|
1047
|
+
htmlLabels: false,
|
|
1048
|
+
flowchart: { useMaxWidth: true, htmlLabels: false },
|
|
1049
|
+
sequence: { useMaxWidth: true }
|
|
1050
|
+
});
|
|
1051
|
+
for (const block of Array.from(mermaidBlocks)) {
|
|
1052
|
+
if (!alive) break;
|
|
1053
|
+
const code = block.getAttribute("data-mermaid-code") || "";
|
|
1054
|
+
const id = `mermaid-${Math.random().toString(36).slice(2, 9)}`;
|
|
1055
|
+
try {
|
|
1056
|
+
const { svg } = await mermaid.render(id, code.trim());
|
|
1057
|
+
block.innerHTML = `<div style="display:flex;justify-content:center;overflow:auto;padding:1rem">${svg}</div>`;
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
} catch {
|
|
1062
|
+
}
|
|
1063
|
+
})();
|
|
1064
|
+
return () => {
|
|
1065
|
+
alive = false;
|
|
1066
|
+
};
|
|
1067
|
+
}, [html]);
|
|
1068
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, className, dangerouslySetInnerHTML: { __html: html } });
|
|
1003
1069
|
};
|
|
1004
1070
|
var markdown_renderer_default = MarkdownRenderer;
|
|
1005
1071
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@affanhamid/markdown-renderer",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Custom markdown renderer with KaTeX support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --external react",
|
|
17
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --external react --external mermaid",
|
|
18
18
|
"test": "jest"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
@@ -22,13 +22,20 @@
|
|
|
22
22
|
"shiki": "^4.0.1"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"react": ">=18"
|
|
25
|
+
"react": ">=18",
|
|
26
|
+
"mermaid": ">=10"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"mermaid": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
26
32
|
},
|
|
27
33
|
"devDependencies": {
|
|
28
34
|
"@types/jest": "^30.0.0",
|
|
29
35
|
"@types/katex": "^0.16.7",
|
|
30
36
|
"@types/react": "^19.0.0",
|
|
31
37
|
"jest": "^30.2.0",
|
|
38
|
+
"mermaid": "^11.12.3",
|
|
32
39
|
"ts-jest": "^29.4.6",
|
|
33
40
|
"tsup": "^8.0.0",
|
|
34
41
|
"typescript": "^5.8.0"
|