marp-dev-preview 0.0.2 → 0.0.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/README.md +23 -7
- package/{marp-preview.mjs → marp-dev-preview.mjs} +71 -13
- package/package.json +15 -11
package/README.md
CHANGED
|
@@ -10,10 +10,18 @@ The tool is mainly intended for slide deck authors who want to preview their sli
|
|
|
10
10
|
* Automatic browser reload on file changes.
|
|
11
11
|
* Custom theme support.
|
|
12
12
|
* Keyboard navigation for slides.
|
|
13
|
-
* Also installs and uses the following markdown-it plugins:
|
|
13
|
+
* Also installs and uses the following markdown-it plugins (it's easy to add more if needed):
|
|
14
14
|
* `markdown-it-container`
|
|
15
15
|
* `markdown-it-mark`
|
|
16
16
|
* `markdown-it-footnote`
|
|
17
|
+
|
|
18
|
+
## Usage via npx
|
|
19
|
+
|
|
20
|
+
The simplest way to start the previewer is via npx:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx marp-dev-preview --theme-dir <dir containing your themes> <your presentation>.md
|
|
24
|
+
```
|
|
17
25
|
|
|
18
26
|
## Installation
|
|
19
27
|
|
|
@@ -31,16 +39,16 @@ npm install marp-dev-preview
|
|
|
31
39
|
|
|
32
40
|
## Usage
|
|
33
41
|
|
|
34
|
-
To start the preview server, run the `
|
|
42
|
+
To start the preview server, run the `mdp` command followed by your markdown file path:
|
|
35
43
|
|
|
36
44
|
```bash
|
|
37
|
-
|
|
45
|
+
mdp <path-to-your-markdown-file> [options]
|
|
38
46
|
```
|
|
39
47
|
|
|
40
48
|
**Example:**
|
|
41
49
|
|
|
42
50
|
```bash
|
|
43
|
-
|
|
51
|
+
mdp my-slides/presentation.md --port 3000 --theme-dir my-themes
|
|
44
52
|
```
|
|
45
53
|
|
|
46
54
|
### Options
|
|
@@ -57,13 +65,21 @@ While viewing the presentation in your browser, in addition to the usual browser
|
|
|
57
65
|
* <kbd>:<number></kbd>: Go to the specified slide number.
|
|
58
66
|
* <kbd>?</kbd>: Toggle the help box displaying key bindings.
|
|
59
67
|
|
|
60
|
-
##
|
|
68
|
+
## Integration with other tools
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
The tool can respond to http requests to change slides and to scroll to a slide containing a given text. Any http client can be used to send such requests, e.g.:
|
|
63
71
|
|
|
64
72
|
```bash
|
|
65
|
-
|
|
73
|
+
curl -X POST -H "Content-Type: application/json" -d '{"command": "find", "string": "my awesome text"}' http://localhost:8080/api/command
|
|
66
74
|
```
|
|
75
|
+
would scroll to the first slide containing "my awesome text".
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
curl -X POST -H "Content-Type: application/json" -d '{"command": "goto", "slide": 3}' http://localhost:8080/api/command
|
|
79
|
+
````
|
|
80
|
+
|
|
81
|
+
would go to slide 3.
|
|
82
|
+
|
|
67
83
|
|
|
68
84
|
## License
|
|
69
85
|
|
|
@@ -7,6 +7,9 @@ import chokidar from 'chokidar';
|
|
|
7
7
|
import { WebSocketServer } from 'ws';
|
|
8
8
|
import yargs from 'yargs';
|
|
9
9
|
import { hideBin } from 'yargs/helpers';
|
|
10
|
+
import markdownItFootnote from 'markdown-it-footnote';
|
|
11
|
+
import markdownItMark from 'markdown-it-mark';
|
|
12
|
+
import markdownItContainer from 'markdown-it-container';
|
|
10
13
|
|
|
11
14
|
const argv = yargs(hideBin(process.argv))
|
|
12
15
|
.usage('Usage: $0 <markdown-file> [options]')
|
|
@@ -65,7 +68,11 @@ let marp;
|
|
|
65
68
|
|
|
66
69
|
async function initializeMarp() {
|
|
67
70
|
const options = { html: true, linkify: true, };
|
|
68
|
-
marp = new Marp(options)
|
|
71
|
+
marp = new Marp(options)
|
|
72
|
+
.use(markdownItFootnote)
|
|
73
|
+
.use(markdownItMark)
|
|
74
|
+
.use(markdownItContainer, 'note');
|
|
75
|
+
|
|
69
76
|
if (themeDir) {
|
|
70
77
|
const themeFiles = await fs.readdir(themeDir);
|
|
71
78
|
for (const file of themeFiles) {
|
|
@@ -130,21 +137,55 @@ async function renderMarp() {
|
|
|
130
137
|
</style>
|
|
131
138
|
<script>
|
|
132
139
|
const ws = new WebSocket('ws://localhost:${port + 1}');
|
|
133
|
-
ws.onmessage = (event) => {
|
|
134
|
-
if (event.data === 'reload') {
|
|
135
|
-
window.location.reload();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
let lastKey = '';
|
|
140
|
-
let command = '';
|
|
141
|
-
let commandMode = false;
|
|
142
140
|
|
|
143
141
|
document.addEventListener('DOMContentLoaded', () => {
|
|
144
142
|
const slides = Array.from(document.querySelectorAll('section[id]'));
|
|
145
143
|
const commandPrompt = document.getElementById('command-prompt');
|
|
146
144
|
const helpBox = document.getElementById('help-box');
|
|
147
145
|
|
|
146
|
+
let lastKey = '';
|
|
147
|
+
let command = '';
|
|
148
|
+
let commandMode = false;
|
|
149
|
+
|
|
150
|
+
function goToSlide(slideNumber) {
|
|
151
|
+
if (!isNaN(slideNumber) && slideNumber > 0 && slideNumber <= slides.length) {
|
|
152
|
+
slides[slideNumber - 1].scrollIntoView({ behavior: 'smooth' });
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function findSlideByString(string) {
|
|
159
|
+
const lowerString = string.toLowerCase();
|
|
160
|
+
let found = false;
|
|
161
|
+
for (let i = 0; i < slides.length; i++) {
|
|
162
|
+
if (slides[i].textContent.toLowerCase().includes(lowerString)) {
|
|
163
|
+
slides[i].scrollIntoView({ behavior: 'smooth' });
|
|
164
|
+
found = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!found) {
|
|
168
|
+
console.error('No slide contains the string: ' + string);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
ws.onmessage = (event) => {
|
|
173
|
+
if (event.data === 'reload') {
|
|
174
|
+
window.location.reload();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const data = JSON.parse(event.data);
|
|
179
|
+
if (data.command === 'goto' && data.slide) {
|
|
180
|
+
goToSlide(parseInt(data.slide, 10));
|
|
181
|
+
} else if (data.command === 'find' && data.string) {
|
|
182
|
+
findSlideByString(data.string);
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.error('Failed to parse WebSocket message:', e);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
148
189
|
function updatePrompt(text, isError = false) {
|
|
149
190
|
if (commandMode) {
|
|
150
191
|
commandPrompt.style.display = 'block';
|
|
@@ -160,11 +201,10 @@ async function renderMarp() {
|
|
|
160
201
|
if (commandMode) {
|
|
161
202
|
if (e.key === 'Enter') {
|
|
162
203
|
const slideNumber = parseInt(command, 10);
|
|
163
|
-
if (
|
|
164
|
-
slides[slideNumber - 1].scrollIntoView({ behavior: 'smooth' });
|
|
204
|
+
if (goToSlide(slideNumber)) {
|
|
165
205
|
commandMode = false;
|
|
166
206
|
command = '';
|
|
167
|
-
updatePrompt(':' + command);
|
|
207
|
+
updatePrompt(':' + command);
|
|
168
208
|
} else {
|
|
169
209
|
updatePrompt(\`Error: Slide not found.\`, true); // Pass message and error flag
|
|
170
210
|
setTimeout(() => {
|
|
@@ -243,6 +283,24 @@ const server = http.createServer(async (req, res) => {
|
|
|
243
283
|
const html = await renderMarp();
|
|
244
284
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
245
285
|
res.end(html);
|
|
286
|
+
} else if (req.url === '/api/command' && req.method === 'POST') {
|
|
287
|
+
let body = '';
|
|
288
|
+
req.on('data', chunk => {
|
|
289
|
+
body += chunk.toString();
|
|
290
|
+
});
|
|
291
|
+
req.on('end', () => {
|
|
292
|
+
try {
|
|
293
|
+
const command = JSON.parse(body);
|
|
294
|
+
for (const ws of wss.clients) {
|
|
295
|
+
ws.send(JSON.stringify(command));
|
|
296
|
+
}
|
|
297
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
298
|
+
res.end(JSON.stringify({ status: 'ok', command }));
|
|
299
|
+
} catch (e) {
|
|
300
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
301
|
+
res.end(JSON.stringify({ status: 'error', message: 'Invalid JSON' }));
|
|
302
|
+
}
|
|
303
|
+
});
|
|
246
304
|
} else {
|
|
247
305
|
const assetPath = path.join(markdownDir, req.url);
|
|
248
306
|
const ext = path.extname(assetPath);
|
package/package.json
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "marp-dev-preview",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "A CLI tool to preview Marp markdown files.",
|
|
5
|
-
"main": "marp-preview.mjs",
|
|
5
|
+
"main": "marp-dev-preview.mjs",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
|
|
8
|
+
"mdp": "marp-dev-preview.mjs"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
|
|
12
|
-
"preview": "node marp-preview.mjs"
|
|
11
|
+
"start": "node marp-dev-preview.mjs"
|
|
13
12
|
},
|
|
14
13
|
"keywords": [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
"marp",
|
|
15
|
+
"markdown",
|
|
16
|
+
"preview",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
20
19
|
"author": "Roberto Esposito",
|
|
21
|
-
"
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+ssh://git@github.com/boborbt/marp-dev-preview.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": "https://github.com/boborbt/marp-dev-preview/issues",
|
|
22
26
|
"dependencies": {
|
|
23
27
|
"@marp-team/marp-core": "^4.1.0",
|
|
24
28
|
"chokidar": "^4.0.3",
|