@abreen/tada 1.15.1 → 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.1",
3
+ "version": "1.15.3",
4
4
  "type": "module",
5
5
  "description": "A static site generator",
6
6
  "license": "MIT",
@@ -115,7 +115,7 @@
115
115
  .js .question-a-body[data-revealed] {
116
116
  cursor: unset;
117
117
 
118
- @include user-select(text);
118
+ @include user-select(unset);
119
119
  }
120
120
 
121
121
  .question-multiple-choice {
@@ -185,7 +185,7 @@
185
185
  .question-multiple-choice-option {
186
186
  cursor: unset;
187
187
 
188
- @include user-select(text);
188
+ @include user-select(unset);
189
189
 
190
190
  &:focus,
191
191
  &:focus-visible {
@@ -9,7 +9,17 @@ interface SlidesPresentDetail {
9
9
  slideIndex?: number;
10
10
  }
11
11
 
12
+ interface SlideAnnotationState {
13
+ canvas: HTMLCanvasElement;
14
+ width: number;
15
+ height: number;
16
+ dpr: number;
17
+ }
18
+
12
19
  const FULLSCREEN_STORAGE_KEY = 'slidesFullscreen';
20
+ const ANNOTATION_COLOR = 'blueviolet';
21
+ const ANNOTATION_ERASER_RADIUS = 18;
22
+ const ANNOTATION_ERASER_DIAMETER = ANNOTATION_ERASER_RADIUS * 2;
13
23
 
14
24
  function isInteractive(
15
25
  view: Window & typeof globalThis,
@@ -111,15 +121,44 @@ export default function mountSlides(window: Window): void | (() => void) {
111
121
  overlay.append(closeButton);
112
122
  document.body.appendChild(overlay);
113
123
 
124
+ const eraserPreview = document.createElement('div');
125
+ eraserPreview.hidden = true;
126
+ eraserPreview.dataset.slidesEraserPreview = '';
127
+ eraserPreview.setAttribute('aria-hidden', 'true');
128
+ eraserPreview.style.setProperty(
129
+ '--slides-eraser-preview-size',
130
+ `${ANNOTATION_ERASER_DIAMETER}px`,
131
+ );
132
+ document.body.appendChild(eraserPreview);
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
+
114
140
  let isPresenting = false;
115
141
  let activeIndex = -1;
116
142
  let presentationMode: PresentationMode | null = null;
117
143
  let isToolbarPinnedVisible = false;
144
+ let isAnnotating = false;
145
+ let isShiftPressed = false;
146
+ let isErasing = false;
147
+ let activeAnnotation: {
148
+ slide: HTMLElement;
149
+ previousX: number;
150
+ previousY: number;
151
+ } | null = null;
118
152
 
119
153
  const toolbarStates = new WeakMap<HTMLElement, TraceToolbarState>();
120
154
  const slideTabIndexes = new WeakMap<HTMLElement, string | null>();
155
+ const annotationStates = new WeakMap<HTMLElement, SlideAnnotationState>();
121
156
 
122
157
  function hidePresentationCursor(): void {
158
+ if (isAnnotating) {
159
+ return;
160
+ }
161
+
123
162
  document.body.classList.add('is-presentation-cursor-hidden');
124
163
  }
125
164
 
@@ -194,8 +233,12 @@ export default function mountSlides(window: Window): void | (() => void) {
194
233
  }
195
234
 
196
235
  function setActiveSlide(index: number): void {
236
+ activeAnnotation = null;
237
+ hideEraserPreview();
197
238
  activeIndex = Math.max(0, Math.min(index, slides.length - 1));
198
239
  updateActiveSlide(slides, activeIndex, isPresenting);
240
+ updateActiveAnnotationCanvas();
241
+ resizeActiveAnnotationCanvas();
199
242
  }
200
243
 
201
244
  function focusSlide(slide: HTMLElement | undefined): void {
@@ -247,6 +290,10 @@ export default function mountSlides(window: Window): void | (() => void) {
247
290
  isPresenting = false;
248
291
  activeIndex = -1;
249
292
  presentationMode = null;
293
+ isShiftPressed = false;
294
+ setAnnotationMode(false);
295
+ clearAnnotationCanvases();
296
+ annotationLayer.hidden = true;
250
297
  hidePresentationControls();
251
298
  showPresentationCursor();
252
299
  document.body.classList.remove('is-presenting');
@@ -289,6 +336,8 @@ export default function mountSlides(window: Window): void | (() => void) {
289
336
  function enterPresentation(mode: PresentationMode, slideIndex = 0): void {
290
337
  isPresenting = true;
291
338
  presentationMode = mode;
339
+ annotationLayer.hidden = false;
340
+ setAnnotationMode(false);
292
341
  hidePresentationControls();
293
342
  showPresentationCursor();
294
343
  document.body.classList.add('is-presenting');
@@ -428,6 +477,12 @@ export default function mountSlides(window: Window): void | (() => void) {
428
477
  return;
429
478
  }
430
479
 
480
+ if (event.key === 'Shift') {
481
+ isShiftPressed = true;
482
+ setEraserMode(isAnnotating);
483
+ return;
484
+ }
485
+
431
486
  if (
432
487
  (event.key === 'ArrowRight' ||
433
488
  event.key === 'ArrowLeft' ||
@@ -465,6 +520,357 @@ export default function mountSlides(window: Window): void | (() => void) {
465
520
  move(1);
466
521
  }
467
522
 
523
+ function getActiveSlide(): HTMLElement | null {
524
+ return activeIndex >= 0 ? (slides[activeIndex] ?? null) : null;
525
+ }
526
+
527
+ function setAnnotationMode(enabled: boolean): void {
528
+ isAnnotating = enabled;
529
+ activeAnnotation = null;
530
+ hideEraserPreview();
531
+ document.body.classList.toggle('is-slides-annotating', enabled);
532
+ setEraserMode(enabled && isShiftPressed);
533
+
534
+ if (enabled) {
535
+ showPresentationCursor();
536
+ }
537
+ }
538
+
539
+ function setEraserMode(enabled: boolean): void {
540
+ isErasing = enabled && isPresenting && isAnnotating;
541
+
542
+ if (isErasing) {
543
+ activeAnnotation = null;
544
+ } else {
545
+ hideEraserPreview();
546
+ }
547
+
548
+ document.body.classList.toggle('is-slides-erasing', isErasing);
549
+ }
550
+
551
+ function hideEraserPreview(): void {
552
+ eraserPreview.hidden = true;
553
+ }
554
+
555
+ function showEraserPreview(event: PointerEvent): void {
556
+ eraserPreview.hidden = false;
557
+ eraserPreview.style.left = `${event.clientX}px`;
558
+ eraserPreview.style.top = `${event.clientY}px`;
559
+ }
560
+
561
+ function toggleAnnotationMode(): void {
562
+ setAnnotationMode(!isAnnotating);
563
+ }
564
+
565
+ function getViewportPoint(
566
+ event: Pick<PointerEvent, 'clientX' | 'clientY'>,
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
+ ) {
579
+ return null;
580
+ }
581
+
582
+ return { x: event.clientX, y: event.clientY };
583
+ }
584
+
585
+ function getCanvasContext(
586
+ canvas: HTMLCanvasElement,
587
+ ): CanvasRenderingContext2D | null {
588
+ try {
589
+ return canvas.getContext('2d');
590
+ } catch {
591
+ return null;
592
+ }
593
+ }
594
+
595
+ function getAnnotationState(slide: HTMLElement): SlideAnnotationState {
596
+ const existing = annotationStates.get(slide);
597
+ if (existing) {
598
+ return existing;
599
+ }
600
+
601
+ const canvas = document.createElement('canvas');
602
+ canvas.dataset.slidesAnnotations = '';
603
+ canvas.dataset.slideIndex = slide.getAttribute('data-slide-index') ?? '';
604
+ canvas.setAttribute('aria-hidden', 'true');
605
+ canvas.hidden = slide !== getActiveSlide();
606
+ annotationLayer.append(canvas);
607
+
608
+ const state: SlideAnnotationState = { canvas, width: 0, height: 0, dpr: 1 };
609
+ annotationStates.set(slide, state);
610
+ resizeAnnotationCanvas(state);
611
+
612
+ return state;
613
+ }
614
+
615
+ function resizeAnnotationCanvas(state: SlideAnnotationState): void {
616
+ const width = Math.max(1, window.innerWidth);
617
+ const height = Math.max(1, window.innerHeight);
618
+ const dpr = Math.max(1, window.devicePixelRatio || 1);
619
+
620
+ if (state.width === width && state.height === height && state.dpr === dpr) {
621
+ return;
622
+ }
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
+
634
+ state.width = width;
635
+ state.height = height;
636
+ state.dpr = dpr;
637
+ state.canvas.width = Math.round(width * dpr);
638
+ state.canvas.height = Math.round(height * dpr);
639
+
640
+ if (priorCanvas) {
641
+ getCanvasContext(state.canvas)?.drawImage(priorCanvas, 0, 0);
642
+ }
643
+ }
644
+
645
+ function resizeActiveAnnotationCanvas(): void {
646
+ const slide = getActiveSlide();
647
+ if (!slide) {
648
+ return;
649
+ }
650
+
651
+ const state = annotationStates.get(slide);
652
+ if (state) {
653
+ resizeAnnotationCanvas(state);
654
+ }
655
+ }
656
+
657
+ function drawAnnotationStroke(
658
+ ctx: CanvasRenderingContext2D,
659
+ state: SlideAnnotationState,
660
+ from: { x: number; y: number },
661
+ to: { x: number; y: number },
662
+ ): void {
663
+ ctx.globalCompositeOperation = 'source-over';
664
+ ctx.strokeStyle = ANNOTATION_COLOR;
665
+ ctx.lineWidth = 3 * state.dpr;
666
+ ctx.lineCap = 'round';
667
+ ctx.lineJoin = 'round';
668
+ ctx.beginPath();
669
+ ctx.moveTo(from.x * state.dpr, from.y * state.dpr);
670
+ ctx.lineTo(to.x * state.dpr, to.y * state.dpr);
671
+ ctx.stroke();
672
+ }
673
+
674
+ function drawAnnotationDot(
675
+ ctx: CanvasRenderingContext2D,
676
+ state: SlideAnnotationState,
677
+ point: { x: number; y: number },
678
+ ): void {
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
+ }
691
+
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;
698
+ ctx.globalCompositeOperation = 'destination-out';
699
+ ctx.beginPath();
700
+ ctx.arc(point.x * state.dpr, point.y * state.dpr, radius, 0, Math.PI * 2);
701
+ ctx.fill();
702
+ ctx.globalCompositeOperation = 'source-over';
703
+ }
704
+
705
+ function clearAnnotationCanvases(): void {
706
+ for (const slide of slides) {
707
+ annotationStates.get(slide)?.canvas.remove();
708
+ annotationStates.delete(slide);
709
+ }
710
+ activeAnnotation = 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
+ }
722
+ }
723
+
724
+ function handleAnnotationContextMenu(event: MouseEvent): void {
725
+ if (!isPresenting) {
726
+ return;
727
+ }
728
+
729
+ event.preventDefault();
730
+ event.stopPropagation();
731
+ toggleAnnotationMode();
732
+ }
733
+
734
+ function handleAnnotationPointerDown(event: PointerEvent): void {
735
+ if (!isPresenting || !isAnnotating || event.button !== 0) {
736
+ return;
737
+ }
738
+
739
+ event.preventDefault();
740
+ event.stopPropagation();
741
+
742
+ if (isErasing) {
743
+ return;
744
+ }
745
+
746
+ const slide = getActiveSlide();
747
+ if (!slide) {
748
+ activeAnnotation = null;
749
+ return;
750
+ }
751
+
752
+ const point = getViewportPoint(event);
753
+ if (!point) {
754
+ return;
755
+ }
756
+
757
+ const state = getAnnotationState(slide);
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 };
764
+
765
+ const target =
766
+ event.target instanceof domView.Element ? event.target : slide;
767
+ target.setPointerCapture?.(event.pointerId);
768
+ }
769
+
770
+ function handleAnnotationPointerMove(event: PointerEvent): void {
771
+ if (!isPresenting || !isAnnotating) {
772
+ return;
773
+ }
774
+
775
+ if (isErasing) {
776
+ handleAnnotationEraseMove(event);
777
+ return;
778
+ }
779
+
780
+ event.preventDefault();
781
+ event.stopPropagation();
782
+
783
+ const point = getViewportPoint(event);
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 };
798
+ return;
799
+ }
800
+
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;
814
+ }
815
+
816
+ function handleAnnotationEraseMove(event: PointerEvent): void {
817
+ const slide = getActiveSlide();
818
+ if (!slide) {
819
+ hideEraserPreview();
820
+ return;
821
+ }
822
+
823
+ const point = getViewportPoint(event);
824
+ if (!point) {
825
+ hideEraserPreview();
826
+ return;
827
+ }
828
+
829
+ event.preventDefault();
830
+ event.stopPropagation();
831
+ showEraserPreview(event);
832
+
833
+ const state = getAnnotationState(slide);
834
+ resizeAnnotationCanvas(state);
835
+ const ctx = getCanvasContext(state.canvas);
836
+ if (ctx) {
837
+ drawAnnotationErase(ctx, state, point);
838
+ }
839
+ }
840
+
841
+ function handleAnnotationPointerUp(event: PointerEvent): void {
842
+ if (!isPresenting || !isAnnotating || !activeAnnotation) {
843
+ return;
844
+ }
845
+
846
+ event.preventDefault();
847
+ event.stopPropagation();
848
+ activeAnnotation = null;
849
+ }
850
+
851
+ function handleKeyup(event: KeyboardEvent): void {
852
+ if (event.key !== 'Shift') {
853
+ return;
854
+ }
855
+
856
+ isShiftPressed = false;
857
+ setEraserMode(false);
858
+ }
859
+
860
+ function handleWindowBlur(): void {
861
+ isShiftPressed = false;
862
+ setEraserMode(false);
863
+ }
864
+
865
+ function handleAnnotationClick(event: MouseEvent): void {
866
+ if (!isPresenting || !isAnnotating) {
867
+ return;
868
+ }
869
+
870
+ event.preventDefault();
871
+ event.stopPropagation();
872
+ }
873
+
468
874
  function handleMouseMove(event: MouseEvent): void {
469
875
  if (!isPresenting) {
470
876
  return;
@@ -507,8 +913,17 @@ export default function mountSlides(window: Window): void | (() => void) {
507
913
  closeButton.addEventListener('click', handleCloseClick);
508
914
  document.addEventListener('fullscreenchange', handleFullscreenChange);
509
915
  window.addEventListener('keydown', handleKeydown);
916
+ window.addEventListener('keyup', handleKeyup);
917
+ window.addEventListener('blur', handleWindowBlur);
510
918
  window.addEventListener('mousemove', handleMouseMove);
919
+ window.addEventListener('resize', resizeActiveAnnotationCanvas);
920
+ window.addEventListener('pointermove', handleAnnotationPointerMove);
921
+ window.addEventListener('pointerup', handleAnnotationPointerUp);
922
+ window.addEventListener('pointercancel', handleAnnotationPointerUp);
923
+ slidesRoot.addEventListener('click', handleAnnotationClick, true);
511
924
  slidesRoot.addEventListener('click', handleSlideClick);
925
+ slidesRoot.addEventListener('contextmenu', handleAnnotationContextMenu);
926
+ slidesRoot.addEventListener('pointerdown', handleAnnotationPointerDown);
512
927
  slidesRoot.addEventListener('tada:slides-present', handleSlidesPresent);
513
928
 
514
929
  return () => {
@@ -521,9 +936,20 @@ export default function mountSlides(window: Window): void | (() => void) {
521
936
  closeButton.removeEventListener('click', handleCloseClick);
522
937
  document.removeEventListener('fullscreenchange', handleFullscreenChange);
523
938
  window.removeEventListener('keydown', handleKeydown);
939
+ window.removeEventListener('keyup', handleKeyup);
940
+ window.removeEventListener('blur', handleWindowBlur);
524
941
  window.removeEventListener('mousemove', handleMouseMove);
942
+ window.removeEventListener('resize', resizeActiveAnnotationCanvas);
943
+ window.removeEventListener('pointermove', handleAnnotationPointerMove);
944
+ window.removeEventListener('pointerup', handleAnnotationPointerUp);
945
+ window.removeEventListener('pointercancel', handleAnnotationPointerUp);
946
+ slidesRoot.removeEventListener('click', handleAnnotationClick, true);
525
947
  slidesRoot.removeEventListener('click', handleSlideClick);
948
+ slidesRoot.removeEventListener('contextmenu', handleAnnotationContextMenu);
949
+ slidesRoot.removeEventListener('pointerdown', handleAnnotationPointerDown);
526
950
  slidesRoot.removeEventListener('tada:slides-present', handleSlidesPresent);
527
951
  overlay.remove();
952
+ eraserPreview.remove();
953
+ annotationLayer.remove();
528
954
  };
529
955
  }
@@ -6,16 +6,90 @@
6
6
  }
7
7
 
8
8
  body.is-presenting {
9
- --slides-scale: clamp(150%, 2.6vmin, 200%);
9
+ --slides-annotation-cursor-light:
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
+ 6 18,
12
+ crosshair;
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:
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")
20
+ 12 12,
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);
27
+ --slides-scale: max(
28
+ 100%,
29
+ min(
30
+ clamp(150%, 2.8vmin, 200%),
31
+ calc((100vw - var(--gap) - var(--gap)) / 35),
32
+ calc(100vh / 26.25)
33
+ )
34
+ );
10
35
 
11
36
  overflow: hidden;
12
37
  }
13
38
 
14
- body.is-presenting.is-presentation-cursor-hidden,
15
- body.is-presenting.is-presentation-cursor-hidden * {
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
+
46
+ body.is-presenting.is-presentation-cursor-hidden:not(.is-slides-annotating),
47
+ body.is-presenting.is-presentation-cursor-hidden:not(.is-slides-annotating) * {
16
48
  cursor: none !important;
17
49
  }
18
50
 
51
+ body.is-presenting.is-slides-annotating .slide-deck,
52
+ body.is-presenting.is-slides-annotating .slide-deck * {
53
+ cursor: var(--slides-annotation-cursor) !important;
54
+ }
55
+
56
+ body.is-presenting.is-slides-annotating.is-slides-erasing .slide-deck,
57
+ body.is-presenting.is-slides-annotating.is-slides-erasing .slide-deck * {
58
+ cursor: var(--slides-eraser-cursor) !important;
59
+ }
60
+
61
+ body.is-presenting [data-slides-eraser-preview] {
62
+ position: fixed;
63
+ z-index: 1002;
64
+ box-sizing: border-box;
65
+ width: var(--slides-eraser-preview-size, 36px);
66
+ height: var(--slides-eraser-preview-size, 36px);
67
+ pointer-events: none;
68
+ background: transparent;
69
+ border: 2px solid var(--bg2-color);
70
+ border-radius: 50%;
71
+ transform: translate(-50%, -50%);
72
+ }
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
+
19
93
  body.is-presenting header,
20
94
  body.is-presenting .title-and-info,
21
95
  body.is-presenting nav.toc,