marp-dev-preview 0.1.5 → 0.1.7

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 CHANGED
@@ -84,35 +84,63 @@ In addition to normal browser navigation keys (`Page Up`, `Page Down`, `Home`, `
84
84
 
85
85
  ---
86
86
 
87
- ## 🔗 Integration with Other Tools
88
-
89
- The preview server exposes a simple **HTTP API** for controlling slides.
90
-
91
- - Reload the document (the server will parse the received markdown content and incrementally update the presentation).
92
-
93
- ```bash
94
- curl -X POST \
95
- -H "Content-Type: text/markdown" \
96
- -d "$(cat file-with-updated-content.md)" \
97
- http://localhost:8080/api/reload
98
- ```
99
-
100
- - Go to the first slide containing `"my awesome text"`:
101
- ```bash
102
- curl -X POST \
103
- -H "Content-Type: application/json" \
104
- -d '{"command": "find", "string": "my awesome text"}' \
105
- http://localhost:8080/api/command
106
- ```
107
-
108
- - Jump directly to slide 3:
109
-
110
- ```bash
111
- curl -X POST
112
- -H "Content-Type: application/json" \
113
- -d '{"command": "goto", "slide": 3}' \
114
- http://localhost:8080/api/command
115
- ```
87
+ ## 🔗 API Endpoints
88
+
89
+ The preview server exposes a simple **HTTP API** for controlling slides and content.
90
+
91
+ ### `POST /api/reload`
92
+
93
+ Reloads the presentation with new markdown content. The server parses the received markdown, renders it into HTML, and broadcasts the changes to all connected clients. This is ideal for tools that need to push updates without requiring a full page refresh.
94
+
95
+ - **Request**:
96
+ - **Headers**: `Content-Type: text/markdown`
97
+ - **Body**: Raw markdown content.
98
+
99
+ - **Response**:
100
+ - `200 OK`: `{ "status": "ok" }` on success.
101
+ - `500 Internal Server Error`: `{ "status": "error", "message": "..." }` on failure.
102
+
103
+ - **Example**:
104
+ ```bash
105
+ curl -X POST \
106
+ -H "Content-Type: text/markdown" \
107
+ -d "$(cat path/to/your/slides.md)" \
108
+ http://localhost:8080/api/reload
109
+ ```
110
+
111
+ ### `POST /api/command`
112
+
113
+ Sends a command to the browser to control the presentation's navigation. This allows external tools to programmatically change the visible slide.
114
+
115
+ - **Request**:
116
+ - **Headers**: `Content-Type: application/json`
117
+ - **Body**: A JSON object describing the command.
118
+
119
+ - **Response**:
120
+ - `200 OK`: `{ "status": "ok", "command": { ... } }` on success.
121
+ - `400 Bad Request`: `{ "status": "error", "message": "Invalid JSON" }` if the body is malformed.
122
+
123
+ #### Supported Commands
124
+
125
+ 1. **`goto`**: Jumps to a specific slide number.
126
+ - **Payload**: `{ "command": "goto", "slide": <number> }`
127
+ - **Example**:
128
+ ```bash
129
+ curl -X POST \
130
+ -H "Content-Type: application/json" \
131
+ -d '{"command": "goto", "slide": 5}' \
132
+ http://localhost:8080/api/command
133
+ ```
134
+
135
+ 2. **`find`**: Searches for a string and jumps to the first slide containing it. The search is case-insensitive.
136
+ - **Payload**: `{ "command": "find", "string": "<search-term>" }`
137
+ - **Example**:
138
+ ```bash
139
+ curl -X POST \
140
+ -H "Content-Type: application/json" \
141
+ -d '{"command": "find", "string": "My Awesome Slide"}' \
142
+ http://localhost:8080/api/command
143
+ ```
116
144
 
117
145
 
118
146
  ---
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "marp-dev-preview",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A CLI tool to preview Marp markdown files.",
5
- "main": "marp-dev-preview.mjs",
5
+ "main": "src/marp-dev-preview.mjs",
6
6
  "type": "module",
7
7
  "bin": {
8
- "mdp": "marp-dev-preview.mjs"
8
+ "mdp": "src/marp-dev-preview.mjs"
9
9
  },
10
10
  "scripts": {
11
- "start": "node marp-dev-preview.mjs",
11
+ "start": "node src/marp-dev-preview.mjs",
12
12
  "test": "jest"
13
13
  },
14
14
  "keywords": [
@@ -53,6 +53,11 @@ document.addEventListener('DOMContentLoaded', () => {
53
53
  try {
54
54
  const data = JSON.parse(event.data);
55
55
  if (data.type === 'update') {
56
+ // TODO: support force rebuild
57
+ // if(data.rebuild == true) {
58
+ // window.location.reload();
59
+ // return;
60
+ // }
56
61
  const marpContainer = document.getElementById('marp-container');
57
62
  if (marpContainer) {
58
63
  morphdom(marpContainer, `<div id="marp-container">${data.html}</div>`);
@@ -65,6 +70,8 @@ document.addEventListener('DOMContentLoaded', () => {
65
70
  goToSlide(parseInt(data.slide, 10));
66
71
  } else if (data.command === 'find' && data.string) {
67
72
  findSlideByString(data.string);
73
+ } else if (data.command === 'close_preview') {
74
+ window.close();
68
75
  }
69
76
  } catch (e) {
70
77
  console.error('Failed to parse WebSocket message:', e);
@@ -21,7 +21,7 @@ const port = argv.port;
21
21
  const verbose = argv.verbose;
22
22
 
23
23
  if (argv.version) {
24
- const pkg = JSON.parse(await fs.readFile(path.join(__dirname, 'package.json'), 'utf8'));
24
+ const pkg = JSON.parse(await fs.readFile(path.join(__dirname, '..', 'package.json'), 'utf8'));
25
25
  console.log(`marp-dev-preview version ${pkg.version}`);
26
26
  process.exit(0);
27
27
  }
@@ -149,9 +149,6 @@ chokidar.watch(markdownFile).on('change', async () => {
149
149
 
150
150
  initializeMarp(themeDir).then(() => {
151
151
  createServer(port, markdownFile, markdownDir, renderMarp, reload, wss, __dirname);
152
- if (themeDir) {
153
- console.log(`Using custom themes from ${themeDir}`);
154
- }
155
152
  }).catch(error => {
156
153
  console.error("Failed to initialize Marp:", error);
157
154
  process.exit(1);
@@ -15,15 +15,31 @@ export async function initializeMarp(themeDir) {
15
15
  .use(markdownItMark)
16
16
  .use(markdownItContainer, 'note');
17
17
 
18
- if (themeDir) {
19
- const themeFiles = await fs.readdir(themeDir);
20
- for (const file of themeFiles) {
21
- if (path.extname(file) === '.css') {
22
- const css = await fs.readFile(path.join(themeDir, file), 'utf8');
23
- marp.themeSet.add(css);
24
- }
18
+ if (!themeDir) {
19
+ return marp;
20
+ }
21
+
22
+ let stats = await fs.stat(themeDir).catch(() => null);
23
+ if (!stats) {
24
+ console.warn(`Theme directory "${themeDir}" does not exist.`);
25
+ return marp;
26
+ }
27
+
28
+ if(!stats.isDirectory()) {
29
+ console.warn(`Theme directory "${themeDir}" is not a directory.`);
30
+ return marp;
31
+ }
32
+
33
+ console.log("Loading themes from:", themeDir);
34
+
35
+ const themeFiles = await fs.readdir(themeDir);
36
+ for (const file of themeFiles) {
37
+ if (path.extname(file) === '.css') {
38
+ const css = await fs.readFile(path.join(themeDir, file), 'utf8');
39
+ marp.themeSet.add(css);
25
40
  }
26
41
  }
42
+
27
43
  return marp;
28
44
  }
29
45
 
@@ -1,4 +1,4 @@
1
- import { parseArgs } from './args.mjs';
1
+ import { parseArgs } from '../src/args.mjs';
2
2
 
3
3
  const mockArgv = {};
4
4
  const defaultValues = {};
@@ -1,4 +1,4 @@
1
- import { createServer } from './server.mjs';
1
+ import { createServer } from '../src/server.mjs';
2
2
  import http from 'http';
3
3
 
4
4
  jest.mock('http', () => ({
@@ -1,4 +1,4 @@
1
- import { initializeMarp, renderMarp } from './marp-utils.mjs';
1
+ import { initializeMarp, renderMarp } from '../src/marp-utils.mjs';
2
2
  import { Marp } from '@marp-team/marp-core';
3
3
 
4
4
  jest.mock('@marp-team/marp-core', () => ({
File without changes
File without changes