@abreen/tada 1.11.3 → 1.13.2

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.
Files changed (79) hide show
  1. package/README.md +65 -64
  2. package/bin/tada.ts +61 -66
  3. package/build/build-manifest.ts +14 -3
  4. package/build/bundle.ts +0 -1
  5. package/build/config-files.ts +183 -0
  6. package/build/config-loader.ts +159 -0
  7. package/build/copy.ts +0 -83
  8. package/build/generate-content-assets.ts +75 -160
  9. package/build/globals.ts +48 -89
  10. package/build/log.ts +27 -18
  11. package/build/pagefind.ts +33 -26
  12. package/build/pipeline.ts +98 -79
  13. package/build/reachability.ts +51 -248
  14. package/build/serve.ts +35 -6
  15. package/build/site-variables.ts +20 -11
  16. package/build/source-model.ts +400 -0
  17. package/build/{watch/records.ts → source-records.ts} +111 -49
  18. package/build/template-globals.ts +91 -0
  19. package/build/templates.ts +50 -40
  20. package/build/test-helpers.ts +15 -0
  21. package/build/toc-plugin.ts +5 -1
  22. package/build/types.d.ts +9 -1
  23. package/build/util.ts +8 -0
  24. package/build/utils/code.ts +6 -16
  25. package/build/utils/content-files.ts +57 -126
  26. package/build/utils/final-html.ts +239 -0
  27. package/build/utils/markdown.ts +237 -17
  28. package/build/utils/paths.ts +2 -1
  29. package/build/utils/render.ts +108 -99
  30. package/build/validate-config-links.ts +91 -14
  31. package/build/watch/assets.ts +0 -2
  32. package/build/watch/build-full.ts +14 -15
  33. package/build/watch/build-incremental.ts +15 -8
  34. package/build/watch/compiler-types.d.ts +6 -1
  35. package/build/watch/compiler.ts +27 -15
  36. package/build/watch/index.ts +2 -7
  37. package/build/watch/planner.ts +32 -16
  38. package/build/watch/reload.ts +4 -0
  39. package/build/watch/runtime.ts +29 -45
  40. package/build/watch/snapshot.ts +50 -402
  41. package/build/watch/validation.ts +6 -3
  42. package/build/watch-reachability-state.ts +16 -23
  43. package/build/watch-reload-client.ts +11 -4
  44. package/init/content/index.md +1 -1
  45. package/init/content/labs/02/index.md +61 -0
  46. package/init/content/labs/02/selection_sort.py +23 -0
  47. package/init/content/labs/index.md +4 -2
  48. package/init/content/lectures/01/index.md +1 -1
  49. package/init/content/markdown.md +1 -1
  50. package/package.json +3 -4
  51. package/src/code/index.ts +46 -18
  52. package/src/code.scss +2 -1
  53. package/src/globals.ts +47 -0
  54. package/src/index.ts +1 -0
  55. package/src/navigate/index.ts +2 -5
  56. package/src/navigate/lifecycle.ts +3 -1
  57. package/src/navigate/runtime.ts +6 -5
  58. package/src/page-update/index.ts +20 -16
  59. package/src/search/index.ts +53 -25
  60. package/src/slides/index.ts +467 -0
  61. package/src/slides/style.scss +104 -0
  62. package/src/style.scss +8 -3
  63. package/src/timezone/index.ts +16 -20
  64. package/src/top/index.ts +2 -4
  65. package/src/trace/index.ts +91 -7
  66. package/src/trace/style.scss +1 -1
  67. package/src/util.ts +6 -2
  68. package/templates/_author.html +2 -2
  69. package/templates/_heading.html +1 -1
  70. package/templates/_nav.html +2 -2
  71. package/templates/_theme.scss +3 -3
  72. package/templates/_top.html +14 -14
  73. package/templates/default.html +8 -0
  74. package/types/dev.d.ts +1 -1
  75. package/types/site-variables.d.ts +4 -5
  76. package/watch/fs-commit.ts +63 -10
  77. package/build/apply-base-path-plugin.ts +0 -186
  78. package/build/content-watch.ts +0 -104
  79. package/build/validate-internal-links-plugin.ts +0 -207
@@ -0,0 +1,104 @@
1
+ @use '../mixins' as *;
2
+
3
+ .slide-deck,
4
+ .slide {
5
+ display: block;
6
+ }
7
+
8
+ body.is-presenting {
9
+ --slides-scale: clamp(150%, 3.6vmin, 250%);
10
+
11
+ overflow: hidden;
12
+ }
13
+
14
+ body.is-presenting.is-presentation-cursor-hidden,
15
+ body.is-presenting.is-presentation-cursor-hidden * {
16
+ cursor: none !important;
17
+ }
18
+
19
+ body.is-presenting header,
20
+ body.is-presenting .title-and-info,
21
+ body.is-presenting nav.toc,
22
+ body.is-presenting footer,
23
+ body.is-presenting .slides-header,
24
+ body.is-presenting .trace-toolbar {
25
+ display: none;
26
+ }
27
+
28
+ @media (hover: none) {
29
+ .slides-header {
30
+ display: none;
31
+ }
32
+ }
33
+
34
+ body.is-presenting [data-slides-close] {
35
+ position: fixed;
36
+ top: var(--gap);
37
+ right: var(--gap);
38
+ z-index: 1001;
39
+ }
40
+
41
+ @media print {
42
+ [data-slides-close] {
43
+ display: none;
44
+ }
45
+ }
46
+
47
+ body.is-presenting .slide-deck {
48
+ position: fixed;
49
+ inset: 0;
50
+ z-index: 999;
51
+ background: var(--bg-color);
52
+ }
53
+
54
+ body.is-presenting .slide {
55
+ @include user-select(none);
56
+
57
+ position: fixed;
58
+ top: 50%;
59
+ left: 50%;
60
+ z-index: 1000;
61
+ box-sizing: border-box;
62
+ display: none;
63
+ width: min(100vw, calc(100vh * 4 / 3));
64
+ height: min(100vh, calc(100vw * 3 / 4));
65
+ padding: calc(3 * var(--gap));
66
+ overflow: auto;
67
+ font-size: var(--slides-scale);
68
+ letter-spacing: 0.2px;
69
+ outline: none;
70
+ background: var(--bg-color);
71
+ transform: translate(-50%, -50%);
72
+
73
+ > :first-child {
74
+ margin-top: 0;
75
+ }
76
+
77
+ > h2:first-child {
78
+ text-align: center;
79
+
80
+ a.heading-anchor {
81
+ color: var(--theme-color-text);
82
+ }
83
+ }
84
+ }
85
+
86
+ body.is-presenting .slide.is-active {
87
+ display: block;
88
+ }
89
+
90
+ body.is-presenting .slide.is-active:has(> .trace-widget) {
91
+ display: flex;
92
+ flex-direction: column;
93
+ }
94
+
95
+ body.is-presenting .slide:has(> .trace-widget) > .trace-widget {
96
+ flex: 1 1 auto;
97
+ min-height: 0;
98
+ font-size: 66.6%;
99
+ }
100
+
101
+ body.is-presenting .slide:has(> .trace-widget) > .trace-widget > .trace-body {
102
+ height: 100%;
103
+ min-height: 0;
104
+ }
package/src/style.scss CHANGED
@@ -235,12 +235,17 @@ ul.styled-list {
235
235
 
236
236
  ul.styled-list {
237
237
  // Make bullet points slightly bigger
238
- font-size: 1.2rem;
238
+ font-size: 1.2em;
239
+ }
240
+
241
+ ol li .styled-list-item {
242
+ font-size: 1em;
243
+ font-weight: var(--font-weight);
244
+ color: var(--fg-color);
239
245
  }
240
246
 
241
- ol li .styled-list-item,
242
247
  ul li .styled-list-item {
243
- font-size: 1rem;
248
+ font-size: 0.8333em;
244
249
  font-weight: var(--font-weight);
245
250
  color: var(--fg-color);
246
251
  }
@@ -45,6 +45,11 @@ export function normalizeHM(totalMinutes: number) {
45
45
  return [h, m] as const;
46
46
  }
47
47
 
48
+ function getDayShift(totalMinutes: number) {
49
+ // Negative partial days like -30 minutes should still count as the previous day.
50
+ return Math.floor(totalMinutes / 1440);
51
+ }
52
+
48
53
  function parseHHMM(hhmm: string) {
49
54
  const [h, m] = hhmm.split(':');
50
55
  if (h == null || m == null) {
@@ -85,26 +90,16 @@ function getOffsetMinutes(tz: string, date: Date): number {
85
90
  return (utcTs - date.getTime()) / 60000;
86
91
  }
87
92
 
88
- // Read lazily so tests can set __SITE_TIMEZONES__ before the first call.
89
- // Cache the result so mutations (like computeOffsets adding offsetMinutes)
90
- // persist across calls. The bundler inlines __SITE_TIMEZONES__ as a JSON
91
- // literal, so each raw access would create a fresh array.
92
- let _timezones: TimeZone[] | null = null;
93
93
  function getTimezones(): TimeZone[] {
94
- if (_timezones === null) {
95
- _timezones =
96
- typeof __SITE_TIMEZONES__ !== 'undefined' ? __SITE_TIMEZONES__ : [];
97
- }
98
- return _timezones;
94
+ return __SITE_TIMEZONES__.map(tz => ({ ...tz }));
99
95
  }
100
96
 
101
- function getDefaultTimezone() {
102
- const value = __SITE_DEFAULT_TIMEZONE__;
103
- return getTimezones().find(tz => tz.value === value)!;
97
+ function getDefaultTimezone(timezones: TimeZone[]): TimeZone {
98
+ return timezones.find(tz => tz.value === __SITE_DEFAULT_TIMEZONE__)!;
104
99
  }
105
100
 
106
- function computeOffsets(baseDate: Date) {
107
- getTimezones().forEach(tz => {
101
+ function computeOffsets(timezones: TimeZone[], baseDate: Date) {
102
+ timezones.forEach(tz => {
108
103
  tz.offsetMinutes = getOffsetMinutes(tz.value, baseDate);
109
104
  });
110
105
  }
@@ -143,14 +138,15 @@ function dominantPeriodStyle(
143
138
  }
144
139
 
145
140
  export default (window: Window) => {
146
- const defaultTz = getDefaultTimezone();
141
+ const timezones = getTimezones();
142
+ const defaultTz = getDefaultTimezone(timezones);
147
143
  const resetTitle = `Reset time zone to ${defaultTz.abbreviation} (default)`;
148
144
 
149
145
  // determine initial time zone (from storage or default)
150
146
  let initialTz = defaultTz.value;
151
147
  try {
152
148
  const stored = window.localStorage.getItem(STORAGE_KEY);
153
- if (stored && getTimezones().some(t => t.value === stored)) {
149
+ if (stored && timezones.some(t => t.value === stored)) {
154
150
  initialTz = stored;
155
151
  }
156
152
  } catch {
@@ -158,7 +154,7 @@ export default (window: Window) => {
158
154
  }
159
155
 
160
156
  const now = new Date();
161
- computeOffsets(now);
157
+ computeOffsets(timezones, now);
162
158
  const defaultOffset = defaultTz.offsetMinutes ?? 0;
163
159
 
164
160
  // Snapshot the original AM/PM style from each <time> element's text
@@ -173,7 +169,7 @@ export default (window: Window) => {
173
169
  const pagePeriodStyle = dominantPeriodStyle(periodStyles);
174
170
 
175
171
  function updateTimes(targetTz: string) {
176
- const target = getTimezones().find(t => t.value === targetTz) || defaultTz;
172
+ const target = timezones.find(t => t.value === targetTz) || defaultTz;
177
173
  const targetOffset = target.offsetMinutes ?? 0;
178
174
  const deltaMinutes = targetOffset - defaultOffset;
179
175
 
@@ -196,7 +192,7 @@ export default (window: Window) => {
196
192
 
197
193
  // raw (can be < 0 or > 1439)
198
194
  const rawMinutes = baseMinutes + deltaMinutes;
199
- const dayShift = Math.floor(rawMinutes / 1440) - (rawMinutes < 0 ? 1 : 0);
195
+ const dayShift = getDayShift(rawMinutes);
200
196
  // Normalize after computing dayShift
201
197
  const [h, m] = normalizeHM(rawMinutes);
202
198
 
package/src/top/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { debounce, removeClass } from '../util';
2
+ import { globals } from '../globals';
2
3
 
3
4
  /** Show the "Back to top" button once the user is past this scroll position */
4
5
  const SHOW_THRESHOLD_PX = 250;
@@ -30,10 +31,7 @@ export default (window: Window) => {
30
31
  e.preventDefault();
31
32
  const cleanUrl = window.location.pathname + window.location.search;
32
33
  if (window.location.hash) {
33
- // Clear the hash via fragment navigation so :target clears and
34
- // hashchange fires. location.hash = '' may leave a trailing '#';
35
- // strip it with replaceState.
36
- window.location.hash = '';
34
+ globals.setLocationHash(window, '');
37
35
  window.history.replaceState(null, '', cleanUrl);
38
36
  } else {
39
37
  window.history.replaceState(null, '', cleanUrl);
@@ -1,4 +1,5 @@
1
1
  import type { TraceManifest, TraceChunkEntry } from './types';
2
+ import { globals } from '../globals';
2
3
 
3
4
  interface WidgetState {
4
5
  manifest: TraceManifest;
@@ -33,7 +34,7 @@ async function loadChunk(
33
34
  return;
34
35
  }
35
36
  const url = chunkUrlFromManifest(manifestUrl, chunkIndex);
36
- const res = await fetch(url);
37
+ const res = await globals.fetch(url);
37
38
  const chunk: TraceChunkEntry[] = await res.json();
38
39
  state.chunks.set(chunkIndex, chunk);
39
40
  }
@@ -135,6 +136,63 @@ function updateStepControls(
135
136
  }
136
137
  }
137
138
 
139
+ function parsePositiveNumber(
140
+ rawValue: string | null | undefined,
141
+ ): number | null {
142
+ if (!rawValue) {
143
+ return null;
144
+ }
145
+
146
+ const parsed = Number.parseFloat(rawValue);
147
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
148
+ }
149
+
150
+ function getPresentationScale(diagram: HTMLElement, doc: Document): number {
151
+ if (!doc.body.classList.contains('is-presenting')) {
152
+ return 1;
153
+ }
154
+
155
+ const view = doc.defaultView;
156
+ const traceWidget = diagram.closest('.trace-widget') as HTMLElement | null;
157
+ const traceFontSize = parsePositiveNumber(
158
+ traceWidget && view ? view.getComputedStyle(traceWidget).fontSize : null,
159
+ );
160
+ const baseFontSize = parsePositiveNumber(
161
+ view ? view.getComputedStyle(doc.body).fontSize : null,
162
+ );
163
+ if (traceFontSize && baseFontSize) {
164
+ return traceFontSize / baseFontSize;
165
+ }
166
+
167
+ return 1;
168
+ }
169
+
170
+ function scaleDiagramSvg(diagram: HTMLElement, doc: Document): void {
171
+ const svg = diagram.querySelector('.trace-memory') as SVGElement | null;
172
+ if (!svg) {
173
+ return;
174
+ }
175
+
176
+ const width = svg.dataset.traceBaseWidth ?? svg.getAttribute('width');
177
+ const height = svg.dataset.traceBaseHeight ?? svg.getAttribute('height');
178
+ if (!width || !height) {
179
+ return;
180
+ }
181
+
182
+ svg.dataset.traceBaseWidth = width;
183
+ svg.dataset.traceBaseHeight = height;
184
+
185
+ const baseWidth = Number.parseFloat(width);
186
+ const baseHeight = Number.parseFloat(height);
187
+ if (!Number.isFinite(baseWidth) || !Number.isFinite(baseHeight)) {
188
+ return;
189
+ }
190
+
191
+ const scale = getPresentationScale(diagram, doc);
192
+ svg.setAttribute('width', String(baseWidth * scale));
193
+ svg.setAttribute('height', String(baseHeight * scale));
194
+ }
195
+
138
196
  function renderWidgetState(
139
197
  state: WidgetState,
140
198
  elements: WidgetElements,
@@ -161,6 +219,7 @@ function renderWidgetState(
161
219
 
162
220
  const prevHeight = elements.diagram.scrollHeight;
163
221
  elements.diagram.innerHTML = entry.svg;
222
+ scaleDiagramSvg(elements.diagram, doc);
164
223
 
165
224
  // If the diagram grew past the visible area, scroll to show the new content
166
225
  if (elements.diagram.scrollHeight > prevHeight) {
@@ -177,7 +236,7 @@ async function initWidget(root: HTMLElement, doc: Document): Promise<void> {
177
236
 
178
237
  let res: Response;
179
238
  try {
180
- res = await fetch(manifestUrl);
239
+ res = await globals.fetch(manifestUrl);
181
240
  } catch {
182
241
  return;
183
242
  }
@@ -239,20 +298,45 @@ async function initWidget(root: HTMLElement, doc: Document): Promise<void> {
239
298
 
240
299
  const elements: WidgetElements = { root, source, controls, output, diagram };
241
300
 
242
- updateStepControls(controls, state.currentStep, manifest.totalSteps, doc);
243
-
244
- diagram.innerHTML = entry.svg;
245
- updateSourceHighlight(source, entry.line);
301
+ renderWidgetState(state, elements, doc);
246
302
  }
247
303
 
248
- export default function mountTrace(window: Window): void {
304
+ export default function mountTrace(window: Window): void | (() => void) {
249
305
  const widgets = window.document.querySelectorAll('.trace-widget');
250
306
 
251
307
  if (widgets.length === 0) {
252
308
  return;
253
309
  }
254
310
 
311
+ const refreshDiagramScales = (): void => {
312
+ for (const widget of widgets) {
313
+ const diagram = (widget as HTMLElement).querySelector(
314
+ '.trace-diagram',
315
+ ) as HTMLElement | null;
316
+ if (diagram) {
317
+ scaleDiagramSvg(diagram, window.document);
318
+ }
319
+ }
320
+ };
321
+
322
+ const MutationObserverCtor = (window as Window & typeof globalThis)
323
+ .MutationObserver;
324
+ const bodyClassObserver = MutationObserverCtor
325
+ ? new MutationObserverCtor(() => {
326
+ refreshDiagramScales();
327
+ })
328
+ : null;
329
+
330
+ bodyClassObserver?.observe(window.document.body, {
331
+ attributes: true,
332
+ attributeFilter: ['class'],
333
+ });
334
+
255
335
  for (const widget of widgets) {
256
336
  initWidget(widget as HTMLElement, window.document);
257
337
  }
338
+
339
+ return () => {
340
+ bodyClassObserver?.disconnect();
341
+ };
258
342
  }
@@ -144,7 +144,7 @@
144
144
  max-width: 100%;
145
145
  height: auto;
146
146
  font-family: var(--mono-font, monospace);
147
- font-size: var(--font-size-smaller, 15px);
147
+ font-size: 15px;
148
148
 
149
149
  .trace-separator {
150
150
  stroke: var(--fg2-color);
package/src/util.ts CHANGED
@@ -30,18 +30,22 @@ export function getElement(
30
30
  return el as HTMLElement;
31
31
  }
32
32
 
33
- export function applyBasePath(subPath: string): string {
33
+ function applyBasePathWithBasePath(subPath: string, basePath: string): string {
34
34
  if (!subPath.startsWith('/')) {
35
35
  throw new Error('invalid internal path, must start with "/": ' + subPath);
36
36
  }
37
37
 
38
- let path = __SITE_BASE_PATH__;
38
+ let path = basePath;
39
39
  if (path.endsWith('/')) {
40
40
  path = path.slice(0, -1);
41
41
  }
42
42
  return path + subPath;
43
43
  }
44
44
 
45
+ export function applyBasePath(subPath: string): string {
46
+ return applyBasePathWithBasePath(subPath, __SITE_BASE_PATH__);
47
+ }
48
+
45
49
  export function scheduleTask(win: Window, fn: () => void) {
46
50
  if (typeof win.requestIdleCallback === 'function') {
47
51
  win.requestIdleCallback(fn);
@@ -1,14 +1,14 @@
1
1
  <aside class="author">
2
2
  Written by
3
3
  <% if (page.author.url) { %>
4
- <a href="<%= applyBasePath(page.author.url) %>">
4
+ <a href="<%= page.author.url %>">
5
5
  <% } %>
6
6
  <span class="text"><%= page.author.name %></span>
7
7
  <% if (page.author.avatar) { %>
8
8
  <img
9
9
  width="32"
10
10
  height="32"
11
- src="<%= applyBasePath(page.author.avatar) %>"
11
+ src="<%= page.author.avatar %>"
12
12
  alt="Picture of <%= page.author.name %>"
13
13
  class="avatar"
14
14
  />
@@ -1,6 +1,6 @@
1
1
  <div class="title-and-info" data-pagefind-body>
2
2
  <% if (page.parent) { %>
3
- <a class="breadcrumb" href="<%= applyBasePath(page.parent) %>" data-pagefind-ignore><%= page.parentLabel %></a>
3
+ <a class="breadcrumb" href="<%= page.parent %>" data-pagefind-ignore><%= page.parentLabel %></a>
4
4
  <% } %>
5
5
  <h1 data-pagefind-weight="10" data-pagefind-meta="title_html[data-title-html]" data-title-html="<%- page.titleHtml %>"><%= page.titleHtml %></h1>
6
6
 
@@ -1,5 +1,5 @@
1
1
  <nav>
2
- <% json('nav.json').forEach(function(section) { %>
2
+ <% config('nav').forEach(function(section) { %>
3
3
  <div>
4
4
  <p><%= section.title %></p>
5
5
  <ul>
@@ -7,7 +7,7 @@
7
7
  <li>
8
8
  <a
9
9
  class="<%= cx({disabled: link.disabled, external: link.external}) %>"
10
- <% if (!link.disabled) { %>href="<%= link.internal ? applyBasePath(link.internal) : link.external %>"<% } %>
10
+ <% if (!link.disabled) { %>href="<%= link.internal || link.external %>"<% } %>
11
11
  <% if (link.external) { %>target="_blank"<% } %>
12
12
  >
13
13
  <%= link.text %></a>
@@ -42,9 +42,9 @@
42
42
  --heading-font: var(--font);
43
43
  --heading-line-height: 1.3;
44
44
  --mono-font: 'Google Sans Code', 'Consolas', 'Courier', monospace;
45
- --font-size: 16px;
46
- --font-size-smaller: 15px;
47
- --font-size-small: 13px;
45
+ --font-size: 1rem;
46
+ --font-size-smaller: 0.9375em;
47
+ --font-size-small: 0.8125em;
48
48
  --font-weight: 400;
49
49
  --line-height: 1.7;
50
50
  --mono-font-size: 0.94em;
@@ -2,19 +2,19 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <% if (site.features.favicon) { %>
5
- <link rel="icon" href="<%= applyBasePath('/favicon.ico') %>" sizes="any" />
6
- <link rel="icon" type="image/svg+xml" href="<%= applyBasePath('/favicon.svg') %>" />
7
- <link rel="icon" type="image/png" sizes="512x512" href="<%= applyBasePath('/favicon-512.png') %>" />
8
- <link rel="icon" type="image/png" sizes="256x256" href="<%= applyBasePath('/favicon-256.png') %>" />
9
- <link rel="icon" type="image/png" sizes="192x192" href="<%= applyBasePath('/favicon-192.png') %>" />
10
- <link rel="icon" type="image/png" sizes="128x128" href="<%= applyBasePath('/favicon-128.png') %>" />
11
- <link rel="icon" type="image/png" sizes="64x64" href="<%= applyBasePath('/favicon-64.png') %>" />
12
- <link rel="icon" type="image/png" sizes="48x48" href="<%= applyBasePath('/favicon-48.png') %>" />
13
- <link rel="icon" type="image/png" sizes="32x32" href="<%= applyBasePath('/favicon-32.png') %>" />
14
- <link rel="icon" type="image/png" sizes="16x16" href="<%= applyBasePath('/favicon-16.png') %>" />
15
- <link rel="apple-touch-icon" sizes="512x512" href="<%= applyBasePath('/favicon-512.png') %>" />
16
- <link rel="apple-touch-icon" sizes="256x256" href="<%= applyBasePath('/favicon-256.png') %>" />
17
- <link rel="manifest" href="<%= applyBasePath('/manifest.json') %>" />
5
+ <link rel="icon" href="/favicon.ico" sizes="any" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
+ <link rel="icon" type="image/png" sizes="512x512" href="/favicon-512.png" />
8
+ <link rel="icon" type="image/png" sizes="256x256" href="/favicon-256.png" />
9
+ <link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
10
+ <link rel="icon" type="image/png" sizes="128x128" href="/favicon-128.png" />
11
+ <link rel="icon" type="image/png" sizes="64x64" href="/favicon-64.png" />
12
+ <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48.png" />
13
+ <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
14
+ <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
15
+ <link rel="apple-touch-icon" sizes="512x512" href="/favicon-512.png" />
16
+ <link rel="apple-touch-icon" sizes="256x256" href="/favicon-256.png" />
17
+ <link rel="manifest" href="/manifest.json" />
18
18
  <meta name="apple-mobile-web-app-title" content="<%= site.symbol %>">
19
19
  <meta name="mobile-web-app-capable" content="yes">
20
20
  <% } %>
@@ -38,7 +38,7 @@
38
38
  {
39
39
  "prerender": [{
40
40
  "where": {
41
- "href_matches": "<%= applyBasePath('/') %>*"
41
+ "href_matches": "<%= speculationRulesHrefMatches %>"
42
42
  },
43
43
  "eagerness": "moderate"
44
44
  }]
@@ -3,6 +3,14 @@
3
3
  <div class="main-content">
4
4
  <%= render('_heading.html') %>
5
5
  <main class="body" data-pagefind-body>
6
+ <% if (page.slides) { %>
7
+ <div class="slides-header" data-pagefind-ignore>
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>
12
+ </div>
13
+ <% } %>
6
14
  <%= content %>
7
15
  <%= render('_footer.html') %>
8
16
  </main>
package/types/dev.d.ts CHANGED
@@ -1 +1 @@
1
- declare const __IS_DEV__: boolean;
1
+ declare var __IS_DEV__: boolean;
@@ -5,8 +5,7 @@ interface TimeZone {
5
5
  offsetMinutes?: number;
6
6
  }
7
7
 
8
- declare const __SITE_BASE__: string;
9
- declare const __SITE_BASE_PATH__: string;
10
- declare const __SITE_TITLE_POSTFIX__: string;
11
- declare const __SITE_DEFAULT_TIMEZONE__: string;
12
- declare const __SITE_TIMEZONES__: TimeZone[];
8
+ declare var __SITE_BASE_PATH__: string;
9
+ declare var __SITE_TITLE_POSTFIX__: string;
10
+ declare var __SITE_DEFAULT_TIMEZONE__: string;
11
+ declare var __SITE_TIMEZONES__: TimeZone[];