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.
@@ -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) over = content.scrollHeight - content.clientHeight > 6;
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
- requestAnimationFrame(reportOverflow);
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
- requestAnimationFrame(reportOverflow);
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.4.0",
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": {