mdv-live 0.3.3 → 0.3.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdv-live",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Markdown Viewer - File tree + Live preview + Marp support + Hot reload",
5
5
  "main": "src/server.js",
6
6
  "bin": {
package/src/api/tree.js CHANGED
@@ -7,7 +7,7 @@ import path from 'path';
7
7
  import { getFileType } from '../utils/fileTypes.js';
8
8
  import { getRelativePath, validatePath } from '../utils/path.js';
9
9
 
10
- const IGNORED_PATTERNS = new Set(['node_modules', '__pycache__']);
10
+ const IGNORED_PATTERNS = new Set(['node_modules', '__pycache__', '.git']);
11
11
  const MAX_INITIAL_DEPTH = 1;
12
12
 
13
13
  /**
@@ -16,7 +16,7 @@ const MAX_INITIAL_DEPTH = 1;
16
16
  * @returns {boolean} True if should be ignored
17
17
  */
18
18
  function shouldIgnore(name) {
19
- return name.startsWith('.') || IGNORED_PATTERNS.has(name);
19
+ return IGNORED_PATTERNS.has(name);
20
20
  }
21
21
 
22
22
  /**
@@ -7,12 +7,21 @@
7
7
 
8
8
  import { Marp } from '@marp-team/marp-core';
9
9
 
10
- // Initialize Marp with options
10
+ // Initialize Marp with full HTML support
11
11
  const marp = new Marp({
12
12
  html: true,
13
- math: true, // Enable KaTeX math
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 };
@@ -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>