mc-markdown-viewer 1.0.0

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.
Files changed (2) hide show
  1. package/main.js +513 -0
  2. package/package.json +27 -0
package/main.js ADDED
@@ -0,0 +1,513 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const fs = require('fs').promises;
5
+ const path = require('path');
6
+ const os = require('os');
7
+ const { execSync } = require('child_process');
8
+ const marked = require('marked');
9
+
10
+ program
11
+ .version('1.0.0')
12
+ .description('Convert Markdown to HTML with enhanced Tailwind CSS dark theme')
13
+ .requiredOption('-f, --file <path>', 'Input Markdown file')
14
+ .option('-t, --theme <theme>', 'Theme variant (default, ocean, forest, sunset)', 'default')
15
+ .parse(process.argv);
16
+
17
+ const options = program.opts();
18
+
19
+ const themeVariables = {
20
+ default: {
21
+ primary: '#60a5fa',
22
+ primaryHover: '#93c5fd',
23
+ accent: '#f59e0b',
24
+ background: 'linear-gradient(135deg, #1f2937 0%, #111827 100%)',
25
+ cardBg: 'rgba(31, 41, 55, 0.8)',
26
+ borderColor: '#374151'
27
+ },
28
+ ocean: {
29
+ primary: '#06b6d4',
30
+ primaryHover: '#67e8f9',
31
+ accent: '#3b82f6',
32
+ background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
33
+ cardBg: 'rgba(15, 23, 42, 0.9)',
34
+ borderColor: '#1e293b'
35
+ },
36
+ forest: {
37
+ primary: '#10b981',
38
+ primaryHover: '#34d399',
39
+ accent: '#f59e0b',
40
+ background: 'linear-gradient(135deg, #064e3b 0%, #1f2937 50%, #064e3b 100%)',
41
+ cardBg: 'rgba(6, 78, 59, 0.8)',
42
+ borderColor: '#065f46'
43
+ },
44
+ sunset: {
45
+ primary: '#f97316',
46
+ primaryHover: '#fb923c',
47
+ accent: '#ef4444',
48
+ background: 'linear-gradient(135deg, #7c2d12 0%, #1f2937 50%, #7c2d12 100%)',
49
+ cardBg: 'rgba(124, 45, 18, 0.8)',
50
+ borderColor: '#c2410c'
51
+ }
52
+ };
53
+
54
+ const htmlTemplate = (content, theme = 'default') => {
55
+ const colors = themeVariables[theme];
56
+
57
+ return `
58
+ <!DOCTYPE html>
59
+ <html lang="en" class="dark">
60
+ <head>
61
+ <meta charset="UTF-8">
62
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
63
+ <title>Markdown Viewer</title>
64
+ <script src="https://cdn.tailwindcss.com"></script>
65
+ <link rel="preconnect" href="https://fonts.googleapis.com">
66
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
67
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
68
+ <style>
69
+ :root {
70
+ --primary-color: ${colors.primary};
71
+ --primary-hover: ${colors.primaryHover};
72
+ --accent-color: ${colors.accent};
73
+ --card-bg: ${colors.cardBg};
74
+ --border-color: ${colors.borderColor};
75
+ }
76
+
77
+ body {
78
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
79
+ min-height: 100vh;
80
+ background: ${colors.background};
81
+ background-attachment: fixed;
82
+ line-height: 1.7;
83
+ }
84
+
85
+ /* Smooth scroll behavior */
86
+ html {
87
+ scroll-behavior: smooth;
88
+ }
89
+
90
+ /* Enhanced header and footer */
91
+ header, footer {
92
+ background: var(--card-bg);
93
+ backdrop-filter: blur(20px);
94
+ border-bottom: 1px solid var(--border-color);
95
+ box-shadow: 0 4px 32px rgba(0, 0, 0, 0.3);
96
+ }
97
+
98
+ footer {
99
+ border-bottom: none;
100
+ border-top: 1px solid var(--border-color);
101
+ }
102
+
103
+ /* Enhanced prose styling */
104
+ .prose-enhanced {
105
+ max-width: none;
106
+ color: #e5e7eb;
107
+ }
108
+
109
+ .prose-enhanced h1,
110
+ .prose-enhanced h2,
111
+ .prose-enhanced h3,
112
+ .prose-enhanced h4,
113
+ .prose-enhanced h5,
114
+ .prose-enhanced h6 {
115
+ font-weight: 700;
116
+ color: #f9fafb;
117
+ margin-top: 2rem;
118
+ margin-bottom: 1rem;
119
+ position: relative;
120
+ scroll-margin-top: 6rem;
121
+ }
122
+
123
+ .prose-enhanced h1 {
124
+ font-size: 2.5rem;
125
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
126
+ background-clip: text;
127
+ -webkit-background-clip: text;
128
+ -webkit-text-fill-color: transparent;
129
+ border-bottom: 3px solid var(--border-color);
130
+ padding-bottom: 1rem;
131
+ }
132
+
133
+ .prose-enhanced h2 {
134
+ font-size: 2rem;
135
+ color: var(--primary-color);
136
+ border-bottom: 2px solid var(--border-color);
137
+ padding-bottom: 0.75rem;
138
+ }
139
+
140
+ .prose-enhanced h3 {
141
+ font-size: 1.5rem;
142
+ color: var(--primary-hover);
143
+ border-left: 4px solid var(--primary-color);
144
+ padding-left: 1rem;
145
+ }
146
+
147
+ .prose-enhanced a {
148
+ color: var(--primary-color);
149
+ text-decoration: underline;
150
+ text-underline-offset: 4px;
151
+ text-decoration-thickness: 2px;
152
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
153
+ }
154
+
155
+ .prose-enhanced a:hover {
156
+ color: var(--primary-hover);
157
+ text-decoration-thickness: 3px;
158
+ text-shadow: 0 0 8px var(--primary-color);
159
+ }
160
+
161
+ /* Enhanced code styling */
162
+ .prose-enhanced code {
163
+ background: rgba(15, 23, 42, 0.8);
164
+ color: #e2e8f0;
165
+ padding: 0.25rem 0.5rem;
166
+ border-radius: 0.375rem;
167
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
168
+ font-size: 0.875em;
169
+ font-weight: 500;
170
+ border: 1px solid var(--border-color);
171
+ }
172
+
173
+ .prose-enhanced pre {
174
+ background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.8));
175
+ padding: 1.5rem;
176
+ border-radius: 0.75rem;
177
+ border: 1px solid var(--border-color);
178
+ box-shadow:
179
+ 0 20px 25px -5px rgba(0, 0, 0, 0.3),
180
+ 0 10px 10px -5px rgba(0, 0, 0, 0.2),
181
+ inset 0 1px 0 rgba(255, 255, 255, 0.05);
182
+ position: relative;
183
+ overflow-x: auto;
184
+ }
185
+
186
+ .prose-enhanced pre:before {
187
+ content: '';
188
+ position: absolute;
189
+ top: 0;
190
+ left: 0;
191
+ right: 0;
192
+ height: 2px;
193
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
194
+ }
195
+
196
+ .prose-enhanced pre code {
197
+ background: none;
198
+ padding: 0;
199
+ border: none;
200
+ color: #e2e8f0;
201
+ font-size: 0.875rem;
202
+ }
203
+
204
+ /* Enhanced blockquotes */
205
+ .prose-enhanced blockquote {
206
+ border-left: 4px solid var(--primary-color);
207
+ background: var(--card-bg);
208
+ padding: 1.5rem;
209
+ margin: 2rem 0;
210
+ border-radius: 0 0.5rem 0.5rem 0;
211
+ font-style: italic;
212
+ color: #d1d5db;
213
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
214
+ }
215
+
216
+ /* Enhanced tables */
217
+ .prose-enhanced table {
218
+ width: 100%;
219
+ border-collapse: separate;
220
+ border-spacing: 0;
221
+ background: var(--card-bg);
222
+ border-radius: 0.5rem;
223
+ overflow: hidden;
224
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2);
225
+ }
226
+
227
+ .prose-enhanced th {
228
+ background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
229
+ color: white;
230
+ font-weight: 600;
231
+ padding: 1rem;
232
+ text-align: left;
233
+ }
234
+
235
+ .prose-enhanced td {
236
+ padding: 0.75rem 1rem;
237
+ border-bottom: 1px solid var(--border-color);
238
+ }
239
+
240
+ .prose-enhanced tr:last-child td {
241
+ border-bottom: none;
242
+ }
243
+
244
+ /* Enhanced lists */
245
+ .prose-enhanced ul, .prose-enhanced ol {
246
+ margin: 1.5rem 0;
247
+ padding-left: 2rem;
248
+ }
249
+
250
+ .prose-enhanced li {
251
+ margin: 0.5rem 0;
252
+ color: #e5e7eb;
253
+ }
254
+
255
+ .prose-enhanced ul li::marker {
256
+ color: var(--primary-color);
257
+ }
258
+
259
+ .prose-enhanced ol li::marker {
260
+ color: var(--accent-color);
261
+ font-weight: 600;
262
+ }
263
+
264
+ /* Enhanced horizontal rule */
265
+ .prose-enhanced hr {
266
+ border: none;
267
+ height: 2px;
268
+ background: linear-gradient(90deg, transparent, var(--primary-color), transparent);
269
+ margin: 3rem 0;
270
+ }
271
+
272
+ /* Main content container */
273
+ .content-container {
274
+ background: rgba(17, 24, 39, 0.4);
275
+ backdrop-filter: blur(10px);
276
+ border-radius: 1rem;
277
+ border: 1px solid var(--border-color);
278
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
279
+ transition: all 0.3s ease;
280
+ }
281
+
282
+ /* Animations */
283
+ @keyframes fadeInUp {
284
+ from {
285
+ opacity: 0;
286
+ transform: translateY(30px);
287
+ }
288
+ to {
289
+ opacity: 1;
290
+ transform: translateY(0);
291
+ }
292
+ }
293
+
294
+ .animate-fade-in-up {
295
+ animation: fadeInUp 0.6s ease-out;
296
+ }
297
+
298
+ /* Theme toggle button */
299
+ .theme-indicator {
300
+ display: inline-block;
301
+ width: 12px;
302
+ height: 12px;
303
+ border-radius: 50%;
304
+ background: var(--primary-color);
305
+ margin-left: 0.5rem;
306
+ box-shadow: 0 0 8px var(--primary-color);
307
+ }
308
+
309
+ /* Responsive design improvements */
310
+ @media (max-width: 768px) {
311
+ .prose-enhanced h1 {
312
+ font-size: 2rem;
313
+ }
314
+ .prose-enhanced h2 {
315
+ font-size: 1.5rem;
316
+ }
317
+ .content-container {
318
+ margin: 1rem;
319
+ border-radius: 0.5rem;
320
+ }
321
+ }
322
+
323
+ /* Reduced motion preferences */
324
+ @media (prefers-reduced-motion: reduce) {
325
+ html {
326
+ scroll-behavior: auto;
327
+ }
328
+ * {
329
+ animation-duration: 0.01ms !important;
330
+ animation-iteration-count: 1 !important;
331
+ transition-duration: 0.01ms !important;
332
+ }
333
+ }
334
+
335
+ /* Print styles */
336
+ @media print {
337
+ header, footer {
338
+ display: none;
339
+ }
340
+ body {
341
+ background: white;
342
+ color: black;
343
+ }
344
+ .content-container {
345
+ background: none;
346
+ box-shadow: none;
347
+ border: none;
348
+ }
349
+ }
350
+ </style>
351
+ </head>
352
+ <body>
353
+ <header class="sticky top-0 z-50">
354
+ <div class="max-w-6xl mx-auto py-4 px-6 flex justify-between items-center">
355
+ <div class="flex items-center">
356
+ <h1 class="text-2xl font-bold text-white">Markdown Viewer</h1>
357
+ <span class="theme-indicator" title="Current theme: ${theme}"></span>
358
+ </div>
359
+ <nav class="flex items-center space-x-4">
360
+ <button onclick="scrollToTop()" class="text-gray-300 hover:text-white px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 hover:bg-white/10">
361
+ ↑ Top
362
+ </button>
363
+ <button onclick="toggleReadingMode()" class="text-gray-300 hover:text-white px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 hover:bg-white/10">
364
+ 👁 Focus
365
+ </button>
366
+ </nav>
367
+ </div>
368
+ </header>
369
+
370
+ <main class="max-w-6xl mx-auto py-8 px-6 animate-fade-in-up">
371
+ <div class="content-container p-8 prose-enhanced">
372
+ ${content}
373
+ </div>
374
+ </main>
375
+
376
+ <footer class="mt-16">
377
+ <div class="max-w-6xl mx-auto py-6 px-6 text-sm flex flex-col md:flex-row justify-between items-center space-y-2 md:space-y-0">
378
+ <p class="text-gray-300">Generated by Enhanced Markdown-to-HTML CLI by <a href="https://mohan-chinnappan-n.github.io/about/cv.html">MC</a></p>
379
+ <div class="flex items-center space-x-4 text-gray-400">
380
+ <span>Theme: ${theme}</span>
381
+ <span>© ${new Date().getFullYear()}</span>
382
+ </div>
383
+ </div>
384
+ </footer>
385
+
386
+ <script>
387
+ function scrollToTop() {
388
+ window.scrollTo({ top: 0, behavior: 'smooth' });
389
+ }
390
+
391
+ function toggleReadingMode() {
392
+ const header = document.querySelector('header');
393
+ const footer = document.querySelector('footer');
394
+
395
+ if (header.style.display === 'none') {
396
+ header.style.display = 'block';
397
+ footer.style.display = 'block';
398
+ } else {
399
+ header.style.display = 'none';
400
+ footer.style.display = 'none';
401
+ }
402
+ }
403
+
404
+ // Add smooth scrolling to anchor links
405
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
406
+ anchor.addEventListener('click', function (e) {
407
+ e.preventDefault();
408
+ const target = document.querySelector(this.getAttribute('href'));
409
+ if (target) {
410
+ target.scrollIntoView({ behavior: 'smooth', block: 'start' });
411
+ }
412
+ });
413
+ });
414
+
415
+ // Enhance code blocks with copy functionality
416
+ document.querySelectorAll('pre').forEach(pre => {
417
+ const button = document.createElement('button');
418
+ button.innerHTML = '📋 Copy';
419
+ button.className = 'absolute top-2 right-2 px-3 py-1 bg-gray-700 hover:bg-gray-600 text-gray-300 text-xs rounded transition-colors';
420
+ button.style.position = 'absolute';
421
+ pre.style.position = 'relative';
422
+
423
+ button.addEventListener('click', () => {
424
+ const code = pre.querySelector('code').textContent;
425
+ navigator.clipboard.writeText(code).then(() => {
426
+ button.innerHTML = '✅ Copied!';
427
+ setTimeout(() => {
428
+ button.innerHTML = '📋 Copy';
429
+ }, 2000);
430
+ });
431
+ });
432
+
433
+ pre.appendChild(button);
434
+ });
435
+
436
+ // Add reading progress indicator
437
+ function updateReadingProgress() {
438
+ const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
439
+ const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
440
+ const scrolled = (winScroll / height) * 100;
441
+
442
+ let progressBar = document.getElementById('reading-progress');
443
+ if (!progressBar) {
444
+ progressBar = document.createElement('div');
445
+ progressBar.id = 'reading-progress';
446
+ progressBar.style.cssText = \`
447
+ position: fixed;
448
+ top: 0;
449
+ left: 0;
450
+ width: \${scrolled}%;
451
+ height: 3px;
452
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
453
+ z-index: 100;
454
+ transition: width 0.1s ease-out;
455
+ \`;
456
+ document.body.appendChild(progressBar);
457
+ }
458
+ progressBar.style.width = scrolled + '%';
459
+ }
460
+
461
+ window.addEventListener('scroll', updateReadingProgress);
462
+ updateReadingProgress();
463
+ </script>
464
+ </body>
465
+ </html>
466
+ `;
467
+ };
468
+
469
+ async function convertMarkdownToHtml(inputFile, theme) {
470
+ try {
471
+ // Read the markdown file
472
+ const markdown = await fs.readFile(inputFile, 'utf-8');
473
+
474
+ // Convert markdown to HTML using marked
475
+ const htmlContent = marked.parse(markdown);
476
+
477
+ // Generate full HTML with template
478
+ const fullHtml = htmlTemplate(htmlContent, theme);
479
+
480
+ // Create temp folder
481
+ const tempDir = path.join(os.tmpdir(), 'md-to-html');
482
+ await fs.mkdir(tempDir, { recursive: true });
483
+
484
+ // Create output filename in temp folder
485
+ const outputFile = path.join(tempDir, path.basename(inputFile, path.extname(inputFile)) + '.html');
486
+
487
+ // Write HTML to file
488
+ await fs.writeFile(outputFile, fullHtml);
489
+
490
+ console.log(`Successfully converted ${inputFile} to ${outputFile}`);
491
+ console.log(`Theme: ${theme}`);
492
+
493
+ // Open the file in default browser (cross-platform)
494
+ try {
495
+ if (process.platform === 'darwin') {
496
+ execSync(`open "${outputFile}"`);
497
+ } else if (process.platform === 'win32') {
498
+ execSync(`start "${outputFile}"`);
499
+ } else {
500
+ execSync(`xdg-open "${outputFile}"`);
501
+ }
502
+ } catch (browserError) {
503
+ console.log(`File saved to: ${outputFile}`);
504
+ console.log('Please open the file manually in your browser.');
505
+ }
506
+ } catch (error) {
507
+ console.error('Error:', error.message);
508
+ process.exit(1);
509
+ }
510
+ }
511
+
512
+ // Execute conversion
513
+ convertMarkdownToHtml(options.file, options.theme);
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "mc-markdown-viewer",
3
+ "version": "1.0.0",
4
+ "description": "A beautiful CLI tool to convert Markdown files to HTML with multiple dark themes and modern styling",
5
+
6
+ "main": "main.js",
7
+ "bin": {
8
+ "md-viewer": "./main.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [
14
+ "markdown",
15
+ "cli",
16
+ "viewer"
17
+ ],
18
+ "author": "Mohan Chinnappan",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "commander": "^14.0.1",
22
+ "marked": "^16.3.0"
23
+ },
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ }
27
+ }