deckrun 1.4.0 → 1.6.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.
- package/README.md +205 -32
- package/dist/editor-content.js +85 -0
- package/dist/editor.js +401 -23
- package/dist/fragments.js +71 -0
- package/dist/generate.js +1032 -37
- package/dist/index.js +186 -20
- package/dist/lint.js +218 -0
- package/dist/parser.js +85 -0
- package/dist/pdf.js +5 -1
- package/dist/presentation-options.js +287 -0
- package/dist/preview.js +45 -7
- package/dist/rich-content.js +170 -0
- package/dist/themes.js +2 -0
- package/package.json +5 -2
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
export const DEFAULT_TEMPLATE = "classic";
|
|
2
|
+
export const DEFAULT_TRANSITION = "slide";
|
|
3
|
+
export const TEMPLATE_IDS = [
|
|
4
|
+
"classic",
|
|
5
|
+
"minimal",
|
|
6
|
+
"editorial",
|
|
7
|
+
"spotlight",
|
|
8
|
+
];
|
|
9
|
+
export const TRANSITION_IDS = [
|
|
10
|
+
"slide",
|
|
11
|
+
"fade",
|
|
12
|
+
"zoom",
|
|
13
|
+
"lift",
|
|
14
|
+
"none",
|
|
15
|
+
];
|
|
16
|
+
export const TEMPLATE_SPECS = {
|
|
17
|
+
classic: {
|
|
18
|
+
label: "Classic",
|
|
19
|
+
blurb: "The original balanced deckrun layout",
|
|
20
|
+
},
|
|
21
|
+
minimal: {
|
|
22
|
+
label: "Minimal",
|
|
23
|
+
blurb: "Quiet surfaces, wider margins, fewer decorative treatments",
|
|
24
|
+
},
|
|
25
|
+
editorial: {
|
|
26
|
+
label: "Editorial",
|
|
27
|
+
blurb: "Strong rules and magazine-like reading rhythm",
|
|
28
|
+
},
|
|
29
|
+
spotlight: {
|
|
30
|
+
label: "Spotlight",
|
|
31
|
+
blurb: "Centered, high-impact composition for concise keynote slides",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export const TRANSITION_SPECS = {
|
|
35
|
+
slide: {
|
|
36
|
+
label: "Slide",
|
|
37
|
+
blurb: "Horizontal sliding transition between slides",
|
|
38
|
+
},
|
|
39
|
+
fade: {
|
|
40
|
+
label: "Fade",
|
|
41
|
+
blurb: "Cross-fade between slides",
|
|
42
|
+
},
|
|
43
|
+
zoom: {
|
|
44
|
+
label: "Zoom",
|
|
45
|
+
blurb: "Scale up and down between slides",
|
|
46
|
+
},
|
|
47
|
+
lift: {
|
|
48
|
+
label: "Lift",
|
|
49
|
+
blurb: "Vertical rising transition between slides",
|
|
50
|
+
},
|
|
51
|
+
none: {
|
|
52
|
+
label: "None",
|
|
53
|
+
blurb: "Instant cut between slides",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
export function findTemplate(input) {
|
|
57
|
+
if (!input)
|
|
58
|
+
return null;
|
|
59
|
+
const key = String(input).trim().toLowerCase();
|
|
60
|
+
for (const id of TEMPLATE_IDS) {
|
|
61
|
+
if (id === key)
|
|
62
|
+
return id;
|
|
63
|
+
if (TEMPLATE_SPECS[id].label.toLowerCase() === key)
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
export function resolveTemplateName(input) {
|
|
69
|
+
return findTemplate(input) ?? DEFAULT_TEMPLATE;
|
|
70
|
+
}
|
|
71
|
+
export function templateSummaries() {
|
|
72
|
+
return TEMPLATE_IDS.map((id) => ({
|
|
73
|
+
id,
|
|
74
|
+
label: TEMPLATE_SPECS[id].label,
|
|
75
|
+
blurb: TEMPLATE_SPECS[id].blurb,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
export function templateListing() {
|
|
79
|
+
const pad = Math.max(...TEMPLATE_IDS.map((id) => id.length));
|
|
80
|
+
return TEMPLATE_IDS.map((id) => {
|
|
81
|
+
const s = TEMPLATE_SPECS[id];
|
|
82
|
+
return `${id.padEnd(pad)} ${s.label.padEnd(10)} ${s.blurb}`;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export function findTransition(input) {
|
|
86
|
+
if (!input)
|
|
87
|
+
return null;
|
|
88
|
+
const key = String(input).trim().toLowerCase();
|
|
89
|
+
for (const id of TRANSITION_IDS) {
|
|
90
|
+
if (id === key)
|
|
91
|
+
return id;
|
|
92
|
+
if (TRANSITION_SPECS[id].label.toLowerCase() === key)
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
export function resolveTransitionName(input) {
|
|
98
|
+
return findTransition(input) ?? DEFAULT_TRANSITION;
|
|
99
|
+
}
|
|
100
|
+
export function transitionSummaries() {
|
|
101
|
+
return TRANSITION_IDS.map((id) => ({
|
|
102
|
+
id,
|
|
103
|
+
label: TRANSITION_SPECS[id].label,
|
|
104
|
+
blurb: TRANSITION_SPECS[id].blurb,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
export function transitionListing() {
|
|
108
|
+
const pad = Math.max(...TRANSITION_IDS.map((id) => id.length));
|
|
109
|
+
return TRANSITION_IDS.map((id) => {
|
|
110
|
+
const s = TRANSITION_SPECS[id];
|
|
111
|
+
return `${id.padEnd(pad)} ${s.label.padEnd(8)} ${s.blurb}`;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export const TEMPLATE_CSS = `/* ── Templates ──────────────────────────────────────────────────────────── */
|
|
115
|
+
|
|
116
|
+
/* Minimal */
|
|
117
|
+
:root[data-template="minimal"] {
|
|
118
|
+
--slide-pad-x: 10vw;
|
|
119
|
+
--slide-pad-y: 8vh;
|
|
120
|
+
}
|
|
121
|
+
:root[data-template="minimal"] #backdrop {
|
|
122
|
+
opacity: 0.15;
|
|
123
|
+
}
|
|
124
|
+
:root[data-template="minimal"] .slide__content h1 {
|
|
125
|
+
letter-spacing: -0.02em;
|
|
126
|
+
}
|
|
127
|
+
:root[data-template="minimal"] blockquote {
|
|
128
|
+
border-left-width: 2px;
|
|
129
|
+
background: transparent;
|
|
130
|
+
}
|
|
131
|
+
:root[data-template="minimal"] pre {
|
|
132
|
+
border: 1px solid var(--surface0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Editorial */
|
|
136
|
+
:root[data-template="editorial"] {
|
|
137
|
+
--slide-pad-x: 7vw;
|
|
138
|
+
}
|
|
139
|
+
:root[data-template="editorial"] .slide__content h1 {
|
|
140
|
+
border-bottom: 3px solid var(--accent);
|
|
141
|
+
padding-bottom: 0.3em;
|
|
142
|
+
margin-bottom: 0.6em;
|
|
143
|
+
}
|
|
144
|
+
:root[data-template="editorial"] .slide__content h2 {
|
|
145
|
+
border-bottom: 1px solid var(--surface1);
|
|
146
|
+
padding-bottom: 0.2em;
|
|
147
|
+
}
|
|
148
|
+
:root[data-template="editorial"] blockquote {
|
|
149
|
+
border-left: 4px solid var(--accent);
|
|
150
|
+
font-style: italic;
|
|
151
|
+
background: var(--surface0);
|
|
152
|
+
}
|
|
153
|
+
:root[data-template="editorial"] table th {
|
|
154
|
+
border-bottom: 2px solid var(--accent);
|
|
155
|
+
}
|
|
156
|
+
:root[data-template="editorial"] hr {
|
|
157
|
+
border: none;
|
|
158
|
+
border-top: 2px solid var(--surface2);
|
|
159
|
+
margin: 2em 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Spotlight */
|
|
163
|
+
:root[data-template="spotlight"] .slide {
|
|
164
|
+
justify-content: center;
|
|
165
|
+
align-items: center;
|
|
166
|
+
text-align: center;
|
|
167
|
+
}
|
|
168
|
+
:root[data-template="spotlight"] .slide__content {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
text-align: center;
|
|
174
|
+
}
|
|
175
|
+
:root[data-template="spotlight"] .slide__content h1 {
|
|
176
|
+
font-size: calc(var(--type-display) * 1.15);
|
|
177
|
+
text-align: center;
|
|
178
|
+
}
|
|
179
|
+
:root[data-template="spotlight"] .slide__content p {
|
|
180
|
+
max-width: 80%;
|
|
181
|
+
margin-left: auto;
|
|
182
|
+
margin-right: auto;
|
|
183
|
+
}
|
|
184
|
+
:root[data-template="spotlight"] .slide__content ul,
|
|
185
|
+
:root[data-template="spotlight"] .slide__content ol {
|
|
186
|
+
text-align: left;
|
|
187
|
+
display: inline-block;
|
|
188
|
+
margin-left: auto;
|
|
189
|
+
margin-right: auto;
|
|
190
|
+
}
|
|
191
|
+
:root[data-template="spotlight"] .slide__content blockquote {
|
|
192
|
+
text-align: center;
|
|
193
|
+
border-left: none;
|
|
194
|
+
border-top: 2px solid var(--accent);
|
|
195
|
+
border-bottom: 2px solid var(--accent);
|
|
196
|
+
padding: 1em 2em;
|
|
197
|
+
background: transparent;
|
|
198
|
+
}
|
|
199
|
+
:root[data-template="spotlight"] .slide__content pre {
|
|
200
|
+
text-align: left;
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
export const TRANSITION_CSS = `/* ── Transitions ────────────────────────────────────────────────────────── */
|
|
204
|
+
|
|
205
|
+
/* Fade */
|
|
206
|
+
:root[data-transition="fade"] .slide {
|
|
207
|
+
transition: opacity 0.3s ease;
|
|
208
|
+
transform: none !important;
|
|
209
|
+
}
|
|
210
|
+
:root[data-transition="fade"] .slide.exit-left,
|
|
211
|
+
:root[data-transition="fade"] .slide.exit-right,
|
|
212
|
+
:root[data-transition="fade"] .slide.enter-from-left,
|
|
213
|
+
:root[data-transition="fade"] .slide.enter-from-right {
|
|
214
|
+
transform: none !important;
|
|
215
|
+
opacity: 0;
|
|
216
|
+
}
|
|
217
|
+
:root[data-transition="fade"] .slide.is-active {
|
|
218
|
+
opacity: 1;
|
|
219
|
+
transform: none !important;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Zoom */
|
|
223
|
+
:root[data-transition="zoom"] .slide {
|
|
224
|
+
transform: scale(0.92);
|
|
225
|
+
transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
226
|
+
}
|
|
227
|
+
:root[data-transition="zoom"] .slide.is-active {
|
|
228
|
+
transform: scale(1);
|
|
229
|
+
opacity: 1;
|
|
230
|
+
}
|
|
231
|
+
:root[data-transition="zoom"] .slide.exit-left,
|
|
232
|
+
:root[data-transition="zoom"] .slide.exit-right {
|
|
233
|
+
transform: scale(1.08);
|
|
234
|
+
opacity: 0;
|
|
235
|
+
}
|
|
236
|
+
:root[data-transition="zoom"] .slide.enter-from-left,
|
|
237
|
+
:root[data-transition="zoom"] .slide.enter-from-right {
|
|
238
|
+
transform: scale(0.92);
|
|
239
|
+
opacity: 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Lift */
|
|
243
|
+
:root[data-transition="lift"] .slide {
|
|
244
|
+
transform: translateY(48px);
|
|
245
|
+
transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
|
246
|
+
}
|
|
247
|
+
:root[data-transition="lift"] .slide.is-active {
|
|
248
|
+
transform: translateY(0);
|
|
249
|
+
opacity: 1;
|
|
250
|
+
}
|
|
251
|
+
:root[data-transition="lift"] .slide.exit-left,
|
|
252
|
+
:root[data-transition="lift"] .slide.exit-right {
|
|
253
|
+
transform: translateY(-48px);
|
|
254
|
+
opacity: 0;
|
|
255
|
+
}
|
|
256
|
+
:root[data-transition="lift"] .slide.enter-from-left,
|
|
257
|
+
:root[data-transition="lift"] .slide.enter-from-right {
|
|
258
|
+
transform: translateY(48px);
|
|
259
|
+
opacity: 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* None */
|
|
263
|
+
:root[data-transition="none"] .slide,
|
|
264
|
+
:root[data-transition="none"] .slide.is-active,
|
|
265
|
+
:root[data-transition="none"] .slide.exit-left,
|
|
266
|
+
:root[data-transition="none"] .slide.exit-right,
|
|
267
|
+
:root[data-transition="none"] .slide.enter-from-left,
|
|
268
|
+
:root[data-transition="none"] .slide.enter-from-right {
|
|
269
|
+
transition: none !important;
|
|
270
|
+
transform: none !important;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@media (prefers-reduced-motion: reduce) {
|
|
274
|
+
:root[data-transition] .slide {
|
|
275
|
+
transition: opacity 0.2s linear !important;
|
|
276
|
+
transform: none !important;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@media print {
|
|
281
|
+
:root[data-transition] .slide {
|
|
282
|
+
transition: none !important;
|
|
283
|
+
transform: none !important;
|
|
284
|
+
opacity: 1 !important;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
`;
|
package/dist/preview.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { RESET_CSS, SLIDE_CSS, DECOR_CSS } from "./generate.js";
|
|
2
2
|
import { findFont, FONT_IDS, fontOverrideCss, SIZE_IDS, DEFAULT_SIZE, DEFAULT_THEME, decorMapJson, decorOf, googleFontsHref, hljsHref, hljsMapJson, resolveSizeName, sizeSwitchableCss, themeSwitchableCss, } from "./themes.js";
|
|
3
|
+
import { DEFAULT_TEMPLATE, DEFAULT_TRANSITION, resolveTemplateName, resolveTransitionName, TEMPLATE_CSS, TRANSITION_CSS, } from "./presentation-options.js";
|
|
4
|
+
import { RICH_CONTENT_CSS, RICH_CONTENT_RUNTIME, richContentHead, } from "./rich-content.js";
|
|
5
|
+
import { FRAGMENT_CSS, FRAGMENT_RUNTIME } from "./fragments.js";
|
|
3
6
|
/** Virtual viewport the preview renders at, so `vw` sizing matches a projector. */
|
|
4
7
|
export const PREVIEW_WIDTH = 1600;
|
|
5
8
|
export const PREVIEW_HEIGHT = 900;
|
|
@@ -8,13 +11,15 @@ export const PREVIEW_HEIGHT = 900;
|
|
|
8
11
|
* own stylesheet, so what the editor shows is what `deckrun file.md` renders.
|
|
9
12
|
* Slides arrive over postMessage; nothing is fetched or parsed in here.
|
|
10
13
|
*/
|
|
11
|
-
export function generatePreviewHtml(initialTheme = DEFAULT_THEME, initialSize = DEFAULT_SIZE, fonts = {}) {
|
|
14
|
+
export function generatePreviewHtml(initialTheme = DEFAULT_THEME, initialSize = DEFAULT_SIZE, fonts = {}, initialTemplate = DEFAULT_TEMPLATE, initialTransition = DEFAULT_TRANSITION) {
|
|
12
15
|
const size = resolveSizeName(initialSize);
|
|
16
|
+
const template = resolveTemplateName(initialTemplate);
|
|
17
|
+
const transition = resolveTransitionName(initialTransition);
|
|
13
18
|
const head = findFont(fonts.head);
|
|
14
19
|
const body = findFont(fonts.body);
|
|
15
20
|
const fontAttrs = (head ? ` data-head="${head}"` : "") + (body ? ` data-body="${body}"` : "");
|
|
16
21
|
return `<!DOCTYPE html>
|
|
17
|
-
<html lang="en" data-theme="${initialTheme}" data-decor="${decorOf(initialTheme)}" data-size="${size}"${fontAttrs}>
|
|
22
|
+
<html lang="en" data-theme="${initialTheme}" data-decor="${decorOf(initialTheme)}" data-size="${size}" data-template="${template}" data-transition="${transition}"${fontAttrs}>
|
|
18
23
|
<head>
|
|
19
24
|
<meta charset="UTF-8">
|
|
20
25
|
<title>preview · deckrun</title>
|
|
@@ -23,6 +28,7 @@ export function generatePreviewHtml(initialTheme = DEFAULT_THEME, initialSize =
|
|
|
23
28
|
<link href="${googleFontsHref()}" rel="stylesheet">
|
|
24
29
|
<link rel="stylesheet" id="hljs-theme" href="${hljsHref(initialTheme)}">
|
|
25
30
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
31
|
+
${richContentHead({ math: true, mermaid: true }, "local")}
|
|
26
32
|
<style>
|
|
27
33
|
${RESET_CSS}
|
|
28
34
|
|
|
@@ -34,6 +40,14 @@ ${fontOverrideCss()}
|
|
|
34
40
|
|
|
35
41
|
${SLIDE_CSS}
|
|
36
42
|
|
|
43
|
+
${TEMPLATE_CSS}
|
|
44
|
+
|
|
45
|
+
${TRANSITION_CSS}
|
|
46
|
+
|
|
47
|
+
${FRAGMENT_CSS}
|
|
48
|
+
|
|
49
|
+
${RICH_CONTENT_CSS}
|
|
50
|
+
|
|
37
51
|
${DECOR_CSS}
|
|
38
52
|
|
|
39
53
|
/* ── Preview overrides ────────────────────────────────────────────────── */
|
|
@@ -119,6 +133,11 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
119
133
|
<div id="presentation"></div>
|
|
120
134
|
<div id="pv-empty">nothing to preview yet</div>
|
|
121
135
|
<script>
|
|
136
|
+
${FRAGMENT_RUNTIME}
|
|
137
|
+
|
|
138
|
+
${RICH_CONTENT_RUNTIME}
|
|
139
|
+
</script>
|
|
140
|
+
<script>
|
|
122
141
|
(function () {
|
|
123
142
|
'use strict';
|
|
124
143
|
|
|
@@ -144,7 +163,7 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
144
163
|
|
|
145
164
|
function highlight(root) {
|
|
146
165
|
if (!window.hljs) return;
|
|
147
|
-
var blocks = root.querySelectorAll('pre code');
|
|
166
|
+
var blocks = root.querySelectorAll('pre code:not(.language-mermaid):not(.lang-mermaid)');
|
|
148
167
|
for (var i = 0; i < blocks.length; i++) {
|
|
149
168
|
if (!blocks[i].dataset.highlighted) {
|
|
150
169
|
try { window.hljs.highlightElement(blocks[i]); } catch (e) {}
|
|
@@ -157,7 +176,19 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
157
176
|
if (mode !== 'single') return;
|
|
158
177
|
var content = stage.querySelector('.slide__content');
|
|
159
178
|
var over = false;
|
|
160
|
-
if (content)
|
|
179
|
+
if (content) {
|
|
180
|
+
over = content.scrollHeight - content.clientHeight > 6 ||
|
|
181
|
+
content.scrollWidth - content.clientWidth > 6;
|
|
182
|
+
|
|
183
|
+
// KaTeX display equations and Mermaid hosts deliberately hide their own
|
|
184
|
+
// overflow to keep a projected slide tidy. Inspect them separately so a
|
|
185
|
+
// clipped formula or diagram still triggers the editor's overflow nudge.
|
|
186
|
+
var rich = content.querySelectorAll('.katex-display, .mermaid');
|
|
187
|
+
for (var i = 0; !over && i < rich.length; i++) {
|
|
188
|
+
over = rich[i].scrollHeight - rich[i].clientHeight > 6 ||
|
|
189
|
+
rich[i].scrollWidth - rich[i].clientWidth > 6;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
161
192
|
send({ type: 'overflow', index: index, overflow: over });
|
|
162
193
|
}
|
|
163
194
|
|
|
@@ -166,8 +197,10 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
166
197
|
stage.innerHTML = slides[index] || '';
|
|
167
198
|
var el = stage.querySelector('.slide');
|
|
168
199
|
if (el) el.classList.add('is-active');
|
|
200
|
+
if (window.deckrunPrepareFragments) window.deckrunPrepareFragments(stage, true);
|
|
169
201
|
highlight(stage);
|
|
170
|
-
|
|
202
|
+
var rich = window.deckrunRenderRichContent ? window.deckrunRenderRichContent(stage) : Promise.resolve();
|
|
203
|
+
rich.then(function () { requestAnimationFrame(reportOverflow); });
|
|
171
204
|
}
|
|
172
205
|
|
|
173
206
|
function renderGrid() {
|
|
@@ -195,7 +228,10 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
195
228
|
});
|
|
196
229
|
stage.appendChild(frag);
|
|
197
230
|
scaleThumbs();
|
|
231
|
+
if (window.deckrunPrepareFragments) window.deckrunPrepareFragments(stage, true);
|
|
198
232
|
highlight(stage);
|
|
233
|
+
var rich = window.deckrunRenderRichContent ? window.deckrunRenderRichContent(stage) : Promise.resolve();
|
|
234
|
+
rich.then(scaleThumbs);
|
|
199
235
|
}
|
|
200
236
|
|
|
201
237
|
function scaleThumbs() {
|
|
@@ -229,7 +265,7 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
229
265
|
else if (k === 'g') { e.preventDefault(); send({ type: 'action', action: 'grid' }); }
|
|
230
266
|
else if (k === 'o') { e.preventDefault(); send({ type: 'action', action: 'decks' }); }
|
|
231
267
|
else if (k === '/') { e.preventDefault(); send({ type: 'action', action: 'guide' }); }
|
|
232
|
-
else if (k === 's' && e.shiftKey) { e.preventDefault(); send({ type: 'action', action: 'pdf' }); }
|
|
268
|
+
else if (k === 'p' || (k === 's' && e.shiftKey)) { e.preventDefault(); send({ type: 'action', action: 'pdf' }); }
|
|
233
269
|
else if (k === 's') { e.preventDefault(); send({ type: 'action', action: 'download' }); }
|
|
234
270
|
else if (k === 'l' && e.shiftKey) { e.preventDefault(); send({ type: 'action', action: 'theme' }); }
|
|
235
271
|
return;
|
|
@@ -330,9 +366,11 @@ body.is-empty #pv-empty { display: flex; }
|
|
|
330
366
|
// theme, which delete does and an assignment of '' would not.
|
|
331
367
|
applyFont('head', m.head);
|
|
332
368
|
applyFont('body', m.body);
|
|
369
|
+
if (m.template) document.documentElement.dataset.template = m.template;
|
|
370
|
+
if (m.transition) document.documentElement.dataset.transition = m.transition;
|
|
333
371
|
// Type size and face both change how tall a slide's content runs, so the
|
|
334
372
|
// editor's overflow warning has to be re-measured against them.
|
|
335
|
-
|
|
373
|
+
render();
|
|
336
374
|
} else if (m.type === 'index') {
|
|
337
375
|
index = m.index;
|
|
338
376
|
if (mode === 'grid') {
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
export function richContentFeatures(slides) {
|
|
2
|
+
let math = false;
|
|
3
|
+
let mermaid = false;
|
|
4
|
+
for (const slide of slides) {
|
|
5
|
+
if (!math && (slide.html.includes('class="math-source"') || slide.html.includes("math-source"))) {
|
|
6
|
+
math = true;
|
|
7
|
+
}
|
|
8
|
+
if (!mermaid &&
|
|
9
|
+
(slide.html.includes("language-mermaid") ||
|
|
10
|
+
slide.html.includes("lang-mermaid") ||
|
|
11
|
+
slide.html.includes('class="mermaid"'))) {
|
|
12
|
+
mermaid = true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return { math, mermaid };
|
|
16
|
+
}
|
|
17
|
+
export function richContentHead(features, source = "local") {
|
|
18
|
+
const parts = [];
|
|
19
|
+
if (features.math) {
|
|
20
|
+
if (source === "cdn") {
|
|
21
|
+
parts.push('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.18.4/dist/katex.min.css">');
|
|
22
|
+
parts.push('<script src="https://cdn.jsdelivr.net/npm/katex@0.18.4/dist/katex.min.js"></script>');
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
parts.push('<link rel="stylesheet" href="/__vendor/katex.min.css">');
|
|
26
|
+
parts.push('<script src="/__vendor/katex.min.js"></script>');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (features.mermaid) {
|
|
30
|
+
if (source === "cdn") {
|
|
31
|
+
parts.push('<script src="https://cdn.jsdelivr.net/npm/mermaid@11.17.2/dist/mermaid.min.js"></script>');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
parts.push('<script src="/__vendor/mermaid.min.js"></script>');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return parts.join("\n ");
|
|
38
|
+
}
|
|
39
|
+
export const RICH_CONTENT_CSS = `/* ── Rich Content (Math & Diagrams) ─────────────────────────────────────── */
|
|
40
|
+
|
|
41
|
+
.math-source {
|
|
42
|
+
font-family: inherit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
div.math-source {
|
|
46
|
+
display: flex;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
margin: 1.2em 0;
|
|
49
|
+
overflow-x: auto;
|
|
50
|
+
overflow-y: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
span.math-source {
|
|
54
|
+
display: inline;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.katex-display {
|
|
58
|
+
margin: 0.8em 0;
|
|
59
|
+
overflow-x: auto;
|
|
60
|
+
overflow-y: hidden;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.katex {
|
|
64
|
+
font-size: 1.15em;
|
|
65
|
+
text-rendering: auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.math-error {
|
|
69
|
+
color: var(--maroon, #f38ba8);
|
|
70
|
+
background: var(--surface0, rgba(255, 0, 0, 0.1));
|
|
71
|
+
padding: 2px 6px;
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
font-family: var(--font-mono, monospace);
|
|
74
|
+
font-size: 0.85em;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.mermaid {
|
|
78
|
+
display: flex;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
align-items: center;
|
|
81
|
+
margin: 1.2em auto;
|
|
82
|
+
max-width: 100%;
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.mermaid svg {
|
|
87
|
+
max-width: 100%;
|
|
88
|
+
height: auto;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.mermaid-error {
|
|
92
|
+
color: var(--maroon, #f38ba8);
|
|
93
|
+
background: var(--surface0, rgba(255, 0, 0, 0.1));
|
|
94
|
+
border: 1px solid var(--maroon, #f38ba8);
|
|
95
|
+
border-radius: 6px;
|
|
96
|
+
padding: 12px 16px;
|
|
97
|
+
font-family: var(--font-mono, monospace);
|
|
98
|
+
font-size: 0.9em;
|
|
99
|
+
white-space: pre-wrap;
|
|
100
|
+
margin: 1em 0;
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
export const RICH_CONTENT_RUNTIME = `(function () {
|
|
104
|
+
window.deckrunRenderRichContent = function (root) {
|
|
105
|
+
if (!root) return Promise.resolve();
|
|
106
|
+
|
|
107
|
+
// 1. Render KaTeX math
|
|
108
|
+
if (window.katex) {
|
|
109
|
+
var mathNodes = root.querySelectorAll('.math-source:not([data-rendered])');
|
|
110
|
+
for (var i = 0; i < mathNodes.length; i++) {
|
|
111
|
+
var el = mathNodes[i];
|
|
112
|
+
var tex = el.textContent || '';
|
|
113
|
+
var isDisplay = el.dataset.display === 'true';
|
|
114
|
+
try {
|
|
115
|
+
window.katex.render(tex, el, {
|
|
116
|
+
displayMode: isDisplay,
|
|
117
|
+
throwOnError: false,
|
|
118
|
+
output: 'htmlAndMathml'
|
|
119
|
+
});
|
|
120
|
+
el.setAttribute('data-rendered', 'true');
|
|
121
|
+
} catch (err) {
|
|
122
|
+
el.innerHTML = '<span class="math-error">' + (err && err.message ? err.message : 'Math rendering error') + '</span>';
|
|
123
|
+
el.setAttribute('data-rendered', 'true');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 2. Render Mermaid diagrams
|
|
129
|
+
var mermaidPromises = [];
|
|
130
|
+
var codeBlocks = root.querySelectorAll('pre code.language-mermaid, pre code.lang-mermaid');
|
|
131
|
+
if (codeBlocks.length > 0 && window.mermaid) {
|
|
132
|
+
try {
|
|
133
|
+
window.mermaid.initialize({
|
|
134
|
+
startOnLoad: false,
|
|
135
|
+
theme: 'dark',
|
|
136
|
+
securityLevel: 'loose'
|
|
137
|
+
});
|
|
138
|
+
} catch (e) {}
|
|
139
|
+
|
|
140
|
+
for (var j = 0; j < codeBlocks.length; j++) {
|
|
141
|
+
(function (codeEl) {
|
|
142
|
+
var preEl = codeEl.closest('pre');
|
|
143
|
+
if (!preEl || preEl.dataset.rendered) return;
|
|
144
|
+
preEl.dataset.rendered = 'true';
|
|
145
|
+
var code = codeEl.textContent || '';
|
|
146
|
+
var container = document.createElement('div');
|
|
147
|
+
container.className = 'mermaid';
|
|
148
|
+
preEl.parentNode.insertBefore(container, preEl);
|
|
149
|
+
preEl.style.display = 'none';
|
|
150
|
+
|
|
151
|
+
var id = 'mermaid-' + Math.random().toString(36).slice(2, 10);
|
|
152
|
+
var p = window.mermaid.render(id, code)
|
|
153
|
+
.then(function (res) {
|
|
154
|
+
container.innerHTML = res.svg;
|
|
155
|
+
preEl.remove();
|
|
156
|
+
})
|
|
157
|
+
.catch(function (err) {
|
|
158
|
+
container.className = 'mermaid-error';
|
|
159
|
+
container.textContent = 'Mermaid Error: ' + (err && err.message ? err.message : String(err));
|
|
160
|
+
preEl.remove();
|
|
161
|
+
});
|
|
162
|
+
mermaidPromises.push(p);
|
|
163
|
+
})(codeBlocks[j]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return Promise.all(mermaidPromises).then(function () {});
|
|
168
|
+
};
|
|
169
|
+
})();
|
|
170
|
+
`;
|
package/dist/themes.js
CHANGED
|
@@ -499,6 +499,8 @@ function themeVars(t) {
|
|
|
499
499
|
["glow", alpha(accent, dark ? 0.34 : 0.22)],
|
|
500
500
|
["gradient", `linear-gradient(115deg, ${accent}, ${accent2} 58%, ${accent3})`],
|
|
501
501
|
["accent-fade", `linear-gradient(90deg, ${accent}, ${alpha(accent, 0.45)} 62%, transparent)`],
|
|
502
|
+
["selection-bg", alpha(accent, dark ? 0.22 : 0.18)],
|
|
503
|
+
["selection-text", "inherit"],
|
|
502
504
|
["surface-soft", alpha(n.surface0, dark ? 0.34 : 0.42)],
|
|
503
505
|
["crust-overlay", alpha(n.crust, dark ? 0.84 : 0.88)],
|
|
504
506
|
["scrim", dark ? "rgba(0, 0, 0, 0.5)" : alpha(n.text, 0.26)],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deckrun",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "A local-first presentation tool for writing, editing, presenting, and exporting Markdown slides or self-contained HTML docs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,11 +11,14 @@
|
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc && chmod +x dist/index.js",
|
|
14
|
-
"dev": "tsx src/index.ts"
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"test": "npm run build && node --test"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
17
18
|
"commander": "^11.1.0",
|
|
19
|
+
"katex": "^0.18.4",
|
|
18
20
|
"marked": "^9.1.6",
|
|
21
|
+
"mermaid": "^11.17.2",
|
|
19
22
|
"open": "^9.1.0"
|
|
20
23
|
},
|
|
21
24
|
"devDependencies": {
|