@wipcomputer/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.
@@ -0,0 +1,559 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Markdown Preview</title>
7
+ <!-- Syntax highlighting -->
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css" id="highlight-theme">
9
+ <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
10
+ <!-- Mermaid for diagrams -->
11
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
12
+ <!-- KaTeX for math -->
13
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
14
+ <script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
16
+ <style>
17
+ :root {
18
+ --bg-primary: #ffffff;
19
+ --bg-secondary: #f5f5f5;
20
+ --bg-code: #f8f8f8;
21
+ --text-primary: #333;
22
+ --text-secondary: #666;
23
+ --text-heading: #2c3e50;
24
+ --border-color: #e0e0e0;
25
+ --accent-color: #667eea;
26
+ --link-color: #667eea;
27
+ --table-stripe: #f9f9f9;
28
+ }
29
+
30
+ [data-theme="dark"] {
31
+ --bg-primary: #1e1e1e;
32
+ --bg-secondary: #121212;
33
+ --bg-code: #2d2d2d;
34
+ --text-primary: #e0e0e0;
35
+ --text-secondary: #b0b0b0;
36
+ --text-heading: #ffffff;
37
+ --border-color: #404040;
38
+ --accent-color: #8b9ef7;
39
+ --link-color: #8b9ef7;
40
+ --table-stripe: #252525;
41
+ }
42
+
43
+ * {
44
+ margin: 0;
45
+ padding: 0;
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ body {
50
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
51
+ line-height: 1.6;
52
+ color: var(--text-primary);
53
+ background: var(--bg-primary);
54
+ padding: 20px;
55
+ transition: background 0.3s ease, color 0.3s ease;
56
+ }
57
+
58
+ #header {
59
+ display: flex;
60
+ justify-content: space-between;
61
+ align-items: center;
62
+ margin-bottom: 30px;
63
+ padding-bottom: 15px;
64
+ border-bottom: 2px solid var(--border-color);
65
+ }
66
+
67
+ #controls {
68
+ display: flex;
69
+ gap: 10px;
70
+ }
71
+
72
+ button {
73
+ background: var(--accent-color);
74
+ border: none;
75
+ color: white;
76
+ padding: 8px 16px;
77
+ border-radius: 5px;
78
+ cursor: pointer;
79
+ transition: all 0.3s ease;
80
+ font-size: 14px;
81
+ }
82
+
83
+ button:hover {
84
+ opacity: 0.8;
85
+ transform: translateY(-1px);
86
+ }
87
+
88
+ #content {
89
+ display: flex;
90
+ gap: 20px;
91
+ max-width: 1400px;
92
+ margin: 0 auto;
93
+ }
94
+
95
+ #toc-sidebar {
96
+ width: 250px;
97
+ background: var(--bg-secondary);
98
+ padding: 20px;
99
+ border-radius: 8px;
100
+ border: 1px solid var(--border-color);
101
+ position: sticky;
102
+ top: 20px;
103
+ max-height: calc(100vh - 60px);
104
+ overflow-y: auto;
105
+ display: none;
106
+ }
107
+
108
+ #toc-sidebar.visible {
109
+ display: block;
110
+ }
111
+
112
+ #toc-sidebar h3 {
113
+ margin-bottom: 15px;
114
+ color: var(--text-heading);
115
+ font-size: 1.1em;
116
+ }
117
+
118
+ #toc-list {
119
+ list-style: none;
120
+ padding: 0;
121
+ }
122
+
123
+ #toc-list li {
124
+ margin: 8px 0;
125
+ }
126
+
127
+ #toc-list a {
128
+ color: var(--link-color);
129
+ text-decoration: none;
130
+ font-size: 0.9em;
131
+ transition: padding-left 0.2s ease;
132
+ display: block;
133
+ padding: 4px 0;
134
+ }
135
+
136
+ #toc-list a:hover {
137
+ padding-left: 5px;
138
+ text-decoration: underline;
139
+ }
140
+
141
+ #toc-list .toc-h2 { padding-left: 0; }
142
+ #toc-list .toc-h3 { padding-left: 15px; }
143
+ #toc-list .toc-h4 { padding-left: 30px; }
144
+
145
+ #markdown-container {
146
+ flex: 1;
147
+ min-width: 0;
148
+ }
149
+
150
+ #markdown-content {
151
+ max-width: 900px;
152
+ margin: 0 auto;
153
+ }
154
+
155
+ /* Markdown Styling */
156
+ #markdown-content h1 {
157
+ font-size: 2.5em;
158
+ margin: 30px 0 20px;
159
+ padding-bottom: 10px;
160
+ border-bottom: 2px solid var(--border-color);
161
+ color: var(--text-heading);
162
+ }
163
+
164
+ #markdown-content h2 {
165
+ font-size: 2em;
166
+ margin: 25px 0 15px;
167
+ color: var(--text-heading);
168
+ }
169
+
170
+ #markdown-content h3 {
171
+ font-size: 1.5em;
172
+ margin: 20px 0 10px;
173
+ color: var(--text-heading);
174
+ }
175
+
176
+ #markdown-content h4, #markdown-content h5, #markdown-content h6 {
177
+ margin: 15px 0 10px;
178
+ color: var(--text-heading);
179
+ }
180
+
181
+ #markdown-content p {
182
+ margin: 15px 0;
183
+ line-height: 1.7;
184
+ }
185
+
186
+ #markdown-content ul, #markdown-content ol {
187
+ margin: 15px 0;
188
+ padding-left: 30px;
189
+ }
190
+
191
+ #markdown-content li {
192
+ margin: 8px 0;
193
+ line-height: 1.7;
194
+ }
195
+
196
+ #markdown-content code {
197
+ background: var(--bg-code);
198
+ padding: 2px 6px;
199
+ border-radius: 3px;
200
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
201
+ font-size: 0.9em;
202
+ color: var(--text-primary);
203
+ }
204
+
205
+ #markdown-content pre {
206
+ background: var(--bg-code);
207
+ border: 1px solid var(--border-color);
208
+ border-radius: 5px;
209
+ padding: 15px;
210
+ overflow-x: auto;
211
+ margin: 20px 0;
212
+ }
213
+
214
+ #markdown-content pre code {
215
+ background: none;
216
+ padding: 0;
217
+ }
218
+
219
+ #markdown-content blockquote {
220
+ border-left: 4px solid var(--accent-color);
221
+ padding-left: 20px;
222
+ margin: 20px 0;
223
+ color: var(--text-secondary);
224
+ font-style: italic;
225
+ }
226
+
227
+ #markdown-content table {
228
+ border-collapse: collapse;
229
+ width: 100%;
230
+ margin: 20px 0;
231
+ }
232
+
233
+ #markdown-content th, #markdown-content td {
234
+ border: 1px solid var(--border-color);
235
+ padding: 12px;
236
+ text-align: left;
237
+ }
238
+
239
+ #markdown-content th {
240
+ background: var(--bg-code);
241
+ font-weight: 600;
242
+ color: var(--text-heading);
243
+ }
244
+
245
+ #markdown-content tr:nth-child(even) {
246
+ background: var(--table-stripe);
247
+ }
248
+
249
+ #markdown-content a {
250
+ color: var(--link-color);
251
+ text-decoration: none;
252
+ }
253
+
254
+ #markdown-content a:hover {
255
+ text-decoration: underline;
256
+ }
257
+
258
+ #markdown-content hr {
259
+ border: none;
260
+ border-top: 2px solid var(--border-color);
261
+ margin: 30px 0;
262
+ }
263
+
264
+ /* Task lists (GFM) */
265
+ #markdown-content input[type="checkbox"] {
266
+ margin-right: 8px;
267
+ }
268
+
269
+ /* Mermaid diagrams */
270
+ .mermaid {
271
+ background: var(--bg-primary);
272
+ padding: 20px;
273
+ border-radius: 8px;
274
+ margin: 20px 0;
275
+ }
276
+
277
+ #markdown-content img {
278
+ max-width: 100%;
279
+ height: auto;
280
+ display: block;
281
+ margin: 20px auto;
282
+ }
283
+
284
+ .status-message {
285
+ position: fixed;
286
+ bottom: 20px;
287
+ right: 20px;
288
+ background: #333;
289
+ color: white;
290
+ padding: 10px 20px;
291
+ border-radius: 5px;
292
+ opacity: 0;
293
+ transition: opacity 0.3s ease;
294
+ z-index: 2000;
295
+ }
296
+
297
+ .status-message.show {
298
+ opacity: 0.9;
299
+ }
300
+
301
+ /* Responsive Design */
302
+ @media (max-width: 768px) {
303
+ body {
304
+ padding: 10px;
305
+ }
306
+
307
+ #content {
308
+ flex-direction: column;
309
+ }
310
+
311
+ #toc-sidebar {
312
+ width: 100%;
313
+ max-height: 200px;
314
+ position: relative;
315
+ top: 0;
316
+ margin-bottom: 20px;
317
+ }
318
+
319
+ #header {
320
+ flex-direction: column;
321
+ gap: 10px;
322
+ }
323
+
324
+ #controls {
325
+ flex-wrap: wrap;
326
+ width: 100%;
327
+ }
328
+
329
+ button {
330
+ flex: 1;
331
+ min-width: 120px;
332
+ }
333
+ }
334
+
335
+ /* Print Styles */
336
+ @media print {
337
+ #header,
338
+ #toc-sidebar,
339
+ .status-message {
340
+ display: none !important;
341
+ }
342
+
343
+ body {
344
+ background: white;
345
+ color: black;
346
+ padding: 0;
347
+ }
348
+
349
+ #content {
350
+ display: block;
351
+ }
352
+
353
+ #markdown-content {
354
+ max-width: 100%;
355
+ }
356
+
357
+ #markdown-content a {
358
+ color: black;
359
+ text-decoration: underline;
360
+ }
361
+
362
+ #markdown-content pre {
363
+ border: 1px solid #333;
364
+ page-break-inside: avoid;
365
+ }
366
+
367
+ #markdown-content h1,
368
+ #markdown-content h2,
369
+ #markdown-content h3,
370
+ #markdown-content h4,
371
+ #markdown-content h5,
372
+ #markdown-content h6 {
373
+ page-break-after: avoid;
374
+ }
375
+
376
+ #markdown-content table,
377
+ #markdown-content img {
378
+ page-break-inside: avoid;
379
+ }
380
+ }
381
+ </style>
382
+ </head>
383
+ <body>
384
+ <div id="header">
385
+ <div>BBEdit Markdown Preview</div>
386
+ <div id="controls">
387
+ <button onclick="toggleTOC()" title="Toggle Table of Contents" aria-label="Toggle Table of Contents">📑 TOC</button>
388
+ <button onclick="toggleDarkMode()" title="Toggle Dark Mode" aria-label="Toggle Dark Mode">🌓 Theme</button>
389
+ </div>
390
+ </div>
391
+
392
+ <div id="content">
393
+ <div id="toc-sidebar">
394
+ <h3>Table of Contents</h3>
395
+ <ul id="toc-list"></ul>
396
+ </div>
397
+ <div id="markdown-container">
398
+ <div id="markdown-content">
399
+ #DOCUMENT_CONTENT#
400
+ </div>
401
+ </div>
402
+ </div>
403
+
404
+ <div id="status-message" class="status-message"></div>
405
+
406
+ <script>
407
+ // Initialize Mermaid
408
+ mermaid.initialize({
409
+ startOnLoad: false,
410
+ theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'default',
411
+ securityLevel: 'loose'
412
+ });
413
+
414
+ // Initialize on load
415
+ document.addEventListener('DOMContentLoaded', () => {
416
+ loadThemePreference();
417
+ processContent();
418
+ });
419
+
420
+ async function processContent() {
421
+ // Highlight code blocks
422
+ document.querySelectorAll('pre code').forEach((block) => {
423
+ hljs.highlightElement(block);
424
+ });
425
+
426
+ // Render Mermaid diagrams
427
+ const mermaidBlocks = document.querySelectorAll('code.language-mermaid');
428
+ for (let i = 0; i < mermaidBlocks.length; i++) {
429
+ const block = mermaidBlocks[i];
430
+ const code = block.textContent;
431
+ const pre = block.parentElement;
432
+
433
+ try {
434
+ const { svg } = await mermaid.render(`mermaid-${i}`, code);
435
+ const div = document.createElement('div');
436
+ div.className = 'mermaid';
437
+ div.innerHTML = svg;
438
+ pre.replaceWith(div);
439
+ } catch (err) {
440
+ console.error('Mermaid rendering error:', err);
441
+ }
442
+ }
443
+
444
+ // Render math equations with KaTeX
445
+ renderMathInElement(document.getElementById('markdown-content'), {
446
+ delimiters: [
447
+ {left: '$$', right: '$$', display: true},
448
+ {left: '$', right: '$', display: false},
449
+ {left: '\\[', right: '\\]', display: true},
450
+ {left: '\\(', right: '\\)', display: false}
451
+ ],
452
+ throwOnError: false
453
+ });
454
+
455
+ // Add IDs to headings and generate TOC
456
+ addHeadingIds();
457
+ generateTOC();
458
+ }
459
+
460
+ function addHeadingIds() {
461
+ const headings = document.querySelectorAll('#markdown-content h1, #markdown-content h2, #markdown-content h3, #markdown-content h4');
462
+ headings.forEach((heading, index) => {
463
+ if (!heading.id) {
464
+ const text = heading.textContent.trim();
465
+ const id = text.toLowerCase()
466
+ .replace(/[^\w\s-]/g, '')
467
+ .replace(/\s+/g, '-')
468
+ .replace(/--+/g, '-')
469
+ .trim();
470
+ heading.id = id || `heading-${index}`;
471
+ }
472
+ });
473
+ }
474
+
475
+ function generateTOC() {
476
+ const tocList = document.getElementById('toc-list');
477
+ tocList.innerHTML = '';
478
+
479
+ const headings = document.querySelectorAll('#markdown-content h1, #markdown-content h2, #markdown-content h3, #markdown-content h4');
480
+
481
+ if (headings.length === 0) {
482
+ tocList.innerHTML = '<li style="color: var(--text-secondary); font-style: italic;">No headings found</li>';
483
+ return;
484
+ }
485
+
486
+ headings.forEach((heading) => {
487
+ const li = document.createElement('li');
488
+ const a = document.createElement('a');
489
+ a.textContent = heading.textContent;
490
+ a.href = `#${heading.id}`;
491
+ a.className = `toc-${heading.tagName.toLowerCase()}`;
492
+
493
+ a.addEventListener('click', (e) => {
494
+ e.preventDefault();
495
+ heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
496
+ history.pushState(null, null, `#${heading.id}`);
497
+ });
498
+
499
+ li.appendChild(a);
500
+ tocList.appendChild(li);
501
+ });
502
+ }
503
+
504
+ // Dark Mode Functions
505
+ function toggleDarkMode() {
506
+ const html = document.documentElement;
507
+ const currentTheme = html.getAttribute('data-theme');
508
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
509
+
510
+ html.setAttribute('data-theme', newTheme);
511
+ localStorage.setItem('bbedit-markdown-theme', newTheme);
512
+
513
+ // Update highlight.js theme
514
+ const highlightTheme = document.getElementById('highlight-theme');
515
+ if (newTheme === 'dark') {
516
+ highlightTheme.href = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github-dark.min.css';
517
+ mermaid.initialize({ theme: 'dark' });
518
+ } else {
519
+ highlightTheme.href = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css';
520
+ mermaid.initialize({ theme: 'default' });
521
+ }
522
+
523
+ showStatus(`${newTheme === 'dark' ? 'Dark' : 'Light'} mode enabled`, 1500);
524
+ }
525
+
526
+ function loadThemePreference() {
527
+ const savedTheme = localStorage.getItem('bbedit-markdown-theme');
528
+ if (savedTheme) {
529
+ document.documentElement.setAttribute('data-theme', savedTheme);
530
+
531
+ const highlightTheme = document.getElementById('highlight-theme');
532
+ if (savedTheme === 'dark') {
533
+ highlightTheme.href = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github-dark.min.css';
534
+ mermaid.initialize({ theme: 'dark' });
535
+ }
536
+ }
537
+ }
538
+
539
+ // TOC Functions
540
+ function toggleTOC() {
541
+ const tocSidebar = document.getElementById('toc-sidebar');
542
+ tocSidebar.classList.toggle('visible');
543
+
544
+ const isVisible = tocSidebar.classList.contains('visible');
545
+ showStatus(`TOC ${isVisible ? 'shown' : 'hidden'}`, 1000);
546
+ }
547
+
548
+ function showStatus(message, duration = 2000) {
549
+ const statusEl = document.getElementById('status-message');
550
+ statusEl.textContent = message;
551
+ statusEl.classList.add('show');
552
+
553
+ setTimeout(() => {
554
+ statusEl.classList.remove('show');
555
+ }, duration);
556
+ }
557
+ </script>
558
+ </body>
559
+ </html>
Binary file