@abreen/tada 1.15.2 → 1.15.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.
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.4",
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;
@@ -158,12 +144,12 @@ export default function mountSlides(window: Window): void | (() => void) {
158
144
  let isAnnotating = false;
159
145
  let isShiftPressed = false;
160
146
  let isErasing = false;
147
+ let isDrivingTrace = false;
161
148
  let activeAnnotation: {
162
149
  slide: HTMLElement;
163
- stroke: AnnotationStroke;
150
+ previousX: number;
151
+ previousY: number;
164
152
  } | null = null;
165
- let activeEraser: { slide: HTMLElement; erase: AnnotationErase } | null =
166
- null;
167
153
 
168
154
  const toolbarStates = new WeakMap<HTMLElement, TraceToolbarState>();
169
155
  const slideTabIndexes = new WeakMap<HTMLElement, string | null>();
@@ -249,10 +235,10 @@ export default function mountSlides(window: Window): void | (() => void) {
249
235
 
250
236
  function setActiveSlide(index: number): void {
251
237
  activeAnnotation = null;
252
- activeEraser = null;
253
238
  hideEraserPreview();
254
239
  activeIndex = Math.max(0, Math.min(index, slides.length - 1));
255
240
  updateActiveSlide(slides, activeIndex, isPresenting);
241
+ updateActiveAnnotationCanvas();
256
242
  resizeActiveAnnotationCanvas();
257
243
  }
258
244
 
@@ -308,6 +294,7 @@ export default function mountSlides(window: Window): void | (() => void) {
308
294
  isShiftPressed = false;
309
295
  setAnnotationMode(false);
310
296
  clearAnnotationCanvases();
297
+ annotationLayer.hidden = true;
311
298
  hidePresentationControls();
312
299
  showPresentationCursor();
313
300
  document.body.classList.remove('is-presenting');
@@ -350,6 +337,7 @@ export default function mountSlides(window: Window): void | (() => void) {
350
337
  function enterPresentation(mode: PresentationMode, slideIndex = 0): void {
351
338
  isPresenting = true;
352
339
  presentationMode = mode;
340
+ annotationLayer.hidden = false;
353
341
  setAnnotationMode(false);
354
342
  hidePresentationControls();
355
343
  showPresentationCursor();
@@ -387,7 +375,12 @@ export default function mountSlides(window: Window): void | (() => void) {
387
375
  continue;
388
376
  }
389
377
 
390
- button.click();
378
+ isDrivingTrace = true;
379
+ try {
380
+ button.click();
381
+ } finally {
382
+ isDrivingTrace = false;
383
+ }
391
384
  return true;
392
385
  }
393
386
 
@@ -540,7 +533,6 @@ export default function mountSlides(window: Window): void | (() => void) {
540
533
  function setAnnotationMode(enabled: boolean): void {
541
534
  isAnnotating = enabled;
542
535
  activeAnnotation = null;
543
- activeEraser = null;
544
536
  hideEraserPreview();
545
537
  document.body.classList.toggle('is-slides-annotating', enabled);
546
538
  setEraserMode(enabled && isShiftPressed);
@@ -552,7 +544,6 @@ export default function mountSlides(window: Window): void | (() => void) {
552
544
 
553
545
  function setEraserMode(enabled: boolean): void {
554
546
  isErasing = enabled && isPresenting && isAnnotating;
555
- activeEraser = null;
556
547
 
557
548
  if (isErasing) {
558
549
  activeAnnotation = null;
@@ -577,33 +568,24 @@ export default function mountSlides(window: Window): void | (() => void) {
577
568
  setAnnotationMode(!isAnnotating);
578
569
  }
579
570
 
580
- function getSlidePoint(
581
- slide: HTMLElement,
571
+ function getViewportPoint(
582
572
  event: Pick<PointerEvent, 'clientX' | 'clientY'>,
583
- ): AnnotationPoint | null {
584
- const rect = slide.getBoundingClientRect();
585
- if (rect.width <= 0 || rect.height <= 0) {
573
+ ): { x: number; y: number } | null {
574
+ const width = window.innerWidth;
575
+ const height = window.innerHeight;
576
+
577
+ if (
578
+ width <= 0 ||
579
+ height <= 0 ||
580
+ event.clientX < 0 ||
581
+ event.clientX > width ||
582
+ event.clientY < 0 ||
583
+ event.clientY > height
584
+ ) {
586
585
  return null;
587
586
  }
588
587
 
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
- );
588
+ return { x: event.clientX, y: event.clientY };
607
589
  }
608
590
 
609
591
  function getCanvasContext(
@@ -624,41 +606,46 @@ export default function mountSlides(window: Window): void | (() => void) {
624
606
 
625
607
  const canvas = document.createElement('canvas');
626
608
  canvas.dataset.slidesAnnotations = '';
609
+ canvas.dataset.slideIndex = slide.getAttribute('data-slide-index') ?? '';
627
610
  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
- };
611
+ canvas.hidden = slide !== getActiveSlide();
612
+ annotationLayer.append(canvas);
613
+
614
+ const state: SlideAnnotationState = { canvas, width: 0, height: 0, dpr: 1 };
637
615
  annotationStates.set(slide, state);
638
- resizeAnnotationCanvas(slide, state);
616
+ resizeAnnotationCanvas(state);
639
617
 
640
618
  return state;
641
619
  }
642
620
 
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);
621
+ function resizeAnnotationCanvas(state: SlideAnnotationState): void {
622
+ const width = Math.max(1, window.innerWidth);
623
+ const height = Math.max(1, window.innerHeight);
650
624
  const dpr = Math.max(1, window.devicePixelRatio || 1);
651
625
 
652
626
  if (state.width === width && state.height === height && state.dpr === dpr) {
653
627
  return;
654
628
  }
655
629
 
630
+ const priorCanvas =
631
+ state.canvas.width > 0 && state.canvas.height > 0
632
+ ? document.createElement('canvas')
633
+ : null;
634
+ if (priorCanvas) {
635
+ priorCanvas.width = state.canvas.width;
636
+ priorCanvas.height = state.canvas.height;
637
+ priorCanvas.getContext('2d')?.drawImage(state.canvas, 0, 0);
638
+ }
639
+
656
640
  state.width = width;
657
641
  state.height = height;
658
642
  state.dpr = dpr;
659
643
  state.canvas.width = Math.round(width * dpr);
660
644
  state.canvas.height = Math.round(height * dpr);
661
- redrawAnnotationCanvas(state);
645
+
646
+ if (priorCanvas) {
647
+ getCanvasContext(state.canvas)?.drawImage(priorCanvas, 0, 0);
648
+ }
662
649
  }
663
650
 
664
651
  function resizeActiveAnnotationCanvas(): void {
@@ -669,124 +656,75 @@ export default function mountSlides(window: Window): void | (() => void) {
669
656
 
670
657
  const state = annotationStates.get(slide);
671
658
  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
- }
659
+ resizeAnnotationCanvas(state);
690
660
  }
691
661
  }
692
662
 
693
663
  function drawAnnotationStroke(
694
664
  ctx: CanvasRenderingContext2D,
695
- stroke: AnnotationStroke,
696
665
  state: SlideAnnotationState,
666
+ from: { x: number; y: number },
667
+ to: { x: number; y: number },
697
668
  ): void {
698
- if (stroke.points.length === 0) {
699
- return;
700
- }
701
-
702
669
  ctx.globalCompositeOperation = 'source-over';
703
670
  ctx.strokeStyle = ANNOTATION_COLOR;
704
671
  ctx.lineWidth = 3 * state.dpr;
705
672
  ctx.lineCap = 'round';
706
673
  ctx.lineJoin = 'round';
707
674
  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
-
675
+ ctx.moveTo(from.x * state.dpr, from.y * state.dpr);
676
+ ctx.lineTo(to.x * state.dpr, to.y * state.dpr);
715
677
  ctx.stroke();
716
678
  }
717
679
 
718
- function drawAnnotationErase(
680
+ function drawAnnotationDot(
719
681
  ctx: CanvasRenderingContext2D,
720
- erase: AnnotationErase,
721
682
  state: SlideAnnotationState,
683
+ point: { x: number; y: number },
722
684
  ): void {
723
- if (erase.points.length === 0) {
724
- return;
725
- }
685
+ ctx.globalCompositeOperation = 'source-over';
686
+ ctx.fillStyle = ANNOTATION_COLOR;
687
+ ctx.beginPath();
688
+ ctx.arc(
689
+ point.x * state.dpr,
690
+ point.y * state.dpr,
691
+ 1.5 * state.dpr,
692
+ 0,
693
+ Math.PI * 2,
694
+ );
695
+ ctx.fill();
696
+ }
726
697
 
727
- const radius = erase.radius * state.dpr;
698
+ function drawAnnotationErase(
699
+ ctx: CanvasRenderingContext2D,
700
+ state: SlideAnnotationState,
701
+ point: { x: number; y: number },
702
+ ): void {
703
+ const radius = ANNOTATION_ERASER_RADIUS * state.dpr;
728
704
  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
705
  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();
706
+ ctx.arc(point.x * state.dpr, point.y * state.dpr, radius, 0, Math.PI * 2);
707
+ ctx.fill();
756
708
  ctx.globalCompositeOperation = 'source-over';
757
709
  }
758
710
 
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
711
  function clearAnnotationCanvases(): void {
784
712
  for (const slide of slides) {
785
- slide.querySelector(ANNOTATION_CANVAS_SELECTOR)?.remove();
713
+ annotationStates.get(slide)?.canvas.remove();
786
714
  annotationStates.delete(slide);
787
715
  }
788
716
  activeAnnotation = null;
789
- activeEraser = null;
717
+ hideEraserPreview();
718
+ }
719
+
720
+ function updateActiveAnnotationCanvas(): void {
721
+ const activeSlide = getActiveSlide();
722
+ for (const slide of slides) {
723
+ const state = annotationStates.get(slide);
724
+ if (state) {
725
+ state.canvas.hidden = slide !== activeSlide;
726
+ }
727
+ }
790
728
  }
791
729
 
792
730
  function handleAnnotationContextMenu(event: MouseEvent): void {
@@ -812,21 +750,23 @@ export default function mountSlides(window: Window): void | (() => void) {
812
750
  }
813
751
 
814
752
  const slide = getActiveSlide();
815
- if (!slide || !eventIsInsideSlide(slide, event)) {
753
+ if (!slide) {
816
754
  activeAnnotation = null;
817
755
  return;
818
756
  }
819
757
 
820
- const point = getSlidePoint(slide, event);
758
+ const point = getViewportPoint(event);
821
759
  if (!point) {
822
760
  return;
823
761
  }
824
762
 
825
763
  const state = getAnnotationState(slide);
826
- const stroke: AnnotationStroke = { type: 'stroke', points: [point] };
827
- state.operations.push(stroke);
828
- activeAnnotation = { slide, stroke };
829
- drawCurrentAnnotation();
764
+ resizeAnnotationCanvas(state);
765
+ const ctx = getCanvasContext(state.canvas);
766
+ if (ctx) {
767
+ drawAnnotationDot(ctx, state, point);
768
+ }
769
+ activeAnnotation = { slide, previousX: point.x, previousY: point.y };
830
770
 
831
771
  const target =
832
772
  event.target instanceof domView.Element ? event.target : slide;
@@ -843,40 +783,51 @@ export default function mountSlides(window: Window): void | (() => void) {
843
783
  return;
844
784
  }
845
785
 
846
- if (!activeAnnotation) {
847
- return;
848
- }
849
-
850
786
  event.preventDefault();
851
787
  event.stopPropagation();
852
788
 
853
- const point = getSlidePoint(activeAnnotation.slide, event);
789
+ const point = getViewportPoint(event);
854
790
  if (!point) {
791
+ activeAnnotation = null;
855
792
  return;
856
793
  }
857
794
 
858
- activeAnnotation.stroke.points.push(point);
859
- drawCurrentAnnotation();
795
+ if (!activeAnnotation) {
796
+ if ((event.buttons & 1) !== 1) {
797
+ return;
798
+ }
799
+ const slide = getActiveSlide();
800
+ if (!slide) {
801
+ return;
802
+ }
803
+ activeAnnotation = { slide, previousX: point.x, previousY: point.y };
804
+ return;
805
+ }
806
+
807
+ const state = getAnnotationState(activeAnnotation.slide);
808
+ resizeAnnotationCanvas(state);
809
+ const ctx = getCanvasContext(state.canvas);
810
+ if (ctx) {
811
+ drawAnnotationStroke(
812
+ ctx,
813
+ state,
814
+ { x: activeAnnotation.previousX, y: activeAnnotation.previousY },
815
+ point,
816
+ );
817
+ }
818
+ activeAnnotation.previousX = point.x;
819
+ activeAnnotation.previousY = point.y;
860
820
  }
861
821
 
862
822
  function handleAnnotationEraseMove(event: PointerEvent): void {
863
823
  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;
824
+ if (!slide) {
873
825
  hideEraserPreview();
874
826
  return;
875
827
  }
876
828
 
877
- const point = getSlidePoint(slide, event);
829
+ const point = getViewportPoint(event);
878
830
  if (!point) {
879
- activeEraser = null;
880
831
  hideEraserPreview();
881
832
  return;
882
833
  }
@@ -885,18 +836,12 @@ export default function mountSlides(window: Window): void | (() => void) {
885
836
  event.stopPropagation();
886
837
  showEraserPreview(event);
887
838
 
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 };
839
+ const state = getAnnotationState(slide);
840
+ resizeAnnotationCanvas(state);
841
+ const ctx = getCanvasContext(state.canvas);
842
+ if (ctx) {
843
+ drawAnnotationErase(ctx, state, point);
896
844
  }
897
-
898
- activeEraser.erase.points.push(point);
899
- drawCurrentEraser();
900
845
  }
901
846
 
902
847
  function handleAnnotationPointerUp(event: PointerEvent): void {
@@ -924,7 +869,7 @@ export default function mountSlides(window: Window): void | (() => void) {
924
869
  }
925
870
 
926
871
  function handleAnnotationClick(event: MouseEvent): void {
927
- if (!isPresenting || !isAnnotating) {
872
+ if (!isPresenting || !isAnnotating || isDrivingTrace) {
928
873
  return;
929
874
  }
930
875
 
@@ -1011,5 +956,6 @@ export default function mountSlides(window: Window): void | (() => void) {
1011
956
  slidesRoot.removeEventListener('tada:slides-present', handleSlidesPresent);
1012
957
  overlay.remove();
1013
958
  eraserPreview.remove();
959
+ annotationLayer.remove();
1014
960
  };
1015
961
  }
@@ -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
  }