md2pdf2 0.1.0 → 0.2.1
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 +30 -5
- package/dist/cli.js +30 -9
- package/dist/cli.js.map +1 -1
- package/dist/dev-server.d.ts +8 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +406 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/template-engine.d.ts.map +1 -1
- package/dist/template-engine.js +10 -1
- package/dist/template-engine.js.map +1 -1
- package/package.json +8 -3
- package/templates/minimal.hbs +66 -0
- package/templates/modern.hbs +99 -0
- package/templates/newsletter.hbs +216 -0
- package/templates/parts/footer.hbs +1 -1
- package/templates/professional-report.hbs +261 -0
- package/templates/resume.hbs +211 -0
- package/templates/styles.css +1 -1
package/README.md
CHANGED
|
@@ -17,15 +17,40 @@ A CLI tool that converts Markdown to PDF using customizable templates — think
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
# Install globally (once published)
|
|
20
|
-
npm install -g
|
|
20
|
+
npm install -g md2pdf2
|
|
21
21
|
|
|
22
22
|
# Or use npx
|
|
23
|
-
npx
|
|
23
|
+
npx md2pdf2 convert input.md -o output.pdf
|
|
24
24
|
|
|
25
25
|
# With a custom template
|
|
26
|
-
|
|
26
|
+
md2pdf2 convert input.md --template my-template.hbs -o output.pdf
|
|
27
|
+
|
|
28
|
+
# Dev mode with live preview
|
|
29
|
+
md2pdf2 dev input.md
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Dev Mode
|
|
33
|
+
|
|
34
|
+
Start a live preview server to see your markdown rendered in different templates:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
md2pdf2 dev input.md --port 3456
|
|
27
38
|
```
|
|
28
39
|
|
|
40
|
+
Opens a browser with:
|
|
41
|
+
- **Left pane**: Template selector (switch between templates)
|
|
42
|
+
- **Right pane**: Live preview of your markdown
|
|
43
|
+
|
|
44
|
+
Changes to your `.md` file or templates auto-reload the preview.
|
|
45
|
+
|
|
46
|
+
### Built-in Templates
|
|
47
|
+
|
|
48
|
+
- `default` - Clean, professional look
|
|
49
|
+
- `modern` - Bold colors, Inter-style typography
|
|
50
|
+
- `minimal` - Simple, classic serif
|
|
51
|
+
- `newsletter` - Email newsletter style
|
|
52
|
+
- `resume` - CV/resume formatting
|
|
53
|
+
|
|
29
54
|
## Project Structure
|
|
30
55
|
|
|
31
56
|
```
|
|
@@ -37,7 +62,7 @@ my-doc/
|
|
|
37
62
|
│ ├── parts/
|
|
38
63
|
│ │ └── header.hbs
|
|
39
64
|
│ └── styles.css
|
|
40
|
-
├──
|
|
65
|
+
├── md2pdf2.config.js
|
|
41
66
|
└── package.json
|
|
42
67
|
```
|
|
43
68
|
|
|
@@ -63,7 +88,7 @@ Templates use handlebars-like syntax for placeholders and partials:
|
|
|
63
88
|
|
|
64
89
|
## Configuration
|
|
65
90
|
|
|
66
|
-
`
|
|
91
|
+
`md2pdf2.config.js`:
|
|
67
92
|
|
|
68
93
|
```js
|
|
69
94
|
export default {
|
package/dist/cli.js
CHANGED
|
@@ -5,9 +5,10 @@ import { Converter } from './converter.js';
|
|
|
5
5
|
import { TemplateEngine } from './template-engine.js';
|
|
6
6
|
import { PDFGenerator } from './pdf-generator.js';
|
|
7
7
|
import { DEFAULT_CONFIG } from './types.js';
|
|
8
|
+
import { startDevServer } from './dev-server.js';
|
|
8
9
|
const program = new Command();
|
|
9
10
|
program
|
|
10
|
-
.name('
|
|
11
|
+
.name('md2pdf2')
|
|
11
12
|
.description('Convert Markdown to PDF using customizable templates')
|
|
12
13
|
.version('0.1.0');
|
|
13
14
|
program
|
|
@@ -17,8 +18,7 @@ program
|
|
|
17
18
|
.option('-o, --output <file>', 'Output PDF file')
|
|
18
19
|
.option('-t, --template <file>', 'Custom template file (Handlebars)')
|
|
19
20
|
.option('-s, --style <file>', 'Custom CSS file')
|
|
20
|
-
.option('-c, --config <file>', 'Configuration file (
|
|
21
|
-
.option('--no-pdf', 'Only generate HTML, do not create PDF')
|
|
21
|
+
.option('-c, --config <file>', 'Configuration file (md2pdf2.config.js)')
|
|
22
22
|
.action(async (input, options) => {
|
|
23
23
|
try {
|
|
24
24
|
// Load configuration
|
|
@@ -59,12 +59,10 @@ program
|
|
|
59
59
|
// Save HTML for debugging
|
|
60
60
|
await writeFileAsync(htmlPath, renderedHtml);
|
|
61
61
|
console.log(`✓ HTML generated: ${htmlPath}`);
|
|
62
|
-
// Generate PDF
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
console.log(`✓ PDF generated: ${outputPath}`);
|
|
67
|
-
}
|
|
62
|
+
// Generate PDF
|
|
63
|
+
const generator = new PDFGenerator();
|
|
64
|
+
await generator.generate(renderedHtml, outputPath, config.pdfOptions);
|
|
65
|
+
console.log(`✓ PDF generated: ${outputPath}`);
|
|
68
66
|
}
|
|
69
67
|
catch (error) {
|
|
70
68
|
console.error('Error:', error instanceof Error ? error.message : error);
|
|
@@ -84,5 +82,28 @@ function getOutputPath(inputPath) {
|
|
|
84
82
|
const baseName = getFileName(inputPath);
|
|
85
83
|
return `${baseName}.pdf`;
|
|
86
84
|
}
|
|
85
|
+
program
|
|
86
|
+
.command('dev')
|
|
87
|
+
.description('Start dev server with live preview')
|
|
88
|
+
.argument('<input>', 'Input Markdown file')
|
|
89
|
+
.option('-p, --port <port>', 'Dev server port', '3456')
|
|
90
|
+
.option('-c, --config <file>', 'Configuration file')
|
|
91
|
+
.action(async (input, options) => {
|
|
92
|
+
try {
|
|
93
|
+
if (!(await fileExists(input))) {
|
|
94
|
+
console.error(`Error: Input file not found: ${input}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
await startDevServer({
|
|
98
|
+
input,
|
|
99
|
+
port: parseInt(options.port, 10),
|
|
100
|
+
config: options.config
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
87
108
|
program.parse();
|
|
88
109
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAgB,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAYA,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,iBA2I7D"}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import { watch } from 'chokidar';
|
|
5
|
+
import { readdirSync, statSync } from 'fs';
|
|
6
|
+
import { readFileAsync, fileExists, getFileName } from './utils.js';
|
|
7
|
+
import { Converter } from './converter.js';
|
|
8
|
+
import { TemplateEngine } from './template-engine.js';
|
|
9
|
+
import { PDFGenerator } from './pdf-generator.js';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { DEFAULT_CONFIG } from './types.js';
|
|
12
|
+
export async function startDevServer(options) {
|
|
13
|
+
const port = options.port || 3456;
|
|
14
|
+
const app = express();
|
|
15
|
+
const server = createServer(app);
|
|
16
|
+
const wss = new WebSocketServer({ server });
|
|
17
|
+
let config = { ...DEFAULT_CONFIG };
|
|
18
|
+
let clients = [];
|
|
19
|
+
let templates = [];
|
|
20
|
+
const inputFile = path.resolve(options.input);
|
|
21
|
+
const inputDir = path.dirname(inputFile);
|
|
22
|
+
const inputBaseName = getFileName(inputFile);
|
|
23
|
+
if (options.config && await fileExists(options.config)) {
|
|
24
|
+
const configModule = await import(path.resolve(process.cwd(), options.config));
|
|
25
|
+
const userConfig = configModule.default || configModule;
|
|
26
|
+
Object.assign(config, userConfig);
|
|
27
|
+
}
|
|
28
|
+
const templatesDir = config.template ? path.dirname(config.template) : './templates';
|
|
29
|
+
templates = await discoverTemplates(templatesDir);
|
|
30
|
+
async function discoverTemplates(dir) {
|
|
31
|
+
try {
|
|
32
|
+
const results = [];
|
|
33
|
+
const scanDir = (d) => {
|
|
34
|
+
const entries = readdirSync(d);
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const fullPath = path.join(d, entry);
|
|
37
|
+
if (statSync(fullPath).isDirectory()) {
|
|
38
|
+
if (entry !== 'parts')
|
|
39
|
+
scanDir(fullPath);
|
|
40
|
+
}
|
|
41
|
+
else if (entry.endsWith('.hbs')) {
|
|
42
|
+
results.push(fullPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
scanDir(dir);
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function renderMarkdown(templatePath) {
|
|
54
|
+
if (!(await fileExists(options.input))) {
|
|
55
|
+
return '<p style="color: red;">Input file not found</p>';
|
|
56
|
+
}
|
|
57
|
+
const markdown = await readFileAsync(options.input);
|
|
58
|
+
const converter = new Converter();
|
|
59
|
+
const { html, frontMatter } = converter.convert(markdown);
|
|
60
|
+
let styles = '';
|
|
61
|
+
if (config.style && await fileExists(config.style)) {
|
|
62
|
+
styles = await readFileAsync(config.style);
|
|
63
|
+
}
|
|
64
|
+
const engine = new TemplateEngine(config);
|
|
65
|
+
if (templatePath && await fileExists(templatePath)) {
|
|
66
|
+
await engine.loadTemplate(templatePath);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
engine.loadDefaultTemplate();
|
|
70
|
+
}
|
|
71
|
+
return engine.render(html, frontMatter, styles);
|
|
72
|
+
}
|
|
73
|
+
function getTemplateList() {
|
|
74
|
+
return templates.map(t => ({
|
|
75
|
+
name: path.basename(t, '.hbs'),
|
|
76
|
+
path: t
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
app.get('/', (_req, res) => {
|
|
80
|
+
res.send(getDevHTML(port));
|
|
81
|
+
});
|
|
82
|
+
app.get('/api/templates', (_req, res) => {
|
|
83
|
+
res.json(getTemplateList());
|
|
84
|
+
});
|
|
85
|
+
app.get('/api/render', async (req, res) => {
|
|
86
|
+
const template = req.query.template;
|
|
87
|
+
const html = await renderMarkdown(template);
|
|
88
|
+
res.send(html);
|
|
89
|
+
});
|
|
90
|
+
app.get('/api/generate-pdf', async (req, res) => {
|
|
91
|
+
try {
|
|
92
|
+
const template = req.query.template;
|
|
93
|
+
const html = await renderMarkdown(template);
|
|
94
|
+
const templateInfo = templates.find(t => t === template);
|
|
95
|
+
const templateName = templateInfo ? path.basename(templateInfo, '.hbs') : 'output';
|
|
96
|
+
const outputPath = path.join(inputDir, `${inputBaseName}-${templateName}.pdf`);
|
|
97
|
+
const generator = new PDFGenerator();
|
|
98
|
+
await generator.generate(html, outputPath, config.pdfOptions);
|
|
99
|
+
res.download(outputPath);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
res.status(500).json({ error: error instanceof Error ? error.message : 'Failed to generate PDF' });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
wss.on('connection', (ws) => {
|
|
106
|
+
clients.push(ws);
|
|
107
|
+
ws.on('close', () => {
|
|
108
|
+
clients = clients.filter(c => c !== ws);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
function broadcast(type, data) {
|
|
112
|
+
const message = JSON.stringify({ type, data });
|
|
113
|
+
clients.forEach(client => {
|
|
114
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
115
|
+
client.send(message);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const watcher = watch([options.input, templatesDir], {
|
|
120
|
+
ignoreInitial: true,
|
|
121
|
+
awaitWriteFinish: { stabilityThreshold: 100 }
|
|
122
|
+
});
|
|
123
|
+
watcher.on('change', async (filepath) => {
|
|
124
|
+
if (filepath.endsWith('.hbs')) {
|
|
125
|
+
templates = await discoverTemplates(templatesDir);
|
|
126
|
+
broadcast('templates-updated', getTemplateList());
|
|
127
|
+
}
|
|
128
|
+
broadcast('reload');
|
|
129
|
+
});
|
|
130
|
+
server.listen(port, () => {
|
|
131
|
+
console.log(`\n Dev server running at http://localhost:${port}`);
|
|
132
|
+
console.log(` Watching: ${options.input}, ${templatesDir}/\n`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function getDevHTML(port) {
|
|
136
|
+
return `<!DOCTYPE html>
|
|
137
|
+
<html lang="en">
|
|
138
|
+
<head>
|
|
139
|
+
<meta charset="UTF-8">
|
|
140
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
141
|
+
<title>md2pdf2 - Dev Preview</title>
|
|
142
|
+
<style>
|
|
143
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
144
|
+
body {
|
|
145
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
146
|
+
height: 100vh;
|
|
147
|
+
display: flex;
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
background: #0f172a;
|
|
150
|
+
color: #e2e8f0;
|
|
151
|
+
}
|
|
152
|
+
.sidebar {
|
|
153
|
+
width: 280px;
|
|
154
|
+
background: #1e293b;
|
|
155
|
+
border-right: 1px solid #334155;
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
}
|
|
159
|
+
.sidebar-header {
|
|
160
|
+
padding: 20px;
|
|
161
|
+
border-bottom: 1px solid #334155;
|
|
162
|
+
}
|
|
163
|
+
.sidebar-header h1 {
|
|
164
|
+
font-size: 18px;
|
|
165
|
+
font-weight: 600;
|
|
166
|
+
color: #f8fafc;
|
|
167
|
+
}
|
|
168
|
+
.sidebar-header p {
|
|
169
|
+
font-size: 12px;
|
|
170
|
+
color: #94a3b8;
|
|
171
|
+
margin-top: 4px;
|
|
172
|
+
}
|
|
173
|
+
.template-list {
|
|
174
|
+
flex: 1;
|
|
175
|
+
overflow-y: auto;
|
|
176
|
+
padding: 12px;
|
|
177
|
+
}
|
|
178
|
+
.template-item {
|
|
179
|
+
padding: 12px 16px;
|
|
180
|
+
border-radius: 8px;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
margin-bottom: 4px;
|
|
183
|
+
font-size: 14px;
|
|
184
|
+
transition: all 0.15s;
|
|
185
|
+
border: 1px solid transparent;
|
|
186
|
+
}
|
|
187
|
+
.template-item:hover {
|
|
188
|
+
background: #334155;
|
|
189
|
+
}
|
|
190
|
+
.template-item.active {
|
|
191
|
+
background: #3b82f6;
|
|
192
|
+
color: white;
|
|
193
|
+
border-color: #60a5fa;
|
|
194
|
+
}
|
|
195
|
+
.preview-container {
|
|
196
|
+
flex: 1;
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
}
|
|
200
|
+
.preview-header {
|
|
201
|
+
padding: 12px 20px;
|
|
202
|
+
background: #1e293b;
|
|
203
|
+
border-bottom: 1px solid #334155;
|
|
204
|
+
display: flex;
|
|
205
|
+
justify-content: space-between;
|
|
206
|
+
align-items: center;
|
|
207
|
+
}
|
|
208
|
+
.preview-header span {
|
|
209
|
+
font-size: 13px;
|
|
210
|
+
color: #94a3b8;
|
|
211
|
+
}
|
|
212
|
+
.status-dot {
|
|
213
|
+
width: 8px;
|
|
214
|
+
height: 8px;
|
|
215
|
+
background: #22c55e;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
display: inline-block;
|
|
218
|
+
margin-right: 6px;
|
|
219
|
+
}
|
|
220
|
+
.export-btn {
|
|
221
|
+
background: #3b82f6;
|
|
222
|
+
color: white;
|
|
223
|
+
border: none;
|
|
224
|
+
padding: 8px 16px;
|
|
225
|
+
border-radius: 6px;
|
|
226
|
+
font-size: 13px;
|
|
227
|
+
cursor: pointer;
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
gap: 6px;
|
|
231
|
+
transition: background 0.15s;
|
|
232
|
+
}
|
|
233
|
+
.export-btn:hover {
|
|
234
|
+
background: #2563eb;
|
|
235
|
+
}
|
|
236
|
+
.export-btn:disabled {
|
|
237
|
+
background: #475569;
|
|
238
|
+
cursor: not-allowed;
|
|
239
|
+
}
|
|
240
|
+
.export-btn svg {
|
|
241
|
+
width: 16px;
|
|
242
|
+
height: 16px;
|
|
243
|
+
}
|
|
244
|
+
.preview-frame {
|
|
245
|
+
flex: 1;
|
|
246
|
+
background: white;
|
|
247
|
+
border: none;
|
|
248
|
+
}
|
|
249
|
+
.loading {
|
|
250
|
+
position: absolute;
|
|
251
|
+
top: 50%;
|
|
252
|
+
left: 50%;
|
|
253
|
+
transform: translate(-50%, -50%);
|
|
254
|
+
color: #94a3b8;
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
}
|
|
257
|
+
.toast {
|
|
258
|
+
position: fixed;
|
|
259
|
+
bottom: 20px;
|
|
260
|
+
right: 20px;
|
|
261
|
+
background: #1e293b;
|
|
262
|
+
color: #e2e8f0;
|
|
263
|
+
padding: 12px 20px;
|
|
264
|
+
border-radius: 8px;
|
|
265
|
+
font-size: 13px;
|
|
266
|
+
opacity: 0;
|
|
267
|
+
transform: translateY(10px);
|
|
268
|
+
transition: all 0.3s ease;
|
|
269
|
+
pointer-events: none;
|
|
270
|
+
border: 1px solid #334155;
|
|
271
|
+
}
|
|
272
|
+
.toast.show {
|
|
273
|
+
opacity: 1;
|
|
274
|
+
transform: translateY(0);
|
|
275
|
+
}
|
|
276
|
+
.toast .toast-dot {
|
|
277
|
+
width: 6px;
|
|
278
|
+
height: 6px;
|
|
279
|
+
background: #22c55e;
|
|
280
|
+
border-radius: 50%;
|
|
281
|
+
display: inline-block;
|
|
282
|
+
margin-right: 8px;
|
|
283
|
+
animation: pulse 1s ease infinite;
|
|
284
|
+
}
|
|
285
|
+
@keyframes pulse {
|
|
286
|
+
0%, 100% { opacity: 1; }
|
|
287
|
+
50% { opacity: 0.5; }
|
|
288
|
+
}
|
|
289
|
+
</style>
|
|
290
|
+
</head>
|
|
291
|
+
<body>
|
|
292
|
+
<div class="toast" id="toast"><span class="toast-dot"></span><span id="toastMsg">Reloading...</span></div>
|
|
293
|
+
<div class="sidebar">
|
|
294
|
+
<div class="sidebar-header">
|
|
295
|
+
<h1>md2pdf2</h1>
|
|
296
|
+
<p id="currentTemplate"><span style="color: #22c55e;">●</span> Watching for changes</p>
|
|
297
|
+
</div>
|
|
298
|
+
<div class="template-list" id="templateList"></div>
|
|
299
|
+
</div>
|
|
300
|
+
<div class="preview-container">
|
|
301
|
+
<div class="preview-header">
|
|
302
|
+
<span><span class="status-dot"></span>Live Preview</span>
|
|
303
|
+
<button class="export-btn" id="exportBtn" onclick="exportPDF()">
|
|
304
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
|
|
305
|
+
<span id="btnText">Export PDF</span>
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
<iframe class="preview-frame" id="previewFrame"></iframe>
|
|
309
|
+
</div>
|
|
310
|
+
<script>
|
|
311
|
+
const templateList = document.getElementById('templateList');
|
|
312
|
+
const previewFrame = document.getElementById('previewFrame');
|
|
313
|
+
const currentTemplateEl = document.getElementById('currentTemplate');
|
|
314
|
+
let templates = [];
|
|
315
|
+
let activeTemplate = null;
|
|
316
|
+
|
|
317
|
+
async function loadTemplates() {
|
|
318
|
+
const res = await fetch('/api/templates');
|
|
319
|
+
templates = await res.json();
|
|
320
|
+
renderTemplateList();
|
|
321
|
+
if (templates.length > 0 && !activeTemplate) {
|
|
322
|
+
selectTemplate(templates[0].path);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function renderTemplateList() {
|
|
327
|
+
templateList.innerHTML = templates.map(t => \`
|
|
328
|
+
<div class="template-item \${activeTemplate === t.path ? 'active' : ''}" data-path="\${t.path}">
|
|
329
|
+
\${t.name}
|
|
330
|
+
</div>
|
|
331
|
+
\`).join('');
|
|
332
|
+
|
|
333
|
+
templateList.querySelectorAll('.template-item').forEach(el => {
|
|
334
|
+
el.addEventListener('click', () => selectTemplate(el.dataset.path));
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function selectTemplate(path) {
|
|
339
|
+
activeTemplate = path;
|
|
340
|
+
const template = templates.find(t => t.path === path);
|
|
341
|
+
if (template) currentTemplateEl.innerHTML = '<span style="color: #22c55e;">●</span> ' + template.name;
|
|
342
|
+
renderTemplateList();
|
|
343
|
+
await renderPreview();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function renderPreview() {
|
|
347
|
+
const res = await fetch('/api/render?template=' + encodeURIComponent(activeTemplate));
|
|
348
|
+
const html = await res.text();
|
|
349
|
+
previewFrame.srcdoc = html;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function exportPDF() {
|
|
353
|
+
const btn = document.getElementById('exportBtn');
|
|
354
|
+
const btnText = document.getElementById('btnText');
|
|
355
|
+
btn.disabled = true;
|
|
356
|
+
btnText.textContent = 'Generating...';
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
const res = await fetch('/api/generate-pdf?template=' + encodeURIComponent(activeTemplate));
|
|
360
|
+
if (!res.ok) throw new Error('Failed to generate PDF');
|
|
361
|
+
|
|
362
|
+
const blob = await res.blob();
|
|
363
|
+
const url = URL.createObjectURL(blob);
|
|
364
|
+
const a = document.createElement('a');
|
|
365
|
+
a.href = url;
|
|
366
|
+
a.download = 'output.pdf';
|
|
367
|
+
document.body.appendChild(a);
|
|
368
|
+
a.click();
|
|
369
|
+
document.body.removeChild(a);
|
|
370
|
+
URL.revokeObjectURL(url);
|
|
371
|
+
} catch (err) {
|
|
372
|
+
alert('Failed to generate PDF: ' + err.message);
|
|
373
|
+
} finally {
|
|
374
|
+
btn.disabled = false;
|
|
375
|
+
btnText.textContent = 'Export PDF';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function showToast(msg) {
|
|
380
|
+
const toast = document.getElementById('toast');
|
|
381
|
+
const toastMsg = document.getElementById('toastMsg');
|
|
382
|
+
toastMsg.textContent = msg;
|
|
383
|
+
toast.classList.add('show');
|
|
384
|
+
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const ws = new WebSocket('ws://localhost:${port}');
|
|
388
|
+
ws.onmessage = async (event) => {
|
|
389
|
+
const msg = JSON.parse(event.data);
|
|
390
|
+
if (msg.type === 'reload') {
|
|
391
|
+
showToast('File changed, reloading...');
|
|
392
|
+
await renderPreview();
|
|
393
|
+
}
|
|
394
|
+
if (msg.type === 'templates-updated') {
|
|
395
|
+
templates = msg.data;
|
|
396
|
+
renderTemplateList();
|
|
397
|
+
showToast('Templates updated');
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
loadTemplates();
|
|
402
|
+
</script>
|
|
403
|
+
</body>
|
|
404
|
+
</html>`;
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=dev-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server.js","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAkD;gBAEtD,MAAM,EAAE,YAAY;IAUhC,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAkD;gBAEtD,MAAM,EAAE,YAAY;IAUhC,OAAO,CAAC,eAAe;YAaT,sBAAsB;IAW9B,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,mBAAmB,IAAI,IAAI;IAiD3B,OAAO,CAAC,eAAe;IAIvB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;CASlF"}
|
package/dist/template-engine.js
CHANGED
|
@@ -15,6 +15,14 @@ export class TemplateEngine {
|
|
|
15
15
|
Handlebars.registerHelper('hasPartial', (name) => {
|
|
16
16
|
return !!this.partials[name];
|
|
17
17
|
});
|
|
18
|
+
Handlebars.registerHelper('currentDate', () => {
|
|
19
|
+
return new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
|
20
|
+
});
|
|
21
|
+
Handlebars.registerHelper('slice', (str, start, end) => {
|
|
22
|
+
if (typeof str !== 'string')
|
|
23
|
+
return '';
|
|
24
|
+
return str.slice(start, end);
|
|
25
|
+
});
|
|
18
26
|
}
|
|
19
27
|
async loadPartialsFromConfig(partialsConfig) {
|
|
20
28
|
for (const [name, path] of Object.entries(partialsConfig)) {
|
|
@@ -90,7 +98,8 @@ export class TemplateEngine {
|
|
|
90
98
|
content,
|
|
91
99
|
frontMatter,
|
|
92
100
|
styles,
|
|
93
|
-
partials: this.partials
|
|
101
|
+
partials: this.partials,
|
|
102
|
+
...frontMatter
|
|
94
103
|
});
|
|
95
104
|
}
|
|
96
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,OAAO,cAAc;IACjB,QAAQ,CAA8B;IACtC,QAAQ,GAA+C,EAAE,CAAC;IAElE,YAAY,MAAoB;QAC9B,4BAA4B;QAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,wCAAwC;QACxC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE;YACvD,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,cAAsC;QACzE,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,oCAAoC,IAAI,UAAU,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB;QACrC,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAED,mBAAmB;QACjB,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4CpB,CAAC;QACL,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,OAAe;QAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,WAAgC,EAAE,MAAc;QACtE,OAAO,IAAI,CAAC,QAAQ,CAAC;YACnB,OAAO;YACP,WAAW;YACX,MAAM;YACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;
|
|
1
|
+
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../src/template-engine.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,MAAM,OAAO,cAAc;IACjB,QAAQ,CAA8B;IACtC,QAAQ,GAA+C,EAAE,CAAC;IAElE,YAAY,MAAoB;QAC9B,4BAA4B;QAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,wCAAwC;QACxC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE;YACvD,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE;YAC5C,OAAO,IAAI,IAAI,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QACpG,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,GAAW,EAAE,KAAa,EAAE,GAAY,EAAE,EAAE;YAC9E,IAAI,OAAO,GAAG,KAAK,QAAQ;gBAAE,OAAO,EAAE,CAAC;YACvC,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,cAAsC;QACzE,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,oCAAoC,IAAI,UAAU,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,YAAoB;QACrC,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAED,mBAAmB;QACjB,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4CpB,CAAC;QACL,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;IACxD,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,OAAe;QAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,WAAgC,EAAE,MAAc;QACtE,OAAO,IAAI,CAAC,QAAQ,CAAC;YACnB,OAAO;YACP,WAAW;YACX,MAAM;YACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,GAAG,WAAW;SACf,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "md2pdf2",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Convert Markdown to PDF using customizable templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"
|
|
8
|
+
"md2pdf2": "dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
@@ -29,14 +29,19 @@
|
|
|
29
29
|
"url": "git+https://github.com/areai51/md2pdf.git"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"chokidar": "^5.0.0",
|
|
32
33
|
"commander": "^12.1.0",
|
|
34
|
+
"express": "^5.2.1",
|
|
33
35
|
"front-matter": "^4.0.2",
|
|
34
36
|
"handlebars": "^4.7.8",
|
|
35
37
|
"marked": "^11.1.1",
|
|
36
|
-
"puppeteer": "^23.0.0"
|
|
38
|
+
"puppeteer": "^23.0.0",
|
|
39
|
+
"ws": "^8.19.0"
|
|
37
40
|
},
|
|
38
41
|
"devDependencies": {
|
|
42
|
+
"@types/express": "^5.0.6",
|
|
39
43
|
"@types/node": "^20.11.0",
|
|
44
|
+
"@types/ws": "^8.18.1",
|
|
40
45
|
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
41
46
|
"@typescript-eslint/parser": "^6.19.0",
|
|
42
47
|
"eslint": "^8.56.0",
|