@abreen/tada 1.13.2 → 1.13.4

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.
@@ -187,17 +187,13 @@ export function renderTraceWidgetHtml({
187
187
  `<button class="trace-btn trace-first" disabled tabindex="-1" aria-label="First step" title="First step">${iconFirst}</button>` +
188
188
  `<button class="trace-btn trace-prev" disabled tabindex="-1" aria-label="Previous step" title="Previous step">${iconPrev}</button>` +
189
189
  `<span class="trace-step-counter"${counterStyle}>${counterContent}</span>` +
190
- (disabled
191
- ? `<button class="trace-btn trace-next" disabled tabindex="-1" aria-label="Next step" title="Next step">${iconNext}</button>`
192
- : `<button class="trace-btn trace-next" aria-label="Next step" title="Next step">${iconNext}</button>`) +
193
- (disabled
194
- ? `<button class="trace-btn trace-last" disabled tabindex="-1" aria-label="Last step" title="Last step">${iconLast}</button>`
195
- : `<button class="trace-btn trace-last" tabindex="-1" aria-label="Last step" title="Last step">${iconLast}</button>`);
190
+ `<button class="trace-btn trace-next" disabled tabindex="-1" aria-label="Next step" title="Next step">${iconNext}</button>` +
191
+ `<button class="trace-btn trace-last" disabled tabindex="-1" aria-label="Last step" title="Last step">${iconLast}</button>`;
196
192
  const wrapperAttrs = manifestUrl
197
193
  ? ` data-trace-manifest="${manifestUrl}"`
198
194
  : '';
199
195
  const wrapperClass = disabled
200
196
  ? 'trace-widget trace-disabled'
201
197
  : 'trace-widget';
202
- return `<div class="${wrapperClass}"${wrapperAttrs}><noscript><p>This interactive trace requires JavaScript.</p></noscript><div class="trace-body"><div class="trace-toolbar"><div class="trace-controls" role="toolbar" aria-label="Trace navigation">${controls}</div></div><div class="trace-content"><div class="trace-diagram"></div><div class="trace-source-wrapper"><div class="trace-source">${highlightedSource}</div></div><pre class="trace-output"></pre></div></div></div>`;
198
+ return `<div class="${wrapperClass}"${wrapperAttrs}><noscript><p>This interactive trace requires JavaScript.</p></noscript><div class="trace-body"><div class="trace-toolbar"><div class="trace-controls" role="toolbar" aria-label="Trace navigation">${controls}</div></div><div class="trace-content"><div class="trace-diagram"></div><div class="trace-source-wrapper"><div class="trace-source">${highlightedSource}</div></div></div></div></div>`;
203
199
  }
@@ -18,6 +18,6 @@ def selection(nums):
18
18
  swap(nums, i, index_smallest(nums, i))
19
19
 
20
20
 
21
- input1 = [4, 9, 2, 10, 19, 5, 7]
21
+ input1 = [25, 31, 12, 9]
22
22
  selection(input1)
23
23
  print(input1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.13.2",
3
+ "version": "1.13.4",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
package/src/_base.scss CHANGED
@@ -180,6 +180,73 @@ input[type='search'] {
180
180
  }
181
181
  }
182
182
 
183
+ label:has(> input[type='checkbox']) {
184
+ display: inline-flex;
185
+ gap: 0.45em;
186
+ align-items: center;
187
+ font-size: var(--font-size-small);
188
+ font-weight: 500;
189
+
190
+ @include user-select(none);
191
+ }
192
+
193
+ input[type='checkbox'] {
194
+ display: inline-grid;
195
+ flex: 0 0 auto;
196
+ place-content: center;
197
+ width: 1.5em;
198
+ height: 1.25em;
199
+ padding: 0;
200
+ margin: 0;
201
+ font: inherit;
202
+ color: var(--fg-color);
203
+ appearance: none;
204
+ cursor: pointer;
205
+ background: var(--bg-color);
206
+ border: var(--control-border-width) solid var(--fg2-color);
207
+ border-radius: 0.25em;
208
+
209
+ &::after {
210
+ display: block;
211
+ width: 1.1em;
212
+ height: 1.1em;
213
+ content: '';
214
+ background-color: currentcolor;
215
+ opacity: 0;
216
+
217
+ @include contained-mask(var(--icon-check));
218
+ }
219
+
220
+ &:checked {
221
+ &::after {
222
+ opacity: 1;
223
+ }
224
+ }
225
+
226
+ &:disabled {
227
+ cursor: not-allowed;
228
+ opacity: 0.4;
229
+ }
230
+
231
+ &:active:not(:disabled) {
232
+ color: var(--bg-color) !important;
233
+ background: var(--link-active-color) !important;
234
+ }
235
+
236
+ &:focus {
237
+ outline: 2px solid var(--theme-color);
238
+ outline-offset: 1px;
239
+ background-color: var(--bg2-color);
240
+ border-color: var(--fg-color);
241
+ }
242
+ }
243
+
244
+ @media (hover: hover) {
245
+ input[type='checkbox']:hover:not(:disabled) {
246
+ background: var(--bg2-color);
247
+ }
248
+ }
249
+
183
250
  figure {
184
251
  margin: 1rem 0;
185
252
 
@@ -4,3 +4,12 @@ Append a link to each heading having an `id` attribute.
4
4
 
5
5
  When the link is clicked, `window.location` is updated and the heading is
6
6
  highlighted using the same function as the `toc` component.
7
+
8
+ The heading link includes a real inline SVG hash icon, revealed on heading
9
+ hover or link focus.
10
+
11
+ For slide title headings (`div.slide > h2:first-child`), append a
12
+ `Present from this slide` icon button. Clicking the button dispatches a
13
+ bubbling `tada:slides-present` custom event with the slide index so the slides
14
+ component can open presentation mode from that slide using the current
15
+ `Full screen` checkbox state.
@@ -2,32 +2,131 @@ function getElements(parent: HTMLElement): HTMLHeadingElement[] {
2
2
  return Array.from(parent.querySelectorAll('h1, h2, h3, h4, h5, h6'));
3
3
  }
4
4
 
5
+ function createIcon(window: Window, type: 'hash' | 'present'): SVGSVGElement {
6
+ const svg = window.document.createElementNS(
7
+ 'http://www.w3.org/2000/svg',
8
+ 'svg',
9
+ );
10
+ svg.setAttribute('aria-hidden', 'true');
11
+ svg.setAttribute('viewBox', '0 0 24 24');
12
+ svg.setAttribute('width', '16');
13
+ svg.setAttribute('height', '16');
14
+ svg.setAttribute('fill', 'none');
15
+ svg.setAttribute('stroke', 'currentColor');
16
+ svg.setAttribute('stroke-width', '1.5');
17
+ svg.setAttribute('stroke-linecap', 'round');
18
+ svg.setAttribute('stroke-linejoin', 'round');
19
+
20
+ const paths =
21
+ type === 'hash'
22
+ ? ['M10 3 8 21', 'M16 3l-2 18', 'M4 9h17', 'M3 15h17']
23
+ : [
24
+ 'M5 5h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z',
25
+ 'M9 20h6',
26
+ ];
27
+
28
+ for (const d of paths) {
29
+ const path = window.document.createElementNS(
30
+ 'http://www.w3.org/2000/svg',
31
+ 'path',
32
+ );
33
+ path.setAttribute('d', d);
34
+ svg.appendChild(path);
35
+ }
36
+
37
+ return svg;
38
+ }
39
+
40
+ function getSlideIndex(slide: HTMLElement): number {
41
+ const rawIndex = slide.getAttribute('data-slide-index');
42
+ if (rawIndex != null) {
43
+ const parsed = Number.parseInt(rawIndex, 10);
44
+ if (Number.isFinite(parsed)) {
45
+ return parsed;
46
+ }
47
+ }
48
+
49
+ const root = slide.closest('[data-slides-root]');
50
+ if (!root) {
51
+ return 0;
52
+ }
53
+
54
+ return Array.from(root.querySelectorAll('.slide')).indexOf(slide);
55
+ }
56
+
57
+ function isSlideTitleHeading(
58
+ heading: HTMLHeadingElement,
59
+ ): heading is HTMLHeadingElement & { parentElement: HTMLElement } {
60
+ const slide = heading.parentElement;
61
+ return (
62
+ heading.tagName === 'H2' &&
63
+ slide?.tagName === 'DIV' &&
64
+ slide.classList.contains('slide') &&
65
+ slide.firstElementChild === heading
66
+ );
67
+ }
68
+
5
69
  export default (window: Window) => {
70
+ const view = window as Window & typeof globalThis;
6
71
  const elements = getElements(window.document.body);
7
- if (elements == null) {
8
- return;
9
- }
72
+ const cleanups: (() => void)[] = [];
10
73
 
11
74
  elements.forEach(el => {
12
- if (!el.id) {
75
+ if (el.id) {
76
+ const link = window.document.createElement('a');
77
+ link.className = 'heading-anchor';
78
+ link.href = `#${el.id}`;
79
+ link.title = 'Link to this heading';
80
+
81
+ // Move all existing child nodes into the link.
82
+ while (el.firstChild) {
83
+ link.appendChild(el.firstChild);
84
+ }
85
+ link.appendChild(createIcon(window, 'hash'));
86
+ el.appendChild(link);
87
+
88
+ const handleClick = () => {
89
+ el.focus();
90
+ };
91
+ link.addEventListener('click', handleClick);
92
+ cleanups.push(() => {
93
+ link.removeEventListener('click', handleClick);
94
+ });
95
+ }
96
+
97
+ if (!isSlideTitleHeading(el)) {
13
98
  return;
14
99
  }
15
100
 
16
- const link = window.document.createElement('a');
17
- link.className = 'heading-anchor';
18
- link.href = `#${el.id}`;
19
- link.title = 'Link to this heading';
101
+ const button = window.document.createElement('button');
102
+ button.type = 'button';
103
+ button.className = 'heading-present-button';
104
+ button.setAttribute('aria-label', 'Present from this slide');
105
+ button.title = 'Present from this slide';
106
+ button.appendChild(createIcon(window, 'present'));
107
+ el.appendChild(button);
20
108
 
21
- // Move all existing child nodes into the link
22
- while (el.firstChild) {
23
- link.appendChild(el.firstChild);
24
- }
25
- el.appendChild(link);
109
+ const slide = el.parentElement;
110
+ const slideIndex = getSlideIndex(slide);
26
111
 
27
- link.addEventListener('click', () => {
28
- el.focus();
112
+ const handlePresentClick = () => {
113
+ button.dispatchEvent(
114
+ new view.CustomEvent('tada:slides-present', {
115
+ bubbles: true,
116
+ detail: { slideIndex },
117
+ }),
118
+ );
119
+ };
120
+ button.addEventListener('click', handlePresentClick);
121
+
122
+ cleanups.push(() => {
123
+ button.removeEventListener('click', handlePresentClick);
29
124
  });
30
125
  });
31
126
 
32
- return () => {};
127
+ return () => {
128
+ for (const cleanup of cleanups) {
129
+ cleanup();
130
+ }
131
+ };
33
132
  };
@@ -7,29 +7,89 @@ a.heading-anchor {
7
7
  @include subdued-link;
8
8
  }
9
9
 
10
- a.heading-anchor::after {
10
+ .heading-anchor svg,
11
+ .heading-present-button svg {
11
12
  display: inline-block;
12
13
  width: 1em;
13
14
  height: 1em;
15
+ vertical-align: middle;
16
+ }
17
+
18
+ .heading-anchor svg {
14
19
  margin-left: 4px;
15
20
  vertical-align: middle;
16
- content: '';
17
21
  opacity: 0;
22
+ }
18
23
 
19
- @include contained-mask(var(--icon-anchor));
24
+ .heading-present-button {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ width: 1em;
29
+ height: 1em;
30
+ padding: 0;
31
+ margin-left: 4px;
32
+ font: inherit;
33
+ line-height: 1;
34
+ vertical-align: middle;
35
+ color: var(--fg-color);
36
+ background: transparent;
37
+ border: none;
38
+ border-radius: 0;
39
+ opacity: 0;
20
40
  }
21
41
 
22
- a.heading-anchor:focus-visible::after {
42
+ a.heading-anchor:focus-visible svg,
43
+ .heading-present-button:focus {
23
44
  opacity: 1;
24
45
  }
25
46
 
47
+ .heading-present-button:focus {
48
+ outline-offset: 2px;
49
+ background-color: transparent;
50
+ border-color: transparent;
51
+ }
52
+
53
+ button.heading-present-button:active:not(:disabled) {
54
+ color: var(--link-active-color) !important;
55
+ background: transparent !important;
56
+ }
57
+
58
+ body.is-presenting .heading-present-button {
59
+ display: none;
60
+ }
61
+
26
62
  @media (hover: hover) {
27
- h1:hover a.heading-anchor::after,
28
- h2:hover a.heading-anchor::after,
29
- h3:hover a.heading-anchor::after,
30
- h4:hover a.heading-anchor::after,
31
- h5:hover a.heading-anchor::after,
32
- h6:hover a.heading-anchor::after {
63
+ button.heading-present-button:hover:not(:disabled) {
64
+ color: var(--fg2-color);
65
+ background: transparent;
66
+ }
67
+
68
+ h1:hover a.heading-anchor svg,
69
+ h2:hover a.heading-anchor svg,
70
+ h3:hover a.heading-anchor svg,
71
+ h4:hover a.heading-anchor svg,
72
+ h5:hover a.heading-anchor svg,
73
+ h6:hover a.heading-anchor svg,
74
+ h1:hover .heading-present-button,
75
+ h2:hover .heading-present-button,
76
+ h3:hover .heading-present-button,
77
+ h4:hover .heading-present-button,
78
+ h5:hover .heading-present-button,
79
+ h6:hover .heading-present-button {
33
80
  opacity: 1;
34
81
  }
35
82
  }
83
+
84
+ @media (hover: none) {
85
+ .heading-present-button {
86
+ display: none;
87
+ }
88
+ }
89
+
90
+ @media print {
91
+ .heading-anchor svg,
92
+ .heading-present-button {
93
+ display: none;
94
+ }
95
+ }
@@ -82,10 +82,6 @@
82
82
  border: none;
83
83
  }
84
84
 
85
- a.heading-anchor::after {
86
- display: none;
87
- }
88
-
89
85
  .external-link-tail::after,
90
86
  a.external:not(:has(.external-link-tail))::after {
91
87
  content: none;
@@ -5,6 +5,10 @@ interface TraceToolbarState {
5
5
 
6
6
  type PresentationMode = 'normal' | 'fullscreen';
7
7
 
8
+ interface SlidesPresentDetail {
9
+ slideIndex?: number;
10
+ }
11
+
8
12
  function isInteractive(
9
13
  view: Window & typeof globalThis,
10
14
  target: EventTarget | null,
@@ -45,17 +49,17 @@ export default function mountSlides(window: Window): void | (() => void) {
45
49
  const presentButton = document.querySelector(
46
50
  '[data-slides-present]',
47
51
  ) as HTMLButtonElement | null;
48
- const presentFullscreenButton = document.querySelector(
49
- '[data-slides-present-fullscreen]',
50
- ) as HTMLButtonElement | null;
51
- const rootScopeButton = presentButton ?? presentFullscreenButton;
52
+ const fullscreenCheckbox = document.querySelector(
53
+ '[data-slides-fullscreen]',
54
+ ) as HTMLInputElement | null;
55
+ const rootScopeButton = presentButton ?? fullscreenCheckbox;
52
56
  const scopedRoot = rootScopeButton
53
57
  ?.closest('main')
54
58
  ?.querySelector('[data-slides-root]');
55
59
  const root = (scopedRoot ??
56
60
  document.querySelector('[data-slides-root]')) as HTMLElement | null;
57
61
 
58
- if (!view || !root || (!presentButton && !presentFullscreenButton)) {
62
+ if (!view || !root) {
59
63
  return;
60
64
  }
61
65
 
@@ -78,8 +82,8 @@ export default function mountSlides(window: Window): void | (() => void) {
78
82
  if (presentButton) {
79
83
  presentButton.disabled = false;
80
84
  }
81
- if (presentFullscreenButton) {
82
- presentFullscreenButton.disabled = false;
85
+ if (fullscreenCheckbox) {
86
+ fullscreenCheckbox.disabled = false;
83
87
  }
84
88
 
85
89
  const overlay = document.createElement('div');
@@ -252,7 +256,7 @@ export default function mountSlides(window: Window): void | (() => void) {
252
256
  }
253
257
  }
254
258
 
255
- function enterPresentation(mode: PresentationMode): void {
259
+ function enterPresentation(mode: PresentationMode, slideIndex = 0): void {
256
260
  isPresenting = true;
257
261
  presentationMode = mode;
258
262
  hidePresentationControls();
@@ -260,8 +264,8 @@ export default function mountSlides(window: Window): void | (() => void) {
260
264
  document.body.classList.add('is-presenting');
261
265
  setTraceToolbarVisibility(true);
262
266
  resetReadyTraces();
263
- setActiveSlide(0);
264
- focusSlide(slides[0]);
267
+ setActiveSlide(slideIndex);
268
+ focusSlide(slides[activeIndex]);
265
269
  }
266
270
 
267
271
  function driveTrace(delta: 1 | -1): boolean {
@@ -329,12 +333,8 @@ export default function mountSlides(window: Window): void | (() => void) {
329
333
  focusSlide(slides[activeIndex]);
330
334
  }
331
335
 
332
- function handlePresentClick(): void {
333
- enterPresentation('normal');
334
- }
335
-
336
- async function handlePresentFullscreenClick(): Promise<void> {
337
- enterPresentation('fullscreen');
336
+ async function handlePresentFullscreenClick(slideIndex = 0): Promise<void> {
337
+ enterPresentation('fullscreen', slideIndex);
338
338
 
339
339
  if (typeof fullscreenTarget.requestFullscreen !== 'function') {
340
340
  presentationMode = 'normal';
@@ -348,8 +348,17 @@ export default function mountSlides(window: Window): void | (() => void) {
348
348
  }
349
349
  }
350
350
 
351
- function handlePresentFullscreenButtonClick(): void {
352
- void handlePresentFullscreenClick();
351
+ function startPresentation(slideIndex = 0): void {
352
+ if (fullscreenCheckbox?.checked === true) {
353
+ void handlePresentFullscreenClick(slideIndex);
354
+ return;
355
+ }
356
+
357
+ enterPresentation('normal', slideIndex);
358
+ }
359
+
360
+ function handlePresentClick(): void {
361
+ startPresentation(0);
353
362
  }
354
363
 
355
364
  function handleCloseClick(): void {
@@ -439,29 +448,34 @@ export default function mountSlides(window: Window): void | (() => void) {
439
448
  }
440
449
  }
441
450
 
451
+ function handleSlidesPresent(event: Event): void {
452
+ const detail = (event as CustomEvent<SlidesPresentDetail>).detail ?? {};
453
+ const slideIndex =
454
+ typeof detail.slideIndex === 'number' &&
455
+ Number.isFinite(detail.slideIndex)
456
+ ? detail.slideIndex
457
+ : 0;
458
+
459
+ startPresentation(slideIndex);
460
+ }
461
+
442
462
  presentButton?.addEventListener('click', handlePresentClick);
443
- presentFullscreenButton?.addEventListener(
444
- 'click',
445
- handlePresentFullscreenButtonClick,
446
- );
447
463
  closeButton.addEventListener('click', handleCloseClick);
448
464
  document.addEventListener('fullscreenchange', handleFullscreenChange);
449
465
  window.addEventListener('keydown', handleKeydown);
450
466
  window.addEventListener('mousemove', handleMouseMove);
451
467
  slidesRoot.addEventListener('click', handleSlideClick);
468
+ slidesRoot.addEventListener('tada:slides-present', handleSlidesPresent);
452
469
 
453
470
  return () => {
454
471
  exitPresentation();
455
472
  presentButton?.removeEventListener('click', handlePresentClick);
456
- presentFullscreenButton?.removeEventListener(
457
- 'click',
458
- handlePresentFullscreenButtonClick,
459
- );
460
473
  closeButton.removeEventListener('click', handleCloseClick);
461
474
  document.removeEventListener('fullscreenchange', handleFullscreenChange);
462
475
  window.removeEventListener('keydown', handleKeydown);
463
476
  window.removeEventListener('mousemove', handleMouseMove);
464
477
  slidesRoot.removeEventListener('click', handleSlideClick);
478
+ slidesRoot.removeEventListener('tada:slides-present', handleSlidesPresent);
465
479
  overlay.remove();
466
480
  };
467
481
  }
@@ -11,7 +11,7 @@ interface WidgetElements {
11
11
  root: HTMLElement;
12
12
  source: HTMLElement;
13
13
  controls: HTMLElement;
14
- output: HTMLElement;
14
+ content: HTMLElement;
15
15
  diagram: HTMLElement;
16
16
  }
17
17
 
@@ -193,6 +193,27 @@ function scaleDiagramSvg(diagram: HTMLElement, doc: Document): void {
193
193
  svg.setAttribute('height', String(baseHeight * scale));
194
194
  }
195
195
 
196
+ function updateOutput(
197
+ content: HTMLElement,
198
+ output: string,
199
+ doc: Document,
200
+ ): void {
201
+ const existing = content.querySelector(
202
+ ':scope > .trace-output',
203
+ ) as HTMLPreElement | null;
204
+ if (!output) {
205
+ existing?.remove();
206
+ return;
207
+ }
208
+
209
+ const outputElement = existing ?? doc.createElement('pre');
210
+ if (!existing) {
211
+ outputElement.className = 'trace-output';
212
+ content.append(outputElement);
213
+ }
214
+ outputElement.textContent = output;
215
+ }
216
+
196
217
  function renderWidgetState(
197
218
  state: WidgetState,
198
219
  elements: WidgetElements,
@@ -215,7 +236,7 @@ function renderWidgetState(
215
236
  output += s.stdout;
216
237
  }
217
238
  }
218
- elements.output.textContent = output;
239
+ updateOutput(elements.content, output, doc);
219
240
 
220
241
  const prevHeight = elements.diagram.scrollHeight;
221
242
  elements.diagram.innerHTML = entry.svg;
@@ -249,12 +270,9 @@ async function initWidget(root: HTMLElement, doc: Document): Promise<void> {
249
270
 
250
271
  await loadChunk(state, manifestUrl, 0);
251
272
 
252
- const entry = getStep(state);
253
-
254
273
  const source = root.querySelector('.trace-source') as HTMLElement;
274
+ const content = root.querySelector('.trace-content') as HTMLElement;
255
275
  const diagram = root.querySelector('.trace-diagram') as HTMLElement;
256
- const output = root.querySelector('.trace-output') as HTMLPreElement;
257
- output.textContent = entry.stdout || '';
258
276
 
259
277
  const controls = root.querySelector('.trace-controls') as HTMLElement;
260
278
  const firstBtn = controls.querySelector('.trace-first') as HTMLButtonElement;
@@ -296,7 +314,7 @@ async function initWidget(root: HTMLElement, doc: Document): Promise<void> {
296
314
  next.focus();
297
315
  });
298
316
 
299
- const elements: WidgetElements = { root, source, controls, output, diagram };
317
+ const elements: WidgetElements = { root, source, controls, content, diagram };
300
318
 
301
319
  renderWidgetState(state, elements, doc);
302
320
  }
@@ -58,6 +58,7 @@
58
58
  --icon-anchor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 28'><path d='M11 4 L8 20 M17 4 L14 20 M5 9 H21 M3 15 H19' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/></svg>");
59
59
  --icon-warning: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><polygon points='12,2 22,20 2,20' fill='none' stroke='black' stroke-width='1.5' stroke-linejoin='round'/><line x1='12' y1='9' x2='12' y2='13' stroke='black' stroke-width='2' stroke-linecap='round'/><line x1='12' y1='16' x2='12' y2='16' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>");
60
60
  --icon-note: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='10' fill='none' stroke='black' stroke-width='1.5'/><line x1='12' y1='10' x2='12' y2='16' stroke='black' stroke-width='2' stroke-linecap='round'/><line x1='12' y1='7' x2='12' y2='7' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>");
61
+ --icon-check: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><path d='M5 12.5 10 17 19 7' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/></svg>");
61
62
  --icon-search: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><circle cx='11' cy='11' r='7' stroke='<%= iconColor %>' stroke-width='2' fill='none'/><line x1='16.5' y1='16.5' x2='22' y2='22' stroke='<%= iconColor %>' stroke-width='2' stroke-linecap='round'/></svg>");
62
63
  --icon-chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><polyline points='9 18 15 12 9 6' stroke='<%= iconColor %>' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
63
64
  --icon-breadcrumb: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'><path d='M3 7 H17 V22' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/><polyline points='11 16 17 22 23 16' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/></svg>");
@@ -6,9 +6,10 @@
6
6
  <% if (page.slides) { %>
7
7
  <div class="slides-header" data-pagefind-ignore>
8
8
  <button type="button" data-slides-present disabled>Present</button>
9
- <button type="button" data-slides-present-fullscreen disabled>
10
- Present (Full Screen)
11
- </button>
9
+ <label>
10
+ <input type="checkbox" data-slides-fullscreen checked disabled>
11
+ Full screen
12
+ </label>
12
13
  </div>
13
14
  <% } %>
14
15
  <%= content %>