md-slides 1.0.0 โ 1.0.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 +6 -3
- package/package.json +1 -1
- package/src/renderer.js +19 -3
- package/src/themes.js +2 -3
- package/test-output/index.html +733 -0
package/README.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# md-slides
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/md-slides)
|
|
4
|
+
[](https://github.com/dprrwt/md-slides/blob/master/LICENSE)
|
|
2
5
|
|
|
3
6
|
> Convert Markdown to beautiful presentation slides. Zero config, developer-friendly.
|
|
4
7
|
|
|
@@ -20,13 +23,13 @@ Write your presentation in **Markdown**, present it in the **browser**, deploy i
|
|
|
20
23
|
## ๐ฆ Install
|
|
21
24
|
|
|
22
25
|
```bash
|
|
23
|
-
npm install -g
|
|
26
|
+
npm install -g md-slides
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
Or use directly:
|
|
27
30
|
|
|
28
31
|
```bash
|
|
29
|
-
npx
|
|
32
|
+
npx md-slides init my-talk
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
## ๐ Quick Start
|
package/package.json
CHANGED
package/src/renderer.js
CHANGED
|
@@ -28,9 +28,25 @@ function getScript(slideCount, liveReload = false) {
|
|
|
28
28
|
|
|
29
29
|
function goTo(n) {
|
|
30
30
|
if (n < 0 || n >= total) return;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
var goingForward = n > current;
|
|
32
|
+
|
|
33
|
+
// Remove active from old slide
|
|
34
|
+
slides[current].classList.remove('active', 'prev');
|
|
35
|
+
if (goingForward) {
|
|
36
|
+
slides[current].classList.add('prev');
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
current = n;
|
|
40
|
+
|
|
41
|
+
// For backward nav, ensure slide starts from left position
|
|
42
|
+
if (!goingForward) {
|
|
43
|
+
slides[current].classList.add('prev');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Force reflow to re-trigger CSS animations
|
|
47
|
+
void slides[current].offsetWidth;
|
|
48
|
+
|
|
49
|
+
// Activate new slide
|
|
34
50
|
slides[current].classList.remove('prev');
|
|
35
51
|
slides[current].classList.add('active');
|
|
36
52
|
|
|
@@ -166,7 +182,7 @@ export function renderHTML(slidesData, options = {}) {
|
|
|
166
182
|
if (slide.directives.class) {
|
|
167
183
|
classes.push(slide.directives.class);
|
|
168
184
|
}
|
|
169
|
-
|
|
185
|
+
// active class is set by JS goTo() on init โ not in HTML
|
|
170
186
|
|
|
171
187
|
let style = '';
|
|
172
188
|
if (slide.directives.background) {
|
package/src/themes.js
CHANGED
|
@@ -426,7 +426,7 @@ export const baseStyles = `
|
|
|
426
426
|
.slide.active h1,
|
|
427
427
|
.slide.active h2,
|
|
428
428
|
.slide.active h3 {
|
|
429
|
-
animation: fadeInUp 0.6s ease
|
|
429
|
+
animation: fadeInUp 0.6s ease both;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
.slide.active p,
|
|
@@ -435,8 +435,7 @@ export const baseStyles = `
|
|
|
435
435
|
.slide.active pre,
|
|
436
436
|
.slide.active blockquote,
|
|
437
437
|
.slide.active table {
|
|
438
|
-
animation: fadeInUp 0.6s ease 0.15s
|
|
439
|
-
opacity: 0;
|
|
438
|
+
animation: fadeInUp 0.6s ease 0.15s both;
|
|
440
439
|
}
|
|
441
440
|
`;
|
|
442
441
|
|
|
@@ -0,0 +1,733 @@
|
|
|
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>md-slides Demo</title>
|
|
7
|
+
<meta name="author" content="Deepankar Rawat">
|
|
8
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css">
|
|
9
|
+
<style>
|
|
10
|
+
|
|
11
|
+
:root {
|
|
12
|
+
--bg: #0a0a0f;
|
|
13
|
+
--bg-slide: #12121a;
|
|
14
|
+
--fg: #e8e8f0;
|
|
15
|
+
--fg-dim: #8888aa;
|
|
16
|
+
--accent: #6c63ff;
|
|
17
|
+
--accent-glow: rgba(108, 99, 255, 0.3);
|
|
18
|
+
--code-bg: #1a1a2e;
|
|
19
|
+
--code-border: #2a2a4a;
|
|
20
|
+
--blockquote-border: #6c63ff;
|
|
21
|
+
--link: #8b83ff;
|
|
22
|
+
--font-heading: 'Inter', system-ui, sans-serif;
|
|
23
|
+
--font-body: 'Inter', system-ui, sans-serif;
|
|
24
|
+
--font-code: 'JetBrains Mono', 'Fira Code', monospace;
|
|
25
|
+
--radius: 12px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
|
30
|
+
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap');
|
|
31
|
+
|
|
32
|
+
*, *::before, *::after {
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
html, body {
|
|
39
|
+
height: 100%;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
background: var(--bg);
|
|
42
|
+
color: var(--fg);
|
|
43
|
+
font-family: var(--font-body);
|
|
44
|
+
-webkit-font-smoothing: antialiased;
|
|
45
|
+
-moz-osx-font-smoothing: grayscale;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Slide Container */
|
|
49
|
+
.deck {
|
|
50
|
+
position: relative;
|
|
51
|
+
width: 100vw;
|
|
52
|
+
height: 100vh;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.slide {
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: 0;
|
|
58
|
+
left: 0;
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 100%;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
padding: 8vh 10vw;
|
|
65
|
+
background: var(--bg-slide);
|
|
66
|
+
opacity: 0;
|
|
67
|
+
transform: translateX(40px);
|
|
68
|
+
transition: opacity 0.5s ease, transform 0.5s ease;
|
|
69
|
+
pointer-events: none;
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.slide.active {
|
|
74
|
+
opacity: 1;
|
|
75
|
+
transform: translateX(0);
|
|
76
|
+
pointer-events: auto;
|
|
77
|
+
z-index: 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.slide.prev {
|
|
81
|
+
opacity: 0;
|
|
82
|
+
transform: translateX(-40px);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Layouts */
|
|
86
|
+
.slide.layout-center {
|
|
87
|
+
align-items: center;
|
|
88
|
+
text-align: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.slide.layout-title {
|
|
93
|
+
justify-content: center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.slide.layout-title h1 {
|
|
97
|
+
font-size: clamp(2.5rem, 6vw, 5rem);
|
|
98
|
+
margin-bottom: 1rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.slide.layout-quote {
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
text-align: center;
|
|
105
|
+
padding: 10vh 15vw;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.slide.layout-code {
|
|
109
|
+
justify-content: center;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.slide.layout-image {
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.slide.layout-image img {
|
|
118
|
+
max-width: 80%;
|
|
119
|
+
max-height: 70vh;
|
|
120
|
+
border-radius: var(--radius);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Typography */
|
|
124
|
+
h1 {
|
|
125
|
+
font-family: var(--font-heading);
|
|
126
|
+
font-size: clamp(2rem, 5vw, 4rem);
|
|
127
|
+
font-weight: 800;
|
|
128
|
+
letter-spacing: -0.03em;
|
|
129
|
+
line-height: 1.1;
|
|
130
|
+
margin-bottom: 0.8em;
|
|
131
|
+
background: linear-gradient(135deg, var(--fg), var(--accent));
|
|
132
|
+
-webkit-background-clip: text;
|
|
133
|
+
-webkit-text-fill-color: transparent;
|
|
134
|
+
background-clip: text;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
h2 {
|
|
138
|
+
font-family: var(--font-heading);
|
|
139
|
+
font-size: clamp(1.5rem, 3vw, 2.5rem);
|
|
140
|
+
font-weight: 700;
|
|
141
|
+
letter-spacing: -0.02em;
|
|
142
|
+
line-height: 1.2;
|
|
143
|
+
margin-bottom: 0.6em;
|
|
144
|
+
color: var(--accent);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
h3 {
|
|
148
|
+
font-family: var(--font-heading);
|
|
149
|
+
font-size: clamp(1.2rem, 2vw, 1.8rem);
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
margin-bottom: 0.5em;
|
|
152
|
+
color: var(--fg);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
p {
|
|
156
|
+
font-size: clamp(1rem, 1.8vw, 1.4rem);
|
|
157
|
+
line-height: 1.7;
|
|
158
|
+
margin-bottom: 0.8em;
|
|
159
|
+
color: var(--fg-dim);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
strong {
|
|
163
|
+
color: var(--fg);
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
em {
|
|
168
|
+
color: var(--accent);
|
|
169
|
+
font-style: italic;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
a {
|
|
173
|
+
color: var(--link);
|
|
174
|
+
text-decoration: none;
|
|
175
|
+
border-bottom: 1px solid transparent;
|
|
176
|
+
transition: border-color 0.2s;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
a:hover {
|
|
180
|
+
border-bottom-color: var(--link);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Lists */
|
|
184
|
+
ul, ol {
|
|
185
|
+
font-size: clamp(1rem, 1.6vw, 1.3rem);
|
|
186
|
+
line-height: 1.8;
|
|
187
|
+
padding-left: 1.5em;
|
|
188
|
+
margin-bottom: 1em;
|
|
189
|
+
color: var(--fg-dim);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
li {
|
|
193
|
+
margin-bottom: 0.4em;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
li::marker {
|
|
197
|
+
color: var(--accent);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Code */
|
|
201
|
+
code {
|
|
202
|
+
font-family: var(--font-code);
|
|
203
|
+
font-size: 0.85em;
|
|
204
|
+
background: var(--code-bg);
|
|
205
|
+
border: 1px solid var(--code-border);
|
|
206
|
+
padding: 0.15em 0.4em;
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
color: var(--accent);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pre {
|
|
212
|
+
margin: 1em 0;
|
|
213
|
+
border-radius: var(--radius);
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
border: 1px solid var(--code-border);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
pre code {
|
|
219
|
+
display: block;
|
|
220
|
+
padding: 1.5em 2em;
|
|
221
|
+
background: var(--code-bg);
|
|
222
|
+
border: none;
|
|
223
|
+
font-size: clamp(0.8rem, 1.2vw, 1rem);
|
|
224
|
+
line-height: 1.6;
|
|
225
|
+
overflow-x: auto;
|
|
226
|
+
color: var(--fg);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* Blockquotes */
|
|
230
|
+
blockquote {
|
|
231
|
+
border-left: 4px solid var(--blockquote-border);
|
|
232
|
+
padding: 1em 1.5em;
|
|
233
|
+
margin: 1em 0;
|
|
234
|
+
background: var(--accent-glow);
|
|
235
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
blockquote p {
|
|
239
|
+
font-size: clamp(1.2rem, 2vw, 1.8rem);
|
|
240
|
+
font-style: italic;
|
|
241
|
+
color: var(--fg);
|
|
242
|
+
margin-bottom: 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Tables */
|
|
246
|
+
table {
|
|
247
|
+
width: 100%;
|
|
248
|
+
border-collapse: collapse;
|
|
249
|
+
margin: 1em 0;
|
|
250
|
+
font-size: clamp(0.85rem, 1.3vw, 1.1rem);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
th, td {
|
|
254
|
+
padding: 0.75em 1em;
|
|
255
|
+
text-align: left;
|
|
256
|
+
border-bottom: 1px solid var(--code-border);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
th {
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
color: var(--accent);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
td {
|
|
265
|
+
color: var(--fg-dim);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Images */
|
|
269
|
+
img {
|
|
270
|
+
max-width: 100%;
|
|
271
|
+
border-radius: var(--radius);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Progress bar */
|
|
275
|
+
.progress {
|
|
276
|
+
position: fixed;
|
|
277
|
+
bottom: 0;
|
|
278
|
+
left: 0;
|
|
279
|
+
height: 3px;
|
|
280
|
+
background: var(--accent);
|
|
281
|
+
transition: width 0.4s ease;
|
|
282
|
+
z-index: 100;
|
|
283
|
+
box-shadow: 0 0 10px var(--accent-glow);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Slide number */
|
|
287
|
+
.slide-number {
|
|
288
|
+
position: fixed;
|
|
289
|
+
bottom: 16px;
|
|
290
|
+
right: 24px;
|
|
291
|
+
font-family: var(--font-code);
|
|
292
|
+
font-size: 0.8rem;
|
|
293
|
+
color: var(--fg-dim);
|
|
294
|
+
opacity: 0.5;
|
|
295
|
+
z-index: 100;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* Presenter notes toggle */
|
|
299
|
+
.notes-overlay {
|
|
300
|
+
display: none;
|
|
301
|
+
position: fixed;
|
|
302
|
+
bottom: 0;
|
|
303
|
+
left: 0;
|
|
304
|
+
right: 0;
|
|
305
|
+
background: rgba(0,0,0,0.9);
|
|
306
|
+
color: #ccc;
|
|
307
|
+
padding: 1.5em 2em;
|
|
308
|
+
font-size: 0.9rem;
|
|
309
|
+
line-height: 1.6;
|
|
310
|
+
z-index: 200;
|
|
311
|
+
max-height: 30vh;
|
|
312
|
+
overflow-y: auto;
|
|
313
|
+
border-top: 2px solid var(--accent);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.notes-overlay.visible {
|
|
317
|
+
display: block;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Keyboard hint */
|
|
321
|
+
.hint {
|
|
322
|
+
position: fixed;
|
|
323
|
+
bottom: 40px;
|
|
324
|
+
left: 50%;
|
|
325
|
+
transform: translateX(-50%);
|
|
326
|
+
font-family: var(--font-code);
|
|
327
|
+
font-size: 0.75rem;
|
|
328
|
+
color: var(--fg-dim);
|
|
329
|
+
opacity: 0;
|
|
330
|
+
transition: opacity 0.5s;
|
|
331
|
+
z-index: 100;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.hint.visible {
|
|
335
|
+
opacity: 0.4;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Print / PDF */
|
|
339
|
+
@media print {
|
|
340
|
+
.slide {
|
|
341
|
+
position: relative;
|
|
342
|
+
opacity: 1;
|
|
343
|
+
transform: none;
|
|
344
|
+
page-break-after: always;
|
|
345
|
+
min-height: 100vh;
|
|
346
|
+
}
|
|
347
|
+
.progress, .slide-number, .hint, .notes-overlay {
|
|
348
|
+
display: none;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* Animations */
|
|
353
|
+
@keyframes fadeInUp {
|
|
354
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
355
|
+
to { opacity: 1; transform: translateY(0); }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.slide.active h1,
|
|
359
|
+
.slide.active h2,
|
|
360
|
+
.slide.active h3 {
|
|
361
|
+
animation: fadeInUp 0.6s ease both;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.slide.active p,
|
|
365
|
+
.slide.active ul,
|
|
366
|
+
.slide.active ol,
|
|
367
|
+
.slide.active pre,
|
|
368
|
+
.slide.active blockquote,
|
|
369
|
+
.slide.active table {
|
|
370
|
+
animation: fadeInUp 0.6s ease 0.15s both;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
</style>
|
|
374
|
+
</head>
|
|
375
|
+
<body>
|
|
376
|
+
<div class="deck">
|
|
377
|
+
<section class="slide layout-title">
|
|
378
|
+
<div class="slide-content">
|
|
379
|
+
<h1>md-slides โจ</h1>
|
|
380
|
+
<p>Markdown โ Beautiful Slides</p>
|
|
381
|
+
<p><em>Zero config. Developer-friendly.</em></p>
|
|
382
|
+
|
|
383
|
+
</div>
|
|
384
|
+
</section>
|
|
385
|
+
<section class="slide layout-default" data-notes="This tool exists because making slides should be as easy as writing markdown.">
|
|
386
|
+
<div class="slide-content">
|
|
387
|
+
<h2>Why md-slides?</h2>
|
|
388
|
+
<ul>
|
|
389
|
+
<li><strong>Write in Markdown</strong> โ your editor, your flow</li>
|
|
390
|
+
<li><strong>4 themes</strong> โ Dark, Light, Minimal, Neon</li>
|
|
391
|
+
<li><strong>Live reload</strong> โ see changes instantly</li>
|
|
392
|
+
<li><strong>Single HTML output</strong> โ deploy anywhere</li>
|
|
393
|
+
<li><strong>Speaker notes</strong> โ press <code>S</code> to toggle</li>
|
|
394
|
+
</ul>
|
|
395
|
+
|
|
396
|
+
</div>
|
|
397
|
+
</section>
|
|
398
|
+
<section class="slide layout-code">
|
|
399
|
+
<div class="slide-content">
|
|
400
|
+
<h2>Getting Started</h2>
|
|
401
|
+
<pre><code class="language-bash"># Install globally
|
|
402
|
+
npm install -g @dprrwt/slides
|
|
403
|
+
|
|
404
|
+
# Create a new presentation
|
|
405
|
+
slides init my-talk
|
|
406
|
+
|
|
407
|
+
# Start editing and previewing
|
|
408
|
+
cd my-talk
|
|
409
|
+
slides preview
|
|
410
|
+
</code></pre>
|
|
411
|
+
|
|
412
|
+
</div>
|
|
413
|
+
</section>
|
|
414
|
+
<section class="slide layout-code">
|
|
415
|
+
<div class="slide-content">
|
|
416
|
+
<h2>Slide Syntax</h2>
|
|
417
|
+
<p>Separate slides with <code>---</code> on its own line:</p>
|
|
418
|
+
<pre><code class="language-markdown"># Title Slide
|
|
419
|
+
|
|
420
|
+
Content here.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Next Slide
|
|
425
|
+
|
|
426
|
+
More content.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
> A quote slide.
|
|
431
|
+
</code></pre>
|
|
432
|
+
|
|
433
|
+
</div>
|
|
434
|
+
</section>
|
|
435
|
+
<section class="slide layout-code">
|
|
436
|
+
<div class="slide-content">
|
|
437
|
+
<h2>Code Highlighting</h2>
|
|
438
|
+
<p>190+ languages supported out of the box:</p>
|
|
439
|
+
<pre><code class="language-typescript">interface Slide {
|
|
440
|
+
html: string;
|
|
441
|
+
notes: string;
|
|
442
|
+
layout: 'title' | 'center' | 'code' | 'quote';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function present(slides: Slide[]): void {
|
|
446
|
+
slides.forEach((slide, i) => {
|
|
447
|
+
render(slide, { index: i, total: slides.length });
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
</code></pre>
|
|
451
|
+
|
|
452
|
+
</div>
|
|
453
|
+
</section>
|
|
454
|
+
<section class="slide layout-default">
|
|
455
|
+
<div class="slide-content">
|
|
456
|
+
<h2>Smart Layouts</h2>
|
|
457
|
+
<p>Slides auto-detect their layout:</p>
|
|
458
|
+
<table>
|
|
459
|
+
<thead>
|
|
460
|
+
<tr>
|
|
461
|
+
<th>Content</th>
|
|
462
|
+
<th>Layout</th>
|
|
463
|
+
</tr>
|
|
464
|
+
</thead>
|
|
465
|
+
<tbody><tr>
|
|
466
|
+
<td>Just a heading</td>
|
|
467
|
+
<td><strong>Center</strong></td>
|
|
468
|
+
</tr>
|
|
469
|
+
<tr>
|
|
470
|
+
<td>H1 with content</td>
|
|
471
|
+
<td><strong>Title</strong></td>
|
|
472
|
+
</tr>
|
|
473
|
+
<tr>
|
|
474
|
+
<td>Blockquote</td>
|
|
475
|
+
<td><strong>Quote</strong></td>
|
|
476
|
+
</tr>
|
|
477
|
+
<tr>
|
|
478
|
+
<td>Code block</td>
|
|
479
|
+
<td><strong>Code</strong></td>
|
|
480
|
+
</tr>
|
|
481
|
+
<tr>
|
|
482
|
+
<td>Everything else</td>
|
|
483
|
+
<td><strong>Default</strong></td>
|
|
484
|
+
</tr>
|
|
485
|
+
</tbody></table>
|
|
486
|
+
|
|
487
|
+
</div>
|
|
488
|
+
</section>
|
|
489
|
+
<section class="slide layout-quote" data-notes="This is a quote slide โ detected automatically from the blockquote.">
|
|
490
|
+
<div class="slide-content">
|
|
491
|
+
<blockquote>
|
|
492
|
+
<p>"The best presentations are written, not designed."</p>
|
|
493
|
+
</blockquote>
|
|
494
|
+
|
|
495
|
+
</div>
|
|
496
|
+
</section>
|
|
497
|
+
<section class="slide layout-code">
|
|
498
|
+
<div class="slide-content">
|
|
499
|
+
<h2>Themes</h2>
|
|
500
|
+
<p>Set via frontmatter or CLI:</p>
|
|
501
|
+
<pre><code class="language-yaml">---
|
|
502
|
+
theme: neon
|
|
503
|
+
---
|
|
504
|
+
</code></pre>
|
|
505
|
+
<pre><code class="language-bash">slides build --theme light
|
|
506
|
+
slides preview --theme minimal
|
|
507
|
+
</code></pre>
|
|
508
|
+
<p><strong>Built-in:</strong> <code>dark</code> ยท <code>light</code> ยท <code>minimal</code> ยท <code>neon</code></p>
|
|
509
|
+
|
|
510
|
+
</div>
|
|
511
|
+
</section>
|
|
512
|
+
<section class="slide layout-code" data-notes="Only you can see this. Press S.">
|
|
513
|
+
<div class="slide-content">
|
|
514
|
+
<h2>Speaker Notes</h2>
|
|
515
|
+
<p>Add notes with HTML comments:</p>
|
|
516
|
+
<pre><code class="language-markdown">## My Slide
|
|
517
|
+
|
|
518
|
+
Content visible to audience.
|
|
519
|
+
|
|
520
|
+
</code></pre>
|
|
521
|
+
<p>Press <strong>S</strong> now to see the notes panel โ</p>
|
|
522
|
+
<!-- notes: ๐ You found the notes! These are only visible to the presenter. Use them for talking points, reminders, or timing cues. -->
|
|
523
|
+
</div>
|
|
524
|
+
</section>
|
|
525
|
+
<section class="slide layout-default">
|
|
526
|
+
<div class="slide-content">
|
|
527
|
+
<h2>Navigation</h2>
|
|
528
|
+
<table>
|
|
529
|
+
<thead>
|
|
530
|
+
<tr>
|
|
531
|
+
<th>Key</th>
|
|
532
|
+
<th>Action</th>
|
|
533
|
+
</tr>
|
|
534
|
+
</thead>
|
|
535
|
+
<tbody><tr>
|
|
536
|
+
<td><code>โ</code> <code>โ</code> <code>Space</code></td>
|
|
537
|
+
<td>Next slide</td>
|
|
538
|
+
</tr>
|
|
539
|
+
<tr>
|
|
540
|
+
<td><code>โ</code> <code>โ</code></td>
|
|
541
|
+
<td>Previous slide</td>
|
|
542
|
+
</tr>
|
|
543
|
+
<tr>
|
|
544
|
+
<td><code>S</code></td>
|
|
545
|
+
<td>Speaker notes</td>
|
|
546
|
+
</tr>
|
|
547
|
+
<tr>
|
|
548
|
+
<td><code>F</code></td>
|
|
549
|
+
<td>Fullscreen</td>
|
|
550
|
+
</tr>
|
|
551
|
+
<tr>
|
|
552
|
+
<td><code>Home</code> / <code>End</code></td>
|
|
553
|
+
<td>First / Last</td>
|
|
554
|
+
</tr>
|
|
555
|
+
</tbody></table>
|
|
556
|
+
<p><strong>Touch:</strong> Swipe left/right</p>
|
|
557
|
+
<p><strong>Click:</strong> Right half โ next, Left half โ prev</p>
|
|
558
|
+
|
|
559
|
+
</div>
|
|
560
|
+
</section>
|
|
561
|
+
<section class="slide layout-code">
|
|
562
|
+
<div class="slide-content">
|
|
563
|
+
<h2>Deploy Anywhere</h2>
|
|
564
|
+
<p>Output is a <strong>single <code>index.html</code></strong> file:</p>
|
|
565
|
+
<pre><code class="language-bash"># Build
|
|
566
|
+
slides build
|
|
567
|
+
|
|
568
|
+
# Deploy to GitHub Pages
|
|
569
|
+
cd dist
|
|
570
|
+
git init && git add . && git commit -m "slides"
|
|
571
|
+
git push origin gh-pages
|
|
572
|
+
</code></pre>
|
|
573
|
+
<p>Or just email the HTML file. It's self-contained.</p>
|
|
574
|
+
|
|
575
|
+
</div>
|
|
576
|
+
</section>
|
|
577
|
+
<section class="slide layout-title">
|
|
578
|
+
<div class="slide-content">
|
|
579
|
+
<h1>Ship It ๐</h1>
|
|
580
|
+
<pre><code class="language-bash">npm install -g @dprrwt/slides
|
|
581
|
+
</code></pre>
|
|
582
|
+
<p><strong>GitHub:</strong> <a href="https://github.com/dprrwt/md-slides">dprrwt/md-slides</a></p>
|
|
583
|
+
<p><strong>Author:</strong> <a href="https://dprrwt.me">dprrwt.me</a></p>
|
|
584
|
+
|
|
585
|
+
</div>
|
|
586
|
+
</section>
|
|
587
|
+
</div>
|
|
588
|
+
|
|
589
|
+
<div class="progress" style="width: 0%"></div>
|
|
590
|
+
<div class="slide-number">1 / 12</div>
|
|
591
|
+
<div class="notes-overlay"></div>
|
|
592
|
+
<div class="hint">โ โ navigate ยท S notes ยท F fullscreen</div>
|
|
593
|
+
|
|
594
|
+
<script>
|
|
595
|
+
|
|
596
|
+
(function() {
|
|
597
|
+
let current = 0;
|
|
598
|
+
const total = 12;
|
|
599
|
+
const slides = document.querySelectorAll('.slide');
|
|
600
|
+
const progress = document.querySelector('.progress');
|
|
601
|
+
const slideNum = document.querySelector('.slide-number');
|
|
602
|
+
const notesOverlay = document.querySelector('.notes-overlay');
|
|
603
|
+
const hint = document.querySelector('.hint');
|
|
604
|
+
let notesVisible = false;
|
|
605
|
+
|
|
606
|
+
// Parse hash
|
|
607
|
+
const hash = parseInt(window.location.hash.replace('#', ''), 10);
|
|
608
|
+
if (!isNaN(hash) && hash >= 0 && hash < total) {
|
|
609
|
+
current = hash;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function goTo(n) {
|
|
613
|
+
if (n < 0 || n >= total) return;
|
|
614
|
+
var goingForward = n > current;
|
|
615
|
+
|
|
616
|
+
// Remove active from old slide
|
|
617
|
+
slides[current].classList.remove('active', 'prev');
|
|
618
|
+
if (goingForward) {
|
|
619
|
+
slides[current].classList.add('prev');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
current = n;
|
|
623
|
+
|
|
624
|
+
// For backward nav, ensure slide starts from left position
|
|
625
|
+
if (!goingForward) {
|
|
626
|
+
slides[current].classList.add('prev');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Force reflow to re-trigger CSS animations
|
|
630
|
+
void slides[current].offsetWidth;
|
|
631
|
+
|
|
632
|
+
// Activate new slide
|
|
633
|
+
slides[current].classList.remove('prev');
|
|
634
|
+
slides[current].classList.add('active');
|
|
635
|
+
|
|
636
|
+
progress.style.width = ((current + 1) / total * 100) + '%';
|
|
637
|
+
slideNum.textContent = (current + 1) + ' / ' + total;
|
|
638
|
+
window.location.hash = current;
|
|
639
|
+
|
|
640
|
+
// Update notes
|
|
641
|
+
const noteText = slides[current].dataset.notes || '';
|
|
642
|
+
notesOverlay.textContent = noteText || '(no notes for this slide)';
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function next() { goTo(current + 1); }
|
|
646
|
+
function prev() { goTo(current - 1); }
|
|
647
|
+
function first() { goTo(0); }
|
|
648
|
+
function last() { goTo(total - 1); }
|
|
649
|
+
|
|
650
|
+
// Keyboard
|
|
651
|
+
document.addEventListener('keydown', function(e) {
|
|
652
|
+
hint.classList.remove('visible');
|
|
653
|
+
switch(e.key) {
|
|
654
|
+
case 'ArrowRight':
|
|
655
|
+
case 'ArrowDown':
|
|
656
|
+
case ' ':
|
|
657
|
+
case 'l':
|
|
658
|
+
case 'j':
|
|
659
|
+
e.preventDefault();
|
|
660
|
+
next();
|
|
661
|
+
break;
|
|
662
|
+
case 'ArrowLeft':
|
|
663
|
+
case 'ArrowUp':
|
|
664
|
+
case 'h':
|
|
665
|
+
case 'k':
|
|
666
|
+
e.preventDefault();
|
|
667
|
+
prev();
|
|
668
|
+
break;
|
|
669
|
+
case 'Home':
|
|
670
|
+
e.preventDefault();
|
|
671
|
+
first();
|
|
672
|
+
break;
|
|
673
|
+
case 'End':
|
|
674
|
+
e.preventDefault();
|
|
675
|
+
last();
|
|
676
|
+
break;
|
|
677
|
+
case 's':
|
|
678
|
+
case 'n':
|
|
679
|
+
notesVisible = !notesVisible;
|
|
680
|
+
notesOverlay.classList.toggle('visible', notesVisible);
|
|
681
|
+
break;
|
|
682
|
+
case 'f':
|
|
683
|
+
if (document.fullscreenElement) {
|
|
684
|
+
document.exitFullscreen();
|
|
685
|
+
} else {
|
|
686
|
+
document.documentElement.requestFullscreen();
|
|
687
|
+
}
|
|
688
|
+
break;
|
|
689
|
+
case 'Escape':
|
|
690
|
+
notesOverlay.classList.remove('visible');
|
|
691
|
+
notesVisible = false;
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
// Touch / swipe
|
|
697
|
+
let touchStartX = 0;
|
|
698
|
+
let touchStartY = 0;
|
|
699
|
+
document.addEventListener('touchstart', function(e) {
|
|
700
|
+
touchStartX = e.touches[0].clientX;
|
|
701
|
+
touchStartY = e.touches[0].clientY;
|
|
702
|
+
});
|
|
703
|
+
document.addEventListener('touchend', function(e) {
|
|
704
|
+
const dx = e.changedTouches[0].clientX - touchStartX;
|
|
705
|
+
const dy = e.changedTouches[0].clientY - touchStartY;
|
|
706
|
+
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 50) {
|
|
707
|
+
dx > 0 ? prev() : next();
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Click (right half = next, left half = prev)
|
|
712
|
+
document.addEventListener('click', function(e) {
|
|
713
|
+
if (e.target.tagName === 'A' || e.target.tagName === 'BUTTON') return;
|
|
714
|
+
if (e.clientX > window.innerWidth / 2) {
|
|
715
|
+
next();
|
|
716
|
+
} else {
|
|
717
|
+
prev();
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// Init
|
|
722
|
+
goTo(current);
|
|
723
|
+
|
|
724
|
+
// Show hint briefly
|
|
725
|
+
setTimeout(function() { hint.classList.add('visible'); }, 1000);
|
|
726
|
+
setTimeout(function() { hint.classList.remove('visible'); }, 5000);
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
})();
|
|
730
|
+
|
|
731
|
+
</script>
|
|
732
|
+
</body>
|
|
733
|
+
</html>
|