mdv-live 0.3.3 → 0.3.4
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/package.json +1 -1
- package/src/rendering/marp.js +11 -2
- package/src/rendering/slides.js +152 -0
- package/src/static/index.html +16 -0
package/package.json
CHANGED
package/src/rendering/marp.js
CHANGED
|
@@ -7,12 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
import { Marp } from '@marp-team/marp-core';
|
|
9
9
|
|
|
10
|
-
// Initialize Marp with
|
|
10
|
+
// Initialize Marp with full HTML support
|
|
11
11
|
const marp = new Marp({
|
|
12
12
|
html: true,
|
|
13
|
-
math: true,
|
|
13
|
+
math: true,
|
|
14
|
+
markdown: {
|
|
15
|
+
html: true,
|
|
16
|
+
breaks: false,
|
|
17
|
+
linkify: true,
|
|
18
|
+
}
|
|
14
19
|
});
|
|
15
20
|
|
|
21
|
+
// Disable indented code blocks (4-space indent → code)
|
|
22
|
+
// This allows HTML with indentation to render properly
|
|
23
|
+
marp.markdown.disable('code');
|
|
24
|
+
|
|
16
25
|
/**
|
|
17
26
|
* Render Marp presentation to HTML
|
|
18
27
|
* @param {string} content - Markdown content with Marp frontmatter
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple slide renderer for Marp-compatible markdown
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Split by --- (horizontal rule)
|
|
6
|
+
* - Full HTML support (no escaping)
|
|
7
|
+
* - Tailwind CSS compatible
|
|
8
|
+
* - Frontmatter extraction
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import MarkdownIt from 'markdown-it';
|
|
12
|
+
|
|
13
|
+
// Initialize markdown-it with full HTML support
|
|
14
|
+
const md = new MarkdownIt({
|
|
15
|
+
html: true,
|
|
16
|
+
breaks: false,
|
|
17
|
+
linkify: true,
|
|
18
|
+
typographer: true
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse frontmatter from markdown content
|
|
23
|
+
* @param {string} content - Raw markdown
|
|
24
|
+
* @returns {{ frontmatter: object, body: string }}
|
|
25
|
+
*/
|
|
26
|
+
function parseFrontmatter(content) {
|
|
27
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
28
|
+
if (!match) {
|
|
29
|
+
return { frontmatter: {}, body: content };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const frontmatter = {};
|
|
33
|
+
const yaml = match[1];
|
|
34
|
+
|
|
35
|
+
// Simple YAML parsing (key: value)
|
|
36
|
+
yaml.split('\n').forEach(line => {
|
|
37
|
+
const [key, ...rest] = line.split(':');
|
|
38
|
+
if (key && rest.length) {
|
|
39
|
+
frontmatter[key.trim()] = rest.join(':').trim();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
frontmatter,
|
|
45
|
+
body: content.slice(match[0].length)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if content is a slide presentation (has marp: true or uses ---)
|
|
51
|
+
* @param {string} content - Markdown content
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
export function isSlidePresentation(content) {
|
|
55
|
+
// Check for marp: true in frontmatter
|
|
56
|
+
if (/^---\s*\n[\s\S]*?marp:\s*true[\s\S]*?\n---/.test(content)) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render slide content (handles both HTML blocks and markdown)
|
|
64
|
+
* @param {string} slideContent - Single slide content
|
|
65
|
+
* @returns {string} Rendered HTML
|
|
66
|
+
*/
|
|
67
|
+
function renderSlideContent(slideContent) {
|
|
68
|
+
const trimmed = slideContent.trim();
|
|
69
|
+
|
|
70
|
+
// If it starts with HTML tag, render as-is (minimal processing)
|
|
71
|
+
if (trimmed.startsWith('<')) {
|
|
72
|
+
// Process any markdown within the HTML
|
|
73
|
+
// But preserve the HTML structure
|
|
74
|
+
return trimmed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Otherwise, render as markdown
|
|
78
|
+
return md.render(trimmed);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Extract scripts and styles from the first slide (typically config)
|
|
83
|
+
* @param {string} content - First slide content after frontmatter
|
|
84
|
+
* @returns {{ scripts: string, styles: string, content: string }}
|
|
85
|
+
*/
|
|
86
|
+
function extractConfigFromFirstSlide(content) {
|
|
87
|
+
let scripts = '';
|
|
88
|
+
let styles = '';
|
|
89
|
+
let remaining = content;
|
|
90
|
+
|
|
91
|
+
// Extract <script> tags
|
|
92
|
+
const scriptMatches = content.match(/<script[\s\S]*?<\/script>/gi) || [];
|
|
93
|
+
scriptMatches.forEach(script => {
|
|
94
|
+
scripts += script + '\n';
|
|
95
|
+
remaining = remaining.replace(script, '');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Extract <style> tags
|
|
99
|
+
const styleMatches = content.match(/<style[\s\S]*?<\/style>/gi) || [];
|
|
100
|
+
styleMatches.forEach(style => {
|
|
101
|
+
styles += style + '\n';
|
|
102
|
+
remaining = remaining.replace(style, '');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return { scripts, styles, content: remaining.trim() };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Render markdown slides to HTML
|
|
110
|
+
* @param {string} content - Markdown content with slide separators
|
|
111
|
+
* @returns {{ html: string, slideCount: number, scripts: string, styles: string }}
|
|
112
|
+
*/
|
|
113
|
+
export function renderSlides(content) {
|
|
114
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
115
|
+
|
|
116
|
+
// Split by --- (must be on its own line)
|
|
117
|
+
const rawSlides = body.split(/\n---\s*\n/);
|
|
118
|
+
|
|
119
|
+
// Extract scripts/styles from first "slide" (config area)
|
|
120
|
+
const firstSlide = rawSlides[0] || '';
|
|
121
|
+
const { scripts, styles, content: firstContent } = extractConfigFromFirstSlide(firstSlide);
|
|
122
|
+
|
|
123
|
+
// Build slides array (first slide might be empty after extracting config)
|
|
124
|
+
const slidesContent = [firstContent, ...rawSlides.slice(1)].filter(s => s.trim());
|
|
125
|
+
|
|
126
|
+
// Render each slide
|
|
127
|
+
const slides = slidesContent.map((slideContent, index) => {
|
|
128
|
+
const rendered = renderSlideContent(slideContent);
|
|
129
|
+
return `
|
|
130
|
+
<section class="slide" data-slide-index="${index}">
|
|
131
|
+
${rendered}
|
|
132
|
+
</section>
|
|
133
|
+
`;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Wrap in container
|
|
137
|
+
const html = `
|
|
138
|
+
<div class="slides-container" data-slide-count="${slides.length}">
|
|
139
|
+
${slides.join('\n')}
|
|
140
|
+
</div>
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
html,
|
|
145
|
+
slideCount: slides.length,
|
|
146
|
+
scripts,
|
|
147
|
+
styles,
|
|
148
|
+
frontmatter
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default { renderSlides, isSlidePresentation };
|
package/src/static/index.html
CHANGED
|
@@ -28,6 +28,22 @@
|
|
|
28
28
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
29
29
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
30
30
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
|
|
31
|
+
|
|
32
|
+
<!-- Tailwind CSS (for Marp slides) -->
|
|
33
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
34
|
+
<script>
|
|
35
|
+
tailwind.config = {
|
|
36
|
+
corePlugins: { preflight: false, container: false },
|
|
37
|
+
theme: {
|
|
38
|
+
extend: {
|
|
39
|
+
colors: {
|
|
40
|
+
navy: { DEFAULT: '#1B4565', light: '#2d5a7b', dark: '#0f2d44' },
|
|
41
|
+
teal: { DEFAULT: '#3E9BA4', light: '#5bb5bd' },
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
31
47
|
</head>
|
|
32
48
|
<body>
|
|
33
49
|
<script>document.body.dataset.theme = __mdvTheme;</script>
|