@abreen/tada 1.15.2 → 1.15.3

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 CHANGED
@@ -26,6 +26,9 @@ A static site generator. The successor to Presto.
26
26
  * Supports Java and Python source files
27
27
  * Renders step-by-step source highlighting, output, and memory diagrams
28
28
  - Slides mode: separate content by `---` and present sections as full-screen slides
29
+ * Click icon near slide titles to start presenting at that slide
30
+ * Right-click in presentation mode to annotate
31
+ * Hold Shift while annotating to erase
29
32
  - PDF files are copied into `dist/`
30
33
  * Text of each PDF page is extracted using `mutool` (if present) and indexed
31
34
  - External link handling (special visual treatment for external links)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abreen/tada",
3
- "version": "1.15.2",
3
+ "version": "1.15.3",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -9,34 +9,14 @@ interface SlidesPresentDetail {
9
9
  slideIndex?: number;
10
10
  }
11
11
 
12
- interface AnnotationPoint {
13
- x: number;
14
- y: number;
15
- }
16
-
17
- interface AnnotationStroke {
18
- type: 'stroke';
19
- points: AnnotationPoint[];
20
- }
21
-
22
- interface AnnotationErase {
23
- type: 'erase';
24
- points: AnnotationPoint[];
25
- radius: number;
26
- }
27
-
28
- type AnnotationOperation = AnnotationStroke | AnnotationErase;
29
-
30
12
  interface SlideAnnotationState {
31
13
  canvas: HTMLCanvasElement;
32
- operations: AnnotationOperation[];
33
14
  width: number;
34
15
  height: number;
35
16
  dpr: number;
36
17
  }
37
18
 
38
19
  const FULLSCREEN_STORAGE_KEY = 'slidesFullscreen';
39
- const ANNOTATION_CANVAS_SELECTOR = '[data-slides-annotations]';
40
20
  const ANNOTATION_COLOR = 'blueviolet';
41
21
  const ANNOTATION_ERASER_RADIUS = 18;
42
22
  const ANNOTATION_ERASER_DIAMETER = ANNOTATION_ERASER_RADIUS * 2;
@@ -151,6 +131,12 @@ export default function mountSlides(window: Window): void | (() => void) {
151
131
  );
152
132
  document.body.appendChild(eraserPreview);
153
133
 
134
+ const annotationLayer = document.createElement('div');
135
+ annotationLayer.hidden = true;
136
+ annotationLayer.dataset.slidesAnnotationLayer = '';
137
+ annotationLayer.setAttribute('aria-hidden', 'true');
138
+ slidesRoot.append(annotationLayer);
139
+
154
140
  let isPresenting = false;
155
141
  let activeIndex = -1;
156
142
  let presentationMode: PresentationMode | null = null;
@@ -160,10 +146,9 @@ export default function mountSlides(window: Window): void | (() => void) {
160
146
  let isErasing = false;
161
147
  let activeAnnotation: {
162
148
  slide: HTMLElement;
163
- stroke: AnnotationStroke;
149
+ previousX: number;
150
+ previousY: number;
164
151
  } | null = null;
165
- let activeEraser: { slide: HTMLElement; erase: AnnotationErase } | null =
166
- null;
167
152
 
168
153
  const toolbarStates = new WeakMap<HTMLElement, TraceToolbarState>();
169
154
  const slideTabIndexes = new WeakMap<HTMLElement, string | null>();
@@ -249,10 +234,10 @@ export default function mountSlides(window: Window): void | (() => void) {
249
234
 
250
235
  function setActiveSlide(index: number): void {
251
236
  activeAnnotation = null;
252
- activeEraser = null;
253
237
  hideEraserPreview();
254
238
  activeIndex = Math.max(0, Math.min(index, slides.length - 1));
255
239
  updateActiveSlide(slides, activeIndex, isPresenting);
240
+ updateActiveAnnotationCanvas();
256
241
  resizeActiveAnnotationCanvas();
257
242
  }
258
243
 
@@ -308,6 +293,7 @@ export default function mountSlides(window: Window): void | (() => void) {
308
293
  isShiftPressed = false;
309
294
  setAnnotationMode(false);
310
295
  clearAnnotationCanvases();
296
+ annotationLayer.hidden = true;
311
297
  hidePresentationControls();
312
298
  showPresentationCursor();
313
299
  document.body.classList.remove('is-presenting');
@@ -350,6 +336,7 @@ export default function mountSlides(window: Window): void | (() => void) {
350
336
  function enterPresentation(mode: PresentationMode, slideIndex = 0): void {
351
337
  isPresenting = true;
352
338
  presentationMode = mode;
339
+ annotationLayer.hidden = false;
353
340
  setAnnotationMode(false);
354
341
  hidePresentationControls();
355
342
  showPresentationCursor();
@@ -540,7 +527,6 @@ export default function mountSlides(window: Window): void | (() => void) {
540
527
  function setAnnotationMode(enabled: boolean): void {
541
528
  isAnnotating = enabled;
542
529
  activeAnnotation = null;
543
- activeEraser = null;
544
530
  hideEraserPreview();
545
531
  document.body.classList.toggle('is-slides-annotating', enabled);
546
532
  setEraserMode(enabled && isShiftPressed);
@@ -552,7 +538,6 @@ export default function mountSlides(window: Window): void | (() => void) {
552
538
 
553
539
  function setEraserMode(enabled: boolean): void {
554
540
  isErasing = enabled && isPresenting && isAnnotating;
555
- activeEraser = null;
556
541
 
557
542
  if (isErasing) {
558
543
  activeAnnotation = null;
@@ -577,33 +562,24 @@ export default function mountSlides(window: Window): void | (() => void) {
577
562
  setAnnotationMode(!isAnnotating);
578
563
  }
579
564
 
580
- function getSlidePoint(
581
- slide: HTMLElement,
565
+ function getViewportPoint(
582
566
  event: Pick<PointerEvent, 'clientX' | 'clientY'>,
583
- ): AnnotationPoint | null {
584
- const rect = slide.getBoundingClientRect();
585
- if (rect.width <= 0 || rect.height <= 0) {
567
+ ): { x: number; y: number } | null {
568
+ const width = window.innerWidth;
569
+ const height = window.innerHeight;
570
+
571
+ if (
572
+ width <= 0 ||
573
+ height <= 0 ||
574
+ event.clientX < 0 ||
575
+ event.clientX > width ||
576
+ event.clientY < 0 ||
577
+ event.clientY > height
578
+ ) {
586
579
  return null;
587
580
  }
588
581
 
589
- const x = Math.min(Math.max(event.clientX - rect.left, 0), rect.width);
590
- const y = Math.min(Math.max(event.clientY - rect.top, 0), rect.height);
591
-
592
- return { x: x / rect.width, y: y / rect.height };
593
- }
594
-
595
- function eventIsInsideSlide(
596
- slide: HTMLElement,
597
- event: Pick<PointerEvent, 'clientX' | 'clientY'>,
598
- ): boolean {
599
- const rect = slide.getBoundingClientRect();
600
-
601
- return (
602
- event.clientX >= rect.left &&
603
- event.clientX <= rect.right &&
604
- event.clientY >= rect.top &&
605
- event.clientY <= rect.bottom
606
- );
582
+ return { x: event.clientX, y: event.clientY };
607
583
  }
608
584
 
609
585
  function getCanvasContext(
@@ -624,41 +600,46 @@ export default function mountSlides(window: Window): void | (() => void) {
624
600
 
625
601
  const canvas = document.createElement('canvas');
626
602
  canvas.dataset.slidesAnnotations = '';
603
+ canvas.dataset.slideIndex = slide.getAttribute('data-slide-index') ?? '';
627
604
  canvas.setAttribute('aria-hidden', 'true');
628
- slide.append(canvas);
629
-
630
- const state: SlideAnnotationState = {
631
- canvas,
632
- operations: [],
633
- width: 0,
634
- height: 0,
635
- dpr: 1,
636
- };
605
+ canvas.hidden = slide !== getActiveSlide();
606
+ annotationLayer.append(canvas);
607
+
608
+ const state: SlideAnnotationState = { canvas, width: 0, height: 0, dpr: 1 };
637
609
  annotationStates.set(slide, state);
638
- resizeAnnotationCanvas(slide, state);
610
+ resizeAnnotationCanvas(state);
639
611
 
640
612
  return state;
641
613
  }
642
614
 
643
- function resizeAnnotationCanvas(
644
- slide: HTMLElement,
645
- state: SlideAnnotationState,
646
- ): void {
647
- const rect = slide.getBoundingClientRect();
648
- const width = Math.max(1, rect.width);
649
- const height = Math.max(1, rect.height);
615
+ function resizeAnnotationCanvas(state: SlideAnnotationState): void {
616
+ const width = Math.max(1, window.innerWidth);
617
+ const height = Math.max(1, window.innerHeight);
650
618
  const dpr = Math.max(1, window.devicePixelRatio || 1);
651
619
 
652
620
  if (state.width === width && state.height === height && state.dpr === dpr) {
653
621
  return;
654
622
  }
655
623
 
624
+ const priorCanvas =
625
+ state.canvas.width > 0 && state.canvas.height > 0
626
+ ? document.createElement('canvas')
627
+ : null;
628
+ if (priorCanvas) {
629
+ priorCanvas.width = state.canvas.width;
630
+ priorCanvas.height = state.canvas.height;
631
+ priorCanvas.getContext('2d')?.drawImage(state.canvas, 0, 0);
632
+ }
633
+
656
634
  state.width = width;
657
635
  state.height = height;
658
636
  state.dpr = dpr;
659
637
  state.canvas.width = Math.round(width * dpr);
660
638
  state.canvas.height = Math.round(height * dpr);
661
- redrawAnnotationCanvas(state);
639
+
640
+ if (priorCanvas) {
641
+ getCanvasContext(state.canvas)?.drawImage(priorCanvas, 0, 0);
642
+ }
662
643
  }
663
644
 
664
645
  function resizeActiveAnnotationCanvas(): void {
@@ -669,124 +650,75 @@ export default function mountSlides(window: Window): void | (() => void) {
669
650
 
670
651
  const state = annotationStates.get(slide);
671
652
  if (state) {
672
- resizeAnnotationCanvas(slide, state);
673
- }
674
- }
675
-
676
- function redrawAnnotationCanvas(state: SlideAnnotationState): void {
677
- const ctx = getCanvasContext(state.canvas);
678
- if (!ctx) {
679
- return;
680
- }
681
-
682
- ctx.clearRect(0, 0, state.canvas.width, state.canvas.height);
683
-
684
- for (const operation of state.operations) {
685
- if (operation.type === 'stroke') {
686
- drawAnnotationStroke(ctx, operation, state);
687
- } else {
688
- drawAnnotationErase(ctx, operation, state);
689
- }
653
+ resizeAnnotationCanvas(state);
690
654
  }
691
655
  }
692
656
 
693
657
  function drawAnnotationStroke(
694
658
  ctx: CanvasRenderingContext2D,
695
- stroke: AnnotationStroke,
696
659
  state: SlideAnnotationState,
660
+ from: { x: number; y: number },
661
+ to: { x: number; y: number },
697
662
  ): void {
698
- if (stroke.points.length === 0) {
699
- return;
700
- }
701
-
702
663
  ctx.globalCompositeOperation = 'source-over';
703
664
  ctx.strokeStyle = ANNOTATION_COLOR;
704
665
  ctx.lineWidth = 3 * state.dpr;
705
666
  ctx.lineCap = 'round';
706
667
  ctx.lineJoin = 'round';
707
668
  ctx.beginPath();
708
- const [first] = stroke.points;
709
- ctx.moveTo(first.x * state.canvas.width, first.y * state.canvas.height);
710
-
711
- for (const point of stroke.points.slice(1)) {
712
- ctx.lineTo(point.x * state.canvas.width, point.y * state.canvas.height);
713
- }
714
-
669
+ ctx.moveTo(from.x * state.dpr, from.y * state.dpr);
670
+ ctx.lineTo(to.x * state.dpr, to.y * state.dpr);
715
671
  ctx.stroke();
716
672
  }
717
673
 
718
- function drawAnnotationErase(
674
+ function drawAnnotationDot(
719
675
  ctx: CanvasRenderingContext2D,
720
- erase: AnnotationErase,
721
676
  state: SlideAnnotationState,
677
+ point: { x: number; y: number },
722
678
  ): void {
723
- if (erase.points.length === 0) {
724
- return;
725
- }
679
+ ctx.globalCompositeOperation = 'source-over';
680
+ ctx.fillStyle = ANNOTATION_COLOR;
681
+ ctx.beginPath();
682
+ ctx.arc(
683
+ point.x * state.dpr,
684
+ point.y * state.dpr,
685
+ 1.5 * state.dpr,
686
+ 0,
687
+ Math.PI * 2,
688
+ );
689
+ ctx.fill();
690
+ }
726
691
 
727
- const radius = erase.radius * state.dpr;
692
+ function drawAnnotationErase(
693
+ ctx: CanvasRenderingContext2D,
694
+ state: SlideAnnotationState,
695
+ point: { x: number; y: number },
696
+ ): void {
697
+ const radius = ANNOTATION_ERASER_RADIUS * state.dpr;
728
698
  ctx.globalCompositeOperation = 'destination-out';
729
- ctx.lineWidth = radius * 2;
730
- ctx.lineCap = 'round';
731
- ctx.lineJoin = 'round';
732
-
733
- const [first] = erase.points;
734
- if (erase.points.length === 1) {
735
- ctx.beginPath();
736
- ctx.arc(
737
- first.x * state.canvas.width,
738
- first.y * state.canvas.height,
739
- radius,
740
- 0,
741
- Math.PI * 2,
742
- );
743
- ctx.fill();
744
- ctx.globalCompositeOperation = 'source-over';
745
- return;
746
- }
747
-
748
699
  ctx.beginPath();
749
- ctx.moveTo(first.x * state.canvas.width, first.y * state.canvas.height);
750
-
751
- for (const point of erase.points.slice(1)) {
752
- ctx.lineTo(point.x * state.canvas.width, point.y * state.canvas.height);
753
- }
754
-
755
- ctx.stroke();
700
+ ctx.arc(point.x * state.dpr, point.y * state.dpr, radius, 0, Math.PI * 2);
701
+ ctx.fill();
756
702
  ctx.globalCompositeOperation = 'source-over';
757
703
  }
758
704
 
759
- function drawCurrentAnnotation(): void {
760
- if (!activeAnnotation) {
761
- return;
762
- }
763
-
764
- const state = getAnnotationState(activeAnnotation.slide);
765
- resizeAnnotationCanvas(activeAnnotation.slide, state);
766
- redrawAnnotationCanvas(state);
767
- }
768
-
769
- function drawCurrentEraser(): void {
770
- if (!activeEraser) {
771
- return;
772
- }
773
-
774
- const state = annotationStates.get(activeEraser.slide);
775
- if (!state) {
776
- return;
777
- }
778
-
779
- resizeAnnotationCanvas(activeEraser.slide, state);
780
- redrawAnnotationCanvas(state);
781
- }
782
-
783
705
  function clearAnnotationCanvases(): void {
784
706
  for (const slide of slides) {
785
- slide.querySelector(ANNOTATION_CANVAS_SELECTOR)?.remove();
707
+ annotationStates.get(slide)?.canvas.remove();
786
708
  annotationStates.delete(slide);
787
709
  }
788
710
  activeAnnotation = null;
789
- activeEraser = null;
711
+ hideEraserPreview();
712
+ }
713
+
714
+ function updateActiveAnnotationCanvas(): void {
715
+ const activeSlide = getActiveSlide();
716
+ for (const slide of slides) {
717
+ const state = annotationStates.get(slide);
718
+ if (state) {
719
+ state.canvas.hidden = slide !== activeSlide;
720
+ }
721
+ }
790
722
  }
791
723
 
792
724
  function handleAnnotationContextMenu(event: MouseEvent): void {
@@ -812,21 +744,23 @@ export default function mountSlides(window: Window): void | (() => void) {
812
744
  }
813
745
 
814
746
  const slide = getActiveSlide();
815
- if (!slide || !eventIsInsideSlide(slide, event)) {
747
+ if (!slide) {
816
748
  activeAnnotation = null;
817
749
  return;
818
750
  }
819
751
 
820
- const point = getSlidePoint(slide, event);
752
+ const point = getViewportPoint(event);
821
753
  if (!point) {
822
754
  return;
823
755
  }
824
756
 
825
757
  const state = getAnnotationState(slide);
826
- const stroke: AnnotationStroke = { type: 'stroke', points: [point] };
827
- state.operations.push(stroke);
828
- activeAnnotation = { slide, stroke };
829
- drawCurrentAnnotation();
758
+ resizeAnnotationCanvas(state);
759
+ const ctx = getCanvasContext(state.canvas);
760
+ if (ctx) {
761
+ drawAnnotationDot(ctx, state, point);
762
+ }
763
+ activeAnnotation = { slide, previousX: point.x, previousY: point.y };
830
764
 
831
765
  const target =
832
766
  event.target instanceof domView.Element ? event.target : slide;
@@ -843,40 +777,51 @@ export default function mountSlides(window: Window): void | (() => void) {
843
777
  return;
844
778
  }
845
779
 
846
- if (!activeAnnotation) {
847
- return;
848
- }
849
-
850
780
  event.preventDefault();
851
781
  event.stopPropagation();
852
782
 
853
- const point = getSlidePoint(activeAnnotation.slide, event);
783
+ const point = getViewportPoint(event);
854
784
  if (!point) {
785
+ activeAnnotation = null;
786
+ return;
787
+ }
788
+
789
+ if (!activeAnnotation) {
790
+ if ((event.buttons & 1) !== 1) {
791
+ return;
792
+ }
793
+ const slide = getActiveSlide();
794
+ if (!slide) {
795
+ return;
796
+ }
797
+ activeAnnotation = { slide, previousX: point.x, previousY: point.y };
855
798
  return;
856
799
  }
857
800
 
858
- activeAnnotation.stroke.points.push(point);
859
- drawCurrentAnnotation();
801
+ const state = getAnnotationState(activeAnnotation.slide);
802
+ resizeAnnotationCanvas(state);
803
+ const ctx = getCanvasContext(state.canvas);
804
+ if (ctx) {
805
+ drawAnnotationStroke(
806
+ ctx,
807
+ state,
808
+ { x: activeAnnotation.previousX, y: activeAnnotation.previousY },
809
+ point,
810
+ );
811
+ }
812
+ activeAnnotation.previousX = point.x;
813
+ activeAnnotation.previousY = point.y;
860
814
  }
861
815
 
862
816
  function handleAnnotationEraseMove(event: PointerEvent): void {
863
817
  const slide = getActiveSlide();
864
- if (!slide || !eventIsInsideSlide(slide, event)) {
865
- activeEraser = null;
866
- hideEraserPreview();
867
- return;
868
- }
869
-
870
- const state = annotationStates.get(slide);
871
- if (!state) {
872
- activeEraser = null;
818
+ if (!slide) {
873
819
  hideEraserPreview();
874
820
  return;
875
821
  }
876
822
 
877
- const point = getSlidePoint(slide, event);
823
+ const point = getViewportPoint(event);
878
824
  if (!point) {
879
- activeEraser = null;
880
825
  hideEraserPreview();
881
826
  return;
882
827
  }
@@ -885,18 +830,12 @@ export default function mountSlides(window: Window): void | (() => void) {
885
830
  event.stopPropagation();
886
831
  showEraserPreview(event);
887
832
 
888
- if (!activeEraser || activeEraser.slide !== slide) {
889
- const erase: AnnotationErase = {
890
- type: 'erase',
891
- points: [],
892
- radius: ANNOTATION_ERASER_RADIUS,
893
- };
894
- state.operations.push(erase);
895
- activeEraser = { slide, erase };
833
+ const state = getAnnotationState(slide);
834
+ resizeAnnotationCanvas(state);
835
+ const ctx = getCanvasContext(state.canvas);
836
+ if (ctx) {
837
+ drawAnnotationErase(ctx, state, point);
896
838
  }
897
-
898
- activeEraser.erase.points.push(point);
899
- drawCurrentEraser();
900
839
  }
901
840
 
902
841
  function handleAnnotationPointerUp(event: PointerEvent): void {
@@ -1011,5 +950,6 @@ export default function mountSlides(window: Window): void | (() => void) {
1011
950
  slidesRoot.removeEventListener('tada:slides-present', handleSlidesPresent);
1012
951
  overlay.remove();
1013
952
  eraserPreview.remove();
953
+ annotationLayer.remove();
1014
954
  };
1015
955
  }
@@ -6,14 +6,24 @@
6
6
  }
7
7
 
8
8
  body.is-presenting {
9
- --slides-annotation-cursor:
9
+ --slides-annotation-cursor-light:
10
10
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(45 12 12)'%3E%3Crect x='10' y='4.8' width='4' height='13.2' rx='2' fill='white' stroke='black' stroke-width='1.5'/%3E%3Cline x1='12' y1='18' x2='12' y2='20.8' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E")
11
11
  6 18,
12
12
  crosshair;
13
- --slides-eraser-cursor:
13
+ --slides-annotation-cursor-dark:
14
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(45 12 12)'%3E%3Crect x='10' y='4.8' width='4' height='13.2' rx='2' fill='black' stroke='white' stroke-width='1.5'/%3E%3Cline x1='12' y1='18' x2='12' y2='20.8' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E")
15
+ 6 18,
16
+ crosshair;
17
+ --slides-annotation-cursor: var(--slides-annotation-cursor-light);
18
+ --slides-eraser-cursor-light:
14
19
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(-45 12 12)'%3E%3Cpath d='M6 10 Q6 8.5 7.5 8.5 H16.5 Q18 8.5 18 10 V14 Q18 15.5 16.5 15.5 H7.5 Q6 15.5 6 14 Z' fill='white' stroke='black' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cline x1='10' y1='8.5' x2='10' y2='15.5' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E")
15
20
  12 12,
16
21
  cell;
22
+ --slides-eraser-cursor-dark:
23
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(-45 12 12)'%3E%3Cpath d='M6 10 Q6 8.5 7.5 8.5 H16.5 Q18 8.5 18 10 V14 Q18 15.5 16.5 15.5 H7.5 Q6 15.5 6 14 Z' fill='black' stroke='white' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cline x1='10' y1='8.5' x2='10' y2='15.5' stroke='white' stroke-width='1.5' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E")
24
+ 12 12,
25
+ cell;
26
+ --slides-eraser-cursor: var(--slides-eraser-cursor-light);
17
27
  --slides-scale: max(
18
28
  100%,
19
29
  min(
@@ -26,6 +36,13 @@ body.is-presenting {
26
36
  overflow: hidden;
27
37
  }
28
38
 
39
+ @media (prefers-color-scheme: dark) {
40
+ body.is-presenting {
41
+ --slides-annotation-cursor: var(--slides-annotation-cursor-dark);
42
+ --slides-eraser-cursor: var(--slides-eraser-cursor-dark);
43
+ }
44
+ }
45
+
29
46
  body.is-presenting.is-presentation-cursor-hidden:not(.is-slides-annotating),
30
47
  body.is-presenting.is-presentation-cursor-hidden:not(.is-slides-annotating) * {
31
48
  cursor: none !important;
@@ -54,6 +71,25 @@ body.is-presenting [data-slides-eraser-preview] {
54
71
  transform: translate(-50%, -50%);
55
72
  }
56
73
 
74
+ body.is-presenting [data-slides-annotation-layer] {
75
+ position: fixed;
76
+ inset: 0;
77
+ z-index: 1001;
78
+ pointer-events: none;
79
+
80
+ > [data-slides-annotations] {
81
+ position: absolute;
82
+ inset: 0;
83
+ display: block;
84
+ width: 100vw;
85
+ height: 100vh;
86
+ }
87
+
88
+ > [data-slides-annotations][hidden] {
89
+ display: none;
90
+ }
91
+ }
92
+
57
93
  body.is-presenting header,
58
94
  body.is-presenting .title-and-info,
59
95
  body.is-presenting nav.toc,
@@ -108,16 +144,6 @@ body.is-presenting .slide {
108
144
  background: var(--bg-color);
109
145
  transform: translateX(-50%);
110
146
 
111
- > [data-slides-annotations] {
112
- position: absolute;
113
- inset: 0;
114
- z-index: 1;
115
- display: block;
116
- width: 100%;
117
- height: 100%;
118
- pointer-events: none;
119
- }
120
-
121
147
  > :first-child {
122
148
  margin-top: 0;
123
149
  }