@abreen/tada 1.15.3 → 1.15.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.15.3",
3
+ "version": "1.15.5",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -0,0 +1,29 @@
1
+ /*
2
+ * markdown-it-anchor's slugifier uses encodeURIComponent() so generated
3
+ * heading IDs can safely be used as URL hashes.
4
+ *
5
+ * Tada also allows raw HTML, so an author can create a decoded ID like
6
+ * <h2 id="hello world">Hello</h2>. A link to that target becomes
7
+ * #hello%20world, and getElementById('hello%20world') would not find it.
8
+ */
9
+ export function getHashTarget(
10
+ document: Document,
11
+ hash: string,
12
+ ): HTMLElement | null {
13
+ const id = hash.slice(1);
14
+ if (!id) {
15
+ return null;
16
+ }
17
+
18
+ const rawTarget = document.getElementById(id);
19
+ if (rawTarget) {
20
+ return rawTarget;
21
+ }
22
+
23
+ try {
24
+ const decodedId = decodeURIComponent(id);
25
+ return decodedId === id ? null : document.getElementById(decodedId);
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ import mountTop from './top';
21
21
  import mountNavigate from './navigate';
22
22
  import mountPageUpdate from './page-update';
23
23
  import { mountPerPageComponents } from './navigate/lifecycle';
24
+ import { getHashTarget } from './hash-target';
24
25
 
25
26
  import { scheduleTask, formatDuration } from './util';
26
27
 
@@ -68,11 +69,7 @@ document.addEventListener('DOMContentLoaded', async () => {
68
69
  // finished mutating the DOM. On a fresh cold load the browser already
69
70
  // scrolled, so this is a harmless re-align.
70
71
  if (window.location.hash) {
71
- const id = window.location.hash.slice(1);
72
- const el = window.document.getElementById(id);
73
- if (el) {
74
- el.scrollIntoView();
75
- }
72
+ getHashTarget(window.document, window.location.hash)?.scrollIntoView();
76
73
  }
77
74
 
78
75
  for (const [name, reason] of Object.entries(failed)) {
@@ -13,6 +13,7 @@ import {
13
13
  setHistoryIndex,
14
14
  } from './runtime';
15
15
  import { globals } from '../globals';
16
+ import { getHashTarget } from '../hash-target';
16
17
 
17
18
  function findAnchor(event: MouseEvent): HTMLAnchorElement | null {
18
19
  const target = event.target as HTMLElement | null;
@@ -78,7 +79,11 @@ export default function mountNavigate(window: Window): () => void {
78
79
  clearSearch(window.document);
79
80
  closeHeaderDetails(window.document);
80
81
  if (url.hash) {
81
- globals.setLocationHash(window, url.hash);
82
+ if (url.hash === window.location.hash) {
83
+ getHashTarget(window.document, url.hash)?.scrollIntoView();
84
+ } else {
85
+ globals.setLocationHash(window, url.hash);
86
+ }
82
87
  }
83
88
  return;
84
89
  }
@@ -112,12 +117,8 @@ export default function mountNavigate(window: Window): () => void {
112
117
  }
113
118
 
114
119
  // Same page, different hash: fall back to the fragment target
115
- const hash = window.location.hash.slice(1);
116
- if (hash) {
117
- const el = window.document.getElementById(hash);
118
- if (el) {
119
- el.scrollIntoView();
120
- }
120
+ if (window.location.hash) {
121
+ getHashTarget(window.document, window.location.hash)?.scrollIntoView();
121
122
  } else {
122
123
  window.scrollTo({ top: 0 });
123
124
  }
@@ -144,6 +144,7 @@ export default function mountSlides(window: Window): void | (() => void) {
144
144
  let isAnnotating = false;
145
145
  let isShiftPressed = false;
146
146
  let isErasing = false;
147
+ let isDrivingTrace = false;
147
148
  let activeAnnotation: {
148
149
  slide: HTMLElement;
149
150
  previousX: number;
@@ -374,7 +375,12 @@ export default function mountSlides(window: Window): void | (() => void) {
374
375
  continue;
375
376
  }
376
377
 
377
- button.click();
378
+ isDrivingTrace = true;
379
+ try {
380
+ button.click();
381
+ } finally {
382
+ isDrivingTrace = false;
383
+ }
378
384
  return true;
379
385
  }
380
386
 
@@ -863,7 +869,7 @@ export default function mountSlides(window: Window): void | (() => void) {
863
869
  }
864
870
 
865
871
  function handleAnnotationClick(event: MouseEvent): void {
866
- if (!isPresenting || !isAnnotating) {
872
+ if (!isPresenting || !isAnnotating || isDrivingTrace) {
867
873
  return;
868
874
  }
869
875