@reslide-dev/core 0.2.1 → 0.3.0
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/LICENSE +21 -0
- package/dist/{Mermaid-BtXG2Ea2.mjs → Mermaid-BfetSkya.mjs} +485 -82
- package/dist/{Mermaid-CUQUPHdK.d.mts → Mermaid-DWHv8vPf.d.mts} +18 -4
- package/dist/index.d.mts +31 -15
- package/dist/index.mjs +6 -3
- package/dist/mdx-components.d.mts +2 -2
- package/dist/mdx-components.mjs +2 -2
- package/package.json +1 -1
- package/src/themes/default.css +1 -1
- package/src/themes/print.css +71 -42
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yoshikatsu Nishida
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -5,7 +5,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
* Invisible click zones on the left/right edges of the slide.
|
|
6
6
|
* Clicking the left ~15% goes to the previous slide,
|
|
7
7
|
* clicking the right ~15% goes to the next slide.
|
|
8
|
-
* Shows a
|
|
8
|
+
* Shows a gradient overlay and arrow indicator on hover (docswell style).
|
|
9
9
|
*/
|
|
10
10
|
function ClickNavigation({ onPrev, onNext, disabled }) {
|
|
11
11
|
if (disabled) return null;
|
|
@@ -20,12 +20,15 @@ function ClickNavigation({ onPrev, onNext, disabled }) {
|
|
|
20
20
|
function NavZone({ direction, onClick }) {
|
|
21
21
|
const [hovered, setHovered] = useState(false);
|
|
22
22
|
const isPrev = direction === "prev";
|
|
23
|
+
const handleClick = useCallback((e) => {
|
|
24
|
+
e.stopPropagation();
|
|
25
|
+
e.currentTarget.blur();
|
|
26
|
+
onClick();
|
|
27
|
+
}, [onClick]);
|
|
28
|
+
const gradient = isPrev ? "linear-gradient(to right, rgba(0,0,0,0.08), transparent)" : "linear-gradient(to left, rgba(0,0,0,0.08), transparent)";
|
|
23
29
|
return /* @__PURE__ */ jsx("button", {
|
|
24
30
|
type: "button",
|
|
25
|
-
onClick:
|
|
26
|
-
e.stopPropagation();
|
|
27
|
-
onClick();
|
|
28
|
-
}, [onClick]),
|
|
31
|
+
onClick: handleClick,
|
|
29
32
|
onMouseEnter: () => setHovered(true),
|
|
30
33
|
onMouseLeave: () => setHovered(false),
|
|
31
34
|
"aria-label": isPrev ? "Previous slide" : "Next slide",
|
|
@@ -35,16 +38,16 @@ function NavZone({ direction, onClick }) {
|
|
|
35
38
|
bottom: 0,
|
|
36
39
|
[isPrev ? "left" : "right"]: 0,
|
|
37
40
|
width: "15%",
|
|
38
|
-
background: "none",
|
|
41
|
+
background: hovered ? gradient : "none",
|
|
39
42
|
border: "none",
|
|
43
|
+
outline: "none",
|
|
40
44
|
cursor: "pointer",
|
|
41
45
|
zIndex: 20,
|
|
42
46
|
display: "flex",
|
|
43
47
|
alignItems: "center",
|
|
44
48
|
justifyContent: isPrev ? "flex-start" : "flex-end",
|
|
45
49
|
padding: "0 1.5rem",
|
|
46
|
-
|
|
47
|
-
transition: "opacity 0.2s ease"
|
|
50
|
+
transition: "background 0.25s ease"
|
|
48
51
|
},
|
|
49
52
|
children: /* @__PURE__ */ jsx("svg", {
|
|
50
53
|
width: "32",
|
|
@@ -57,9 +60,10 @@ function NavZone({ direction, onClick }) {
|
|
|
57
60
|
strokeLinejoin: "round",
|
|
58
61
|
style: {
|
|
59
62
|
color: "var(--slide-text, #1a1a1a)",
|
|
60
|
-
opacity: .
|
|
61
|
-
filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.
|
|
62
|
-
transform: isPrev ? "none" : "rotate(180deg)"
|
|
63
|
+
opacity: hovered ? .6 : 0,
|
|
64
|
+
filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.15))",
|
|
65
|
+
transform: isPrev ? "none" : "rotate(180deg)",
|
|
66
|
+
transition: "opacity 0.25s ease"
|
|
63
67
|
},
|
|
64
68
|
children: /* @__PURE__ */ jsx("polyline", { points: "15 18 9 12 15 6" })
|
|
65
69
|
})
|
|
@@ -321,6 +325,11 @@ function isPresenterView() {
|
|
|
321
325
|
*/
|
|
322
326
|
function NavigationBar({ isDrawing, onToggleDrawing }) {
|
|
323
327
|
const { currentSlide, totalSlides, clickStep, totalClickSteps, isOverview, isFullscreen, next, prev, toggleOverview, toggleFullscreen } = useDeck();
|
|
328
|
+
const handlePrint = useCallback(() => {
|
|
329
|
+
const url = new URL(window.location.href);
|
|
330
|
+
url.searchParams.set("mode", "print");
|
|
331
|
+
window.open(url.toString(), "_blank");
|
|
332
|
+
}, []);
|
|
324
333
|
const [visible, setVisible] = useState(false);
|
|
325
334
|
const timerRef = useRef(null);
|
|
326
335
|
const barRef = useRef(null);
|
|
@@ -431,6 +440,12 @@ function NavigationBar({ isDrawing, onToggleDrawing }) {
|
|
|
431
440
|
title: "Drawing (d)",
|
|
432
441
|
active: isDrawing,
|
|
433
442
|
children: /* @__PURE__ */ jsx(PenIcon, {})
|
|
443
|
+
}),
|
|
444
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
445
|
+
/* @__PURE__ */ jsx(NavButton, {
|
|
446
|
+
onClick: handlePrint,
|
|
447
|
+
title: "Print / PDF (Cmd+P)",
|
|
448
|
+
children: /* @__PURE__ */ jsx(PrintIcon, {})
|
|
434
449
|
})
|
|
435
450
|
]
|
|
436
451
|
});
|
|
@@ -622,6 +637,124 @@ function PenIcon() {
|
|
|
622
637
|
]
|
|
623
638
|
});
|
|
624
639
|
}
|
|
640
|
+
function PrintIcon() {
|
|
641
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
642
|
+
width: "16",
|
|
643
|
+
height: "16",
|
|
644
|
+
viewBox: "0 0 24 24",
|
|
645
|
+
fill: "none",
|
|
646
|
+
stroke: "currentColor",
|
|
647
|
+
strokeWidth: "2",
|
|
648
|
+
strokeLinecap: "round",
|
|
649
|
+
strokeLinejoin: "round",
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsx("polyline", { points: "6 9 6 2 18 2 18 9" }),
|
|
652
|
+
/* @__PURE__ */ jsx("path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }),
|
|
653
|
+
/* @__PURE__ */ jsx("rect", {
|
|
654
|
+
x: "6",
|
|
655
|
+
y: "14",
|
|
656
|
+
width: "12",
|
|
657
|
+
height: "8"
|
|
658
|
+
})
|
|
659
|
+
]
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/Pointer.tsx
|
|
664
|
+
let rippleId = 0;
|
|
665
|
+
/**
|
|
666
|
+
* Presentation pointer — a large colored circle that follows the cursor.
|
|
667
|
+
* Clicking creates an expanding ripple effect to highlight a point.
|
|
668
|
+
* Toggle with `q` key (handled in Deck).
|
|
669
|
+
*/
|
|
670
|
+
function Pointer({ active, color, size = 24 }) {
|
|
671
|
+
const [pos, setPos] = useState(null);
|
|
672
|
+
const [ripples, setRipples] = useState([]);
|
|
673
|
+
const containerRef = useRef(null);
|
|
674
|
+
const getPos = useCallback((e) => {
|
|
675
|
+
const container = containerRef.current?.parentElement;
|
|
676
|
+
if (!container) return null;
|
|
677
|
+
const rect = container.getBoundingClientRect();
|
|
678
|
+
return {
|
|
679
|
+
x: e.clientX - rect.left,
|
|
680
|
+
y: e.clientY - rect.top
|
|
681
|
+
};
|
|
682
|
+
}, []);
|
|
683
|
+
const handleMouseMove = useCallback((e) => {
|
|
684
|
+
if (!active) return;
|
|
685
|
+
setPos(getPos(e));
|
|
686
|
+
}, [active, getPos]);
|
|
687
|
+
const handleClick = useCallback((e) => {
|
|
688
|
+
if (!active) return;
|
|
689
|
+
const p = getPos(e);
|
|
690
|
+
if (!p) return;
|
|
691
|
+
const id = ++rippleId;
|
|
692
|
+
setRipples((prev) => [...prev, {
|
|
693
|
+
id,
|
|
694
|
+
x: p.x,
|
|
695
|
+
y: p.y
|
|
696
|
+
}]);
|
|
697
|
+
setTimeout(() => setRipples((prev) => prev.filter((r) => r.id !== id)), 500);
|
|
698
|
+
}, [active, getPos]);
|
|
699
|
+
const handleMouseLeave = useCallback(() => setPos(null), []);
|
|
700
|
+
useEffect(() => {
|
|
701
|
+
if (!active) {
|
|
702
|
+
setPos(null);
|
|
703
|
+
setRipples([]);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
707
|
+
window.addEventListener("click", handleClick, true);
|
|
708
|
+
window.addEventListener("mouseleave", handleMouseLeave);
|
|
709
|
+
return () => {
|
|
710
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
711
|
+
window.removeEventListener("click", handleClick, true);
|
|
712
|
+
window.removeEventListener("mouseleave", handleMouseLeave);
|
|
713
|
+
};
|
|
714
|
+
}, [
|
|
715
|
+
active,
|
|
716
|
+
handleMouseMove,
|
|
717
|
+
handleClick,
|
|
718
|
+
handleMouseLeave
|
|
719
|
+
]);
|
|
720
|
+
const resolvedColor = color ?? "var(--slide-accent, #16a34a)";
|
|
721
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("style", { children: `
|
|
722
|
+
@keyframes reslide-pointer-ripple {
|
|
723
|
+
0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0.5; }
|
|
724
|
+
100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }
|
|
725
|
+
}
|
|
726
|
+
` }), /* @__PURE__ */ jsxs("div", {
|
|
727
|
+
ref: containerRef,
|
|
728
|
+
style: {
|
|
729
|
+
position: "absolute",
|
|
730
|
+
inset: 0,
|
|
731
|
+
pointerEvents: "none",
|
|
732
|
+
zIndex: 45,
|
|
733
|
+
overflow: "hidden"
|
|
734
|
+
},
|
|
735
|
+
children: [active && pos && /* @__PURE__ */ jsx("div", { style: {
|
|
736
|
+
position: "absolute",
|
|
737
|
+
left: pos.x,
|
|
738
|
+
top: pos.y,
|
|
739
|
+
width: size,
|
|
740
|
+
height: size,
|
|
741
|
+
borderRadius: "50%",
|
|
742
|
+
backgroundColor: resolvedColor,
|
|
743
|
+
opacity: .7,
|
|
744
|
+
transform: "translate(-50%, -50%)",
|
|
745
|
+
willChange: "left, top"
|
|
746
|
+
} }), ripples.map((r) => /* @__PURE__ */ jsx("div", { style: {
|
|
747
|
+
position: "absolute",
|
|
748
|
+
left: r.x,
|
|
749
|
+
top: r.y,
|
|
750
|
+
width: size * 3,
|
|
751
|
+
height: size * 3,
|
|
752
|
+
borderRadius: "50%",
|
|
753
|
+
border: `2px solid ${resolvedColor}`,
|
|
754
|
+
animation: "reslide-pointer-ripple 0.5s ease-out forwards"
|
|
755
|
+
} }, r.id))]
|
|
756
|
+
})] });
|
|
757
|
+
}
|
|
625
758
|
//#endregion
|
|
626
759
|
//#region src/slide-context.ts
|
|
627
760
|
const SlideIndexContext = createContext(null);
|
|
@@ -632,17 +765,84 @@ function useSlideIndex() {
|
|
|
632
765
|
}
|
|
633
766
|
//#endregion
|
|
634
767
|
//#region src/PrintView.tsx
|
|
768
|
+
const DESIGN_WIDTH$2 = 960;
|
|
769
|
+
const DESIGN_HEIGHT$2 = 540;
|
|
770
|
+
function ScaledSlideCard({ children, index }) {
|
|
771
|
+
const frameRef = useRef(null);
|
|
772
|
+
const [scale, setScale] = useState(.3);
|
|
773
|
+
useEffect(() => {
|
|
774
|
+
const el = frameRef.current;
|
|
775
|
+
if (!el) return;
|
|
776
|
+
const observer = new ResizeObserver((entries) => {
|
|
777
|
+
for (const entry of entries) {
|
|
778
|
+
const { width } = entry.contentRect;
|
|
779
|
+
if (width > 0) setScale(width / DESIGN_WIDTH$2);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
observer.observe(el);
|
|
783
|
+
return () => observer.disconnect();
|
|
784
|
+
}, []);
|
|
785
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
786
|
+
className: "reslide-print-slide-card",
|
|
787
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
788
|
+
ref: frameRef,
|
|
789
|
+
className: "reslide-print-slide-frame",
|
|
790
|
+
children: /* @__PURE__ */ jsx(SlideIndexContext.Provider, {
|
|
791
|
+
value: index,
|
|
792
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
793
|
+
className: "reslide-print-slide-content",
|
|
794
|
+
style: {
|
|
795
|
+
width: DESIGN_WIDTH$2,
|
|
796
|
+
height: DESIGN_HEIGHT$2,
|
|
797
|
+
transform: `scale(${scale})`,
|
|
798
|
+
transformOrigin: "top left"
|
|
799
|
+
},
|
|
800
|
+
children
|
|
801
|
+
})
|
|
802
|
+
})
|
|
803
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
804
|
+
className: "reslide-print-slide-label",
|
|
805
|
+
children: index + 1
|
|
806
|
+
})]
|
|
807
|
+
});
|
|
808
|
+
}
|
|
635
809
|
/**
|
|
636
|
-
* Renders all slides
|
|
637
|
-
*
|
|
810
|
+
* Renders all slides in a printable handout layout.
|
|
811
|
+
* 6 slides per page (2 columns × 3 rows), each maintaining 16:9 ratio.
|
|
638
812
|
*/
|
|
639
813
|
function PrintView({ children }) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
814
|
+
const slides = Children.toArray(children);
|
|
815
|
+
const noop = useCallback(() => {}, []);
|
|
816
|
+
const noopNum = useCallback((_n) => {}, []);
|
|
817
|
+
const noopReg = useCallback((_i, _c) => {}, []);
|
|
818
|
+
const contextValue = useMemo(() => ({
|
|
819
|
+
currentSlide: 0,
|
|
820
|
+
totalSlides: slides.length,
|
|
821
|
+
clickStep: 999,
|
|
822
|
+
totalClickSteps: 0,
|
|
823
|
+
isOverview: false,
|
|
824
|
+
isFullscreen: false,
|
|
825
|
+
next: noop,
|
|
826
|
+
prev: noop,
|
|
827
|
+
goTo: noopNum,
|
|
828
|
+
toggleOverview: noop,
|
|
829
|
+
toggleFullscreen: noop,
|
|
830
|
+
registerClickSteps: noopReg
|
|
831
|
+
}), [
|
|
832
|
+
slides.length,
|
|
833
|
+
noop,
|
|
834
|
+
noopNum,
|
|
835
|
+
noopReg
|
|
836
|
+
]);
|
|
837
|
+
return /* @__PURE__ */ jsx(DeckContext.Provider, {
|
|
838
|
+
value: contextValue,
|
|
839
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
840
|
+
className: "reslide-print-view",
|
|
841
|
+
children: slides.map((slide, i) => /* @__PURE__ */ jsx(ScaledSlideCard, {
|
|
842
|
+
index: i,
|
|
843
|
+
children: slide
|
|
844
|
+
}, i))
|
|
845
|
+
})
|
|
646
846
|
});
|
|
647
847
|
}
|
|
648
848
|
//#endregion
|
|
@@ -655,7 +855,7 @@ function ProgressBar() {
|
|
|
655
855
|
className: "reslide-progress-bar",
|
|
656
856
|
style: {
|
|
657
857
|
position: "absolute",
|
|
658
|
-
|
|
858
|
+
bottom: 0,
|
|
659
859
|
left: 0,
|
|
660
860
|
width: "100%",
|
|
661
861
|
height: "3px",
|
|
@@ -664,12 +864,41 @@ function ProgressBar() {
|
|
|
664
864
|
children: /* @__PURE__ */ jsx("div", { style: {
|
|
665
865
|
height: "100%",
|
|
666
866
|
width: `${Math.min(slideProgress + clickFraction, 1) * 100}%`,
|
|
667
|
-
backgroundColor: "var(--slide-
|
|
867
|
+
backgroundColor: "var(--slide-progress, #16a34a)",
|
|
668
868
|
transition: "width 0.3s ease"
|
|
669
869
|
} })
|
|
670
870
|
});
|
|
671
871
|
}
|
|
672
872
|
//#endregion
|
|
873
|
+
//#region src/SlideNumber.tsx
|
|
874
|
+
/**
|
|
875
|
+
* Displays the current slide number in the bottom-right corner.
|
|
876
|
+
* Automatically reads state from DeckContext.
|
|
877
|
+
*/
|
|
878
|
+
function SlideNumber() {
|
|
879
|
+
const { currentSlide, totalSlides } = useDeck();
|
|
880
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
881
|
+
className: "reslide-slide-number",
|
|
882
|
+
style: {
|
|
883
|
+
position: "absolute",
|
|
884
|
+
bottom: "0.75rem",
|
|
885
|
+
right: "1rem",
|
|
886
|
+
fontSize: "0.75rem",
|
|
887
|
+
fontFamily: "system-ui, sans-serif",
|
|
888
|
+
fontVariantNumeric: "tabular-nums",
|
|
889
|
+
color: "var(--slide-text, #1a1a1a)",
|
|
890
|
+
opacity: .4,
|
|
891
|
+
pointerEvents: "none",
|
|
892
|
+
zIndex: 10
|
|
893
|
+
},
|
|
894
|
+
children: [
|
|
895
|
+
currentSlide + 1,
|
|
896
|
+
" / ",
|
|
897
|
+
totalSlides
|
|
898
|
+
]
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
//#endregion
|
|
673
902
|
//#region src/SlideTransition.tsx
|
|
674
903
|
const DURATION = 300;
|
|
675
904
|
function SlideTransition({ children, currentSlide, transition }) {
|
|
@@ -756,14 +985,20 @@ function useFullscreen(ref) {
|
|
|
756
985
|
}
|
|
757
986
|
//#endregion
|
|
758
987
|
//#region src/Deck.tsx
|
|
759
|
-
|
|
988
|
+
/** Design resolution — slides are authored at this size and scaled to fit */
|
|
989
|
+
const DESIGN_WIDTH$1 = 960;
|
|
990
|
+
const DESIGN_HEIGHT$1 = 540;
|
|
991
|
+
function Deck({ children, transition = "none", aspectRatio = 16 / 9 }) {
|
|
760
992
|
const containerRef = useRef(null);
|
|
993
|
+
const deckRef = useRef(null);
|
|
761
994
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
762
995
|
const [clickStep, setClickStep] = useState(0);
|
|
763
996
|
const [isOverview, setIsOverview] = useState(false);
|
|
764
997
|
const [isDrawing, setIsDrawing] = useState(false);
|
|
998
|
+
const [isPointer, setIsPointer] = useState(false);
|
|
765
999
|
const [isPrinting, setIsPrinting] = useState(false);
|
|
766
1000
|
const [clickStepsMap, setClickStepsMap] = useState({});
|
|
1001
|
+
const [scale, setScale] = useState(1);
|
|
767
1002
|
const { isFullscreen, toggleFullscreen } = useFullscreen(containerRef);
|
|
768
1003
|
const totalSlides = Children.count(children);
|
|
769
1004
|
const totalClickSteps = clickStepsMap[currentSlide] ?? 0;
|
|
@@ -826,6 +1061,7 @@ function Deck({ children, transition = "none" }) {
|
|
|
826
1061
|
useEffect(() => {
|
|
827
1062
|
function handleKeyDown(e) {
|
|
828
1063
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
|
1064
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
829
1065
|
switch (e.key) {
|
|
830
1066
|
case "ArrowRight":
|
|
831
1067
|
case " ":
|
|
@@ -853,6 +1089,12 @@ function Deck({ children, transition = "none" }) {
|
|
|
853
1089
|
case "d":
|
|
854
1090
|
e.preventDefault();
|
|
855
1091
|
setIsDrawing((v) => !v);
|
|
1092
|
+
setIsPointer(false);
|
|
1093
|
+
break;
|
|
1094
|
+
case "q":
|
|
1095
|
+
e.preventDefault();
|
|
1096
|
+
setIsPointer((v) => !v);
|
|
1097
|
+
setIsDrawing(false);
|
|
856
1098
|
break;
|
|
857
1099
|
}
|
|
858
1100
|
}
|
|
@@ -864,6 +1106,36 @@ function Deck({ children, transition = "none" }) {
|
|
|
864
1106
|
toggleOverview,
|
|
865
1107
|
toggleFullscreen
|
|
866
1108
|
]);
|
|
1109
|
+
useEffect(() => {
|
|
1110
|
+
const el = deckRef.current;
|
|
1111
|
+
if (!el) return;
|
|
1112
|
+
let startX = 0;
|
|
1113
|
+
let startY = 0;
|
|
1114
|
+
function handleTouchStart(e) {
|
|
1115
|
+
const touch = e.touches[0];
|
|
1116
|
+
startX = touch.clientX;
|
|
1117
|
+
startY = touch.clientY;
|
|
1118
|
+
}
|
|
1119
|
+
function handleTouchEnd(e) {
|
|
1120
|
+
if (isOverview || isDrawing) return;
|
|
1121
|
+
const touch = e.changedTouches[0];
|
|
1122
|
+
const dx = touch.clientX - startX;
|
|
1123
|
+
const dy = touch.clientY - startY;
|
|
1124
|
+
if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy) * 1.5) if (dx < 0) next();
|
|
1125
|
+
else prev();
|
|
1126
|
+
}
|
|
1127
|
+
el.addEventListener("touchstart", handleTouchStart, { passive: true });
|
|
1128
|
+
el.addEventListener("touchend", handleTouchEnd, { passive: true });
|
|
1129
|
+
return () => {
|
|
1130
|
+
el.removeEventListener("touchstart", handleTouchStart);
|
|
1131
|
+
el.removeEventListener("touchend", handleTouchEnd);
|
|
1132
|
+
};
|
|
1133
|
+
}, [
|
|
1134
|
+
next,
|
|
1135
|
+
prev,
|
|
1136
|
+
isOverview,
|
|
1137
|
+
isDrawing
|
|
1138
|
+
]);
|
|
867
1139
|
useEffect(() => {
|
|
868
1140
|
function onBeforePrint() {
|
|
869
1141
|
setIsPrinting(true);
|
|
@@ -878,6 +1150,18 @@ function Deck({ children, transition = "none" }) {
|
|
|
878
1150
|
window.removeEventListener("afterprint", onAfterPrint);
|
|
879
1151
|
};
|
|
880
1152
|
}, []);
|
|
1153
|
+
useEffect(() => {
|
|
1154
|
+
const el = deckRef.current;
|
|
1155
|
+
if (!el) return;
|
|
1156
|
+
const observer = new ResizeObserver((entries) => {
|
|
1157
|
+
for (const entry of entries) {
|
|
1158
|
+
const { width, height } = entry.contentRect;
|
|
1159
|
+
if (width > 0 && height > 0) setScale(Math.min(width / DESIGN_WIDTH$1, height / DESIGN_HEIGHT$1));
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
observer.observe(el);
|
|
1163
|
+
return () => observer.disconnect();
|
|
1164
|
+
}, []);
|
|
881
1165
|
const contextValue = useMemo(() => ({
|
|
882
1166
|
currentSlide,
|
|
883
1167
|
totalSlides,
|
|
@@ -905,21 +1189,32 @@ function Deck({ children, transition = "none" }) {
|
|
|
905
1189
|
toggleFullscreen,
|
|
906
1190
|
registerClickSteps
|
|
907
1191
|
]);
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1192
|
+
const useLetterbox = aspectRatio > 0;
|
|
1193
|
+
const slideArea = /* @__PURE__ */ jsxs("div", {
|
|
1194
|
+
ref: deckRef,
|
|
1195
|
+
className: "reslide-deck",
|
|
1196
|
+
style: {
|
|
1197
|
+
position: "relative",
|
|
1198
|
+
width: "100%",
|
|
1199
|
+
height: "100%",
|
|
1200
|
+
overflow: "hidden",
|
|
1201
|
+
backgroundColor: "var(--slide-bg, #fff)",
|
|
1202
|
+
color: "var(--slide-text, #1a1a1a)",
|
|
1203
|
+
cursor: isPointer ? "none" : void 0
|
|
1204
|
+
},
|
|
1205
|
+
children: [
|
|
1206
|
+
isPrinting ? /* @__PURE__ */ jsx(PrintView, { children }) : /* @__PURE__ */ jsx("div", {
|
|
1207
|
+
className: "reslide-scale-wrapper",
|
|
1208
|
+
style: {
|
|
1209
|
+
position: "absolute",
|
|
1210
|
+
top: "50%",
|
|
1211
|
+
left: "50%",
|
|
1212
|
+
width: DESIGN_WIDTH$1,
|
|
1213
|
+
height: DESIGN_HEIGHT$1,
|
|
1214
|
+
transform: `translate(-50%, -50%) scale(${scale})`,
|
|
1215
|
+
transformOrigin: "center center"
|
|
1216
|
+
},
|
|
1217
|
+
children: isOverview ? /* @__PURE__ */ jsx(OverviewGrid, {
|
|
923
1218
|
totalSlides,
|
|
924
1219
|
goTo,
|
|
925
1220
|
children
|
|
@@ -927,22 +1222,61 @@ function Deck({ children, transition = "none" }) {
|
|
|
927
1222
|
currentSlide,
|
|
928
1223
|
transition,
|
|
929
1224
|
children
|
|
930
|
-
}),
|
|
931
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ClickNavigation, {
|
|
932
|
-
onPrev: prev,
|
|
933
|
-
onNext: next,
|
|
934
|
-
disabled: isDrawing
|
|
935
|
-
}),
|
|
936
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ProgressBar, {}),
|
|
937
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(DrawingLayer, {
|
|
938
|
-
active: isDrawing,
|
|
939
|
-
currentSlide
|
|
940
|
-
}),
|
|
941
|
-
!isPrinting && /* @__PURE__ */ jsx(NavigationBar, {
|
|
942
|
-
isDrawing,
|
|
943
|
-
onToggleDrawing: () => setIsDrawing((v) => !v)
|
|
944
1225
|
})
|
|
945
|
-
|
|
1226
|
+
}),
|
|
1227
|
+
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ClickNavigation, {
|
|
1228
|
+
onPrev: prev,
|
|
1229
|
+
onNext: next,
|
|
1230
|
+
disabled: isDrawing
|
|
1231
|
+
}),
|
|
1232
|
+
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ProgressBar, {}),
|
|
1233
|
+
!isOverview && !isPrinting && /* @__PURE__ */ jsx(SlideNumber, {}),
|
|
1234
|
+
!isOverview && !isPrinting && /* @__PURE__ */ jsx(DrawingLayer, {
|
|
1235
|
+
active: isDrawing,
|
|
1236
|
+
currentSlide
|
|
1237
|
+
}),
|
|
1238
|
+
!isOverview && !isPrinting && /* @__PURE__ */ jsx(Pointer, { active: isPointer }),
|
|
1239
|
+
!isPrinting && /* @__PURE__ */ jsx(NavigationBar, {
|
|
1240
|
+
isDrawing,
|
|
1241
|
+
onToggleDrawing: () => setIsDrawing((v) => !v)
|
|
1242
|
+
})
|
|
1243
|
+
]
|
|
1244
|
+
});
|
|
1245
|
+
if (!useLetterbox) return /* @__PURE__ */ jsx(DeckContext.Provider, {
|
|
1246
|
+
value: contextValue,
|
|
1247
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1248
|
+
ref: containerRef,
|
|
1249
|
+
style: {
|
|
1250
|
+
width: "100%",
|
|
1251
|
+
height: "100%"
|
|
1252
|
+
},
|
|
1253
|
+
children: slideArea
|
|
1254
|
+
})
|
|
1255
|
+
});
|
|
1256
|
+
return /* @__PURE__ */ jsx(DeckContext.Provider, {
|
|
1257
|
+
value: contextValue,
|
|
1258
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1259
|
+
ref: containerRef,
|
|
1260
|
+
className: "reslide-letterbox",
|
|
1261
|
+
style: {
|
|
1262
|
+
width: "100%",
|
|
1263
|
+
height: "100%",
|
|
1264
|
+
display: "flex",
|
|
1265
|
+
alignItems: "center",
|
|
1266
|
+
justifyContent: "center",
|
|
1267
|
+
backgroundColor: "var(--reslide-letterbox-bg, #000)",
|
|
1268
|
+
overflow: "hidden"
|
|
1269
|
+
},
|
|
1270
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1271
|
+
className: "reslide-letterbox-inner",
|
|
1272
|
+
style: {
|
|
1273
|
+
height: "100%",
|
|
1274
|
+
maxWidth: "100%",
|
|
1275
|
+
aspectRatio: String(aspectRatio),
|
|
1276
|
+
overflow: "hidden"
|
|
1277
|
+
},
|
|
1278
|
+
children: slideArea
|
|
1279
|
+
})
|
|
946
1280
|
})
|
|
947
1281
|
});
|
|
948
1282
|
}
|
|
@@ -963,7 +1297,7 @@ function OverviewGrid({ children, totalSlides, goTo }) {
|
|
|
963
1297
|
type: "button",
|
|
964
1298
|
onClick: () => goTo(i),
|
|
965
1299
|
style: {
|
|
966
|
-
border: "1px solid var(--slide-accent, #
|
|
1300
|
+
border: "1px solid var(--slide-accent, #16a34a)",
|
|
967
1301
|
borderRadius: "0.5rem",
|
|
968
1302
|
overflow: "hidden",
|
|
969
1303
|
cursor: "pointer",
|
|
@@ -1110,7 +1444,7 @@ function Slide({ children, layout = "default", image, className, style }) {
|
|
|
1110
1444
|
alignItems: "center",
|
|
1111
1445
|
textAlign: "center",
|
|
1112
1446
|
padding: "3rem 4rem",
|
|
1113
|
-
backgroundColor: "var(--slide-accent, #
|
|
1447
|
+
backgroundColor: "var(--slide-accent, #16a34a)",
|
|
1114
1448
|
color: "var(--slide-section-text, #fff)",
|
|
1115
1449
|
...style
|
|
1116
1450
|
},
|
|
@@ -1129,7 +1463,7 @@ function Slide({ children, layout = "default", image, className, style }) {
|
|
|
1129
1463
|
style: {
|
|
1130
1464
|
fontSize: "1.5em",
|
|
1131
1465
|
fontStyle: "italic",
|
|
1132
|
-
borderLeft: "4px solid var(--slide-accent, #
|
|
1466
|
+
borderLeft: "4px solid var(--slide-accent, #16a34a)",
|
|
1133
1467
|
paddingLeft: "1.5rem",
|
|
1134
1468
|
margin: 0
|
|
1135
1469
|
},
|
|
@@ -1254,6 +1588,51 @@ SlotRight.displayName = "SlotRight";
|
|
|
1254
1588
|
SlotRight.__reslideSlot = "right";
|
|
1255
1589
|
//#endregion
|
|
1256
1590
|
//#region src/PresenterView.tsx
|
|
1591
|
+
const DESIGN_WIDTH = 960;
|
|
1592
|
+
const DESIGN_HEIGHT = 540;
|
|
1593
|
+
/**
|
|
1594
|
+
* Scaled slide preview — renders at design size and scales to fit container.
|
|
1595
|
+
* Maintains 16:9 aspect ratio.
|
|
1596
|
+
*/
|
|
1597
|
+
function ScaledSlide({ children }) {
|
|
1598
|
+
const containerRef = useRef(null);
|
|
1599
|
+
const [scale, setScale] = useState(1);
|
|
1600
|
+
useEffect(() => {
|
|
1601
|
+
const el = containerRef.current;
|
|
1602
|
+
if (!el) return;
|
|
1603
|
+
const observer = new ResizeObserver((entries) => {
|
|
1604
|
+
for (const entry of entries) {
|
|
1605
|
+
const { width, height } = entry.contentRect;
|
|
1606
|
+
if (width > 0 && height > 0) setScale(Math.min(width / DESIGN_WIDTH, height / DESIGN_HEIGHT));
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
observer.observe(el);
|
|
1610
|
+
return () => observer.disconnect();
|
|
1611
|
+
}, []);
|
|
1612
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1613
|
+
ref: containerRef,
|
|
1614
|
+
style: {
|
|
1615
|
+
width: "100%",
|
|
1616
|
+
aspectRatio: "16 / 9",
|
|
1617
|
+
position: "relative",
|
|
1618
|
+
overflow: "hidden",
|
|
1619
|
+
backgroundColor: "var(--slide-bg, #fff)",
|
|
1620
|
+
color: "var(--slide-text, #1a1a1a)"
|
|
1621
|
+
},
|
|
1622
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1623
|
+
style: {
|
|
1624
|
+
position: "absolute",
|
|
1625
|
+
top: "50%",
|
|
1626
|
+
left: "50%",
|
|
1627
|
+
width: DESIGN_WIDTH,
|
|
1628
|
+
height: DESIGN_HEIGHT,
|
|
1629
|
+
transform: `translate(-50%, -50%) scale(${scale})`,
|
|
1630
|
+
transformOrigin: "center center"
|
|
1631
|
+
},
|
|
1632
|
+
children
|
|
1633
|
+
})
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1257
1636
|
/**
|
|
1258
1637
|
* Presenter view that syncs with the main presentation window.
|
|
1259
1638
|
* Shows: current slide, next slide preview, notes, and timer.
|
|
@@ -1344,16 +1723,17 @@ function PresenterView({ children, notes }) {
|
|
|
1344
1723
|
children: [
|
|
1345
1724
|
/* @__PURE__ */ jsx("div", {
|
|
1346
1725
|
style: {
|
|
1347
|
-
border: "2px solid #
|
|
1726
|
+
border: "2px solid #16a34a",
|
|
1348
1727
|
borderRadius: "0.5rem",
|
|
1349
1728
|
overflow: "hidden",
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1729
|
+
display: "flex",
|
|
1730
|
+
alignItems: "center",
|
|
1731
|
+
justifyContent: "center",
|
|
1732
|
+
backgroundColor: "#000"
|
|
1353
1733
|
},
|
|
1354
1734
|
children: /* @__PURE__ */ jsx(SlideIndexContext.Provider, {
|
|
1355
1735
|
value: currentSlide,
|
|
1356
|
-
children: slides[currentSlide]
|
|
1736
|
+
children: /* @__PURE__ */ jsx(ScaledSlide, { children: slides[currentSlide] })
|
|
1357
1737
|
})
|
|
1358
1738
|
}),
|
|
1359
1739
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -1365,25 +1745,29 @@ function PresenterView({ children, notes }) {
|
|
|
1365
1745
|
},
|
|
1366
1746
|
children: [/* @__PURE__ */ jsx("div", {
|
|
1367
1747
|
style: {
|
|
1368
|
-
flex: "0 0 40%",
|
|
1369
1748
|
border: "1px solid #334155",
|
|
1370
1749
|
borderRadius: "0.5rem",
|
|
1371
1750
|
overflow: "hidden",
|
|
1372
1751
|
opacity: .8,
|
|
1373
|
-
|
|
1374
|
-
|
|
1752
|
+
display: "flex",
|
|
1753
|
+
alignItems: "center",
|
|
1754
|
+
justifyContent: "center",
|
|
1755
|
+
backgroundColor: "#000"
|
|
1375
1756
|
},
|
|
1376
|
-
children: currentSlide < totalSlides - 1
|
|
1757
|
+
children: currentSlide < totalSlides - 1 ? /* @__PURE__ */ jsx(SlideIndexContext.Provider, {
|
|
1377
1758
|
value: currentSlide + 1,
|
|
1378
|
-
children: /* @__PURE__ */ jsx(
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1759
|
+
children: /* @__PURE__ */ jsx(ScaledSlide, { children: slides[currentSlide + 1] })
|
|
1760
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
1761
|
+
style: {
|
|
1762
|
+
width: "100%",
|
|
1763
|
+
aspectRatio: "16 / 9",
|
|
1764
|
+
display: "flex",
|
|
1765
|
+
alignItems: "center",
|
|
1766
|
+
justifyContent: "center",
|
|
1767
|
+
color: "#64748b",
|
|
1768
|
+
fontSize: "0.875rem"
|
|
1769
|
+
},
|
|
1770
|
+
children: "End of slides"
|
|
1387
1771
|
})
|
|
1388
1772
|
}), /* @__PURE__ */ jsxs("div", {
|
|
1389
1773
|
style: {
|
|
@@ -1596,7 +1980,7 @@ function extractHeading(node) {
|
|
|
1596
1980
|
if (!isValidElement(node)) return null;
|
|
1597
1981
|
const el = node;
|
|
1598
1982
|
const type = el.type;
|
|
1599
|
-
if (type === "h1" || type === "h2") return extractText(el.props.children);
|
|
1983
|
+
if (type === "h1" || type === "h2") return extractText$1(el.props.children);
|
|
1600
1984
|
const children = el.props.children;
|
|
1601
1985
|
if (children == null) return null;
|
|
1602
1986
|
let result = null;
|
|
@@ -1608,12 +1992,12 @@ function extractHeading(node) {
|
|
|
1608
1992
|
return result;
|
|
1609
1993
|
}
|
|
1610
1994
|
/** Extract plain text from a React node tree */
|
|
1611
|
-
function extractText(node) {
|
|
1995
|
+
function extractText$1(node) {
|
|
1612
1996
|
if (node == null) return "";
|
|
1613
1997
|
if (typeof node === "string") return node;
|
|
1614
1998
|
if (typeof node === "number") return String(node);
|
|
1615
|
-
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
1616
|
-
if (isValidElement(node)) return extractText(node.props.children);
|
|
1999
|
+
if (Array.isArray(node)) return node.map(extractText$1).join("");
|
|
2000
|
+
if (isValidElement(node)) return extractText$1(node.props.children);
|
|
1617
2001
|
return "";
|
|
1618
2002
|
}
|
|
1619
2003
|
/**
|
|
@@ -1663,7 +2047,7 @@ function Toc({ children, className, style }) {
|
|
|
1663
2047
|
fontSize: "0.875rem",
|
|
1664
2048
|
lineHeight: 1.4,
|
|
1665
2049
|
fontFamily: "inherit",
|
|
1666
|
-
color: isActive ? "var(--toc-active-text, var(--slide-accent, #
|
|
2050
|
+
color: isActive ? "var(--toc-active-text, var(--slide-accent, #16a34a))" : "var(--toc-text, var(--slide-text, #1a1a1a))",
|
|
1667
2051
|
backgroundColor: isActive ? "var(--toc-active-bg, rgba(59, 130, 246, 0.1))" : "transparent",
|
|
1668
2052
|
fontWeight: isActive ? 600 : 400,
|
|
1669
2053
|
transition: "background-color 0.15s, color 0.15s"
|
|
@@ -1691,6 +2075,25 @@ function Toc({ children, className, style }) {
|
|
|
1691
2075
|
//#endregion
|
|
1692
2076
|
//#region src/Mermaid.tsx
|
|
1693
2077
|
/**
|
|
2078
|
+
* Recursively extract text content from React children.
|
|
2079
|
+
* MDX wraps raw text in paragraphs and other elements,
|
|
2080
|
+
* so we need to traverse the tree to get the original text.
|
|
2081
|
+
*/
|
|
2082
|
+
function extractText(node) {
|
|
2083
|
+
if (node == null || typeof node === "boolean") return "";
|
|
2084
|
+
if (typeof node === "string") return node;
|
|
2085
|
+
if (typeof node === "number") return String(node);
|
|
2086
|
+
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
2087
|
+
if (isValidElement(node)) {
|
|
2088
|
+
const { children } = node.props;
|
|
2089
|
+
const text = extractText(children);
|
|
2090
|
+
const tag = typeof node.type === "string" ? node.type : "";
|
|
2091
|
+
if (tag === "p" || tag === "br") return `${text}\n`;
|
|
2092
|
+
return text;
|
|
2093
|
+
}
|
|
2094
|
+
return "";
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
1694
2097
|
* Renders a Mermaid diagram.
|
|
1695
2098
|
*
|
|
1696
2099
|
* Usage in MDX:
|
|
@@ -1709,7 +2112,9 @@ function Mermaid({ children }) {
|
|
|
1709
2112
|
const [svg, setSvg] = useState("");
|
|
1710
2113
|
const [error, setError] = useState("");
|
|
1711
2114
|
const id = useId().replace(/:/g, "_");
|
|
2115
|
+
const code = extractText(children).trim();
|
|
1712
2116
|
useEffect(() => {
|
|
2117
|
+
if (!code) return;
|
|
1713
2118
|
let cancelled = false;
|
|
1714
2119
|
async function render() {
|
|
1715
2120
|
try {
|
|
@@ -1719,8 +2124,6 @@ function Mermaid({ children }) {
|
|
|
1719
2124
|
theme: "default",
|
|
1720
2125
|
securityLevel: "loose"
|
|
1721
2126
|
});
|
|
1722
|
-
const code = typeof children === "string" ? children.trim() : "";
|
|
1723
|
-
if (!code) return;
|
|
1724
2127
|
const { svg: rendered } = await mermaid.default.render(`mermaid-${id}`, code);
|
|
1725
2128
|
if (!cancelled) {
|
|
1726
2129
|
setSvg(rendered);
|
|
@@ -1734,7 +2137,7 @@ function Mermaid({ children }) {
|
|
|
1734
2137
|
return () => {
|
|
1735
2138
|
cancelled = true;
|
|
1736
2139
|
};
|
|
1737
|
-
}, [
|
|
2140
|
+
}, [code, id]);
|
|
1738
2141
|
if (error) return /* @__PURE__ */ jsx("div", {
|
|
1739
2142
|
className: "reslide-mermaid reslide-mermaid--error",
|
|
1740
2143
|
children: /* @__PURE__ */ jsx("pre", { children: error })
|
|
@@ -1746,4 +2149,4 @@ function Mermaid({ children }) {
|
|
|
1746
2149
|
});
|
|
1747
2150
|
}
|
|
1748
2151
|
//#endregion
|
|
1749
|
-
export {
|
|
2152
|
+
export { DeckContext as C, DrawingLayer as S, ClickNavigation as T, useSlideIndex as _, PresenterView as a, isPresenterView as b, Mark as c, Slide as d, Deck as f, SlideIndexContext as g, PrintView as h, GlobalLayer as i, Click as l, ProgressBar as m, Toc as n, SlotRight as o, SlideNumber as p, Draggable as r, Notes as s, Mermaid as t, ClickSteps as u, Pointer as v, useDeck as w, openPresenterWindow as x, NavigationBar as y };
|
|
@@ -9,10 +9,13 @@ interface DeckProps {
|
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
/** Slide transition type */
|
|
11
11
|
transition?: TransitionType;
|
|
12
|
+
/** Aspect ratio (default: 16/9). Set to 0 to disable and fill parent. */
|
|
13
|
+
aspectRatio?: number;
|
|
12
14
|
}
|
|
13
15
|
declare function Deck({
|
|
14
16
|
children,
|
|
15
|
-
transition
|
|
17
|
+
transition,
|
|
18
|
+
aspectRatio
|
|
16
19
|
}: DeckProps): react_jsx_runtime0.JSX.Element;
|
|
17
20
|
//#endregion
|
|
18
21
|
//#region src/Slide.d.ts
|
|
@@ -102,6 +105,17 @@ declare namespace SlotRight {
|
|
|
102
105
|
var __reslideSlot: "right";
|
|
103
106
|
}
|
|
104
107
|
//#endregion
|
|
108
|
+
//#region src/PrintView.d.ts
|
|
109
|
+
/**
|
|
110
|
+
* Renders all slides in a printable handout layout.
|
|
111
|
+
* 6 slides per page (2 columns × 3 rows), each maintaining 16:9 ratio.
|
|
112
|
+
*/
|
|
113
|
+
declare function PrintView({
|
|
114
|
+
children
|
|
115
|
+
}: {
|
|
116
|
+
children: ReactNode;
|
|
117
|
+
}): react_jsx_runtime0.JSX.Element;
|
|
118
|
+
//#endregion
|
|
105
119
|
//#region src/PresenterView.d.ts
|
|
106
120
|
interface PresenterViewProps {
|
|
107
121
|
children: ReactNode;
|
|
@@ -216,8 +230,8 @@ declare function Toc({
|
|
|
216
230
|
//#endregion
|
|
217
231
|
//#region src/Mermaid.d.ts
|
|
218
232
|
interface MermaidProps {
|
|
219
|
-
/** Mermaid diagram code */
|
|
220
|
-
children:
|
|
233
|
+
/** Mermaid diagram code (string or MDX children) */
|
|
234
|
+
children: ReactNode;
|
|
221
235
|
}
|
|
222
236
|
/**
|
|
223
237
|
* Renders a Mermaid diagram.
|
|
@@ -237,4 +251,4 @@ declare function Mermaid({
|
|
|
237
251
|
children
|
|
238
252
|
}: MermaidProps): react_jsx_runtime0.JSX.Element;
|
|
239
253
|
//#endregion
|
|
240
|
-
export {
|
|
254
|
+
export { SlideProps as C, TransitionType as E, Slide as S, DeckProps as T, Mark as _, Draggable as a, ClickProps as b, GlobalLayerProps as c, PresenterView as d, PresenterViewProps as f, NotesProps as g, Notes as h, TocProps as i, isPresenterView as l, SlotRight as m, MermaidProps as n, DraggableProps as o, PrintView as p, Toc as r, GlobalLayer as s, Mermaid as t, openPresenterWindow as u, MarkProps as v, Deck as w, ClickSteps as x, Click as y };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as SlideProps, E as TransitionType, S as Slide, T as DeckProps, _ as Mark, a as Draggable, b as ClickProps, c as GlobalLayerProps, d as PresenterView, f as PresenterViewProps, g as NotesProps, h as Notes, i as TocProps, l as isPresenterView, m as SlotRight, n as MermaidProps, o as DraggableProps, p as PrintView, r as Toc, s as GlobalLayer, t as Mermaid, u as openPresenterWindow, v as MarkProps, w as Deck, x as ClickSteps, y as Click } from "./Mermaid-DWHv8vPf.mjs";
|
|
2
2
|
import * as react from "react";
|
|
3
|
-
import { CSSProperties, ElementType
|
|
3
|
+
import { CSSProperties, ElementType } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|
|
6
|
-
//#region src/PrintView.d.ts
|
|
7
|
-
/**
|
|
8
|
-
* Renders all slides vertically for print/PDF export.
|
|
9
|
-
* Use with @media print CSS to generate PDFs via browser print.
|
|
10
|
-
*/
|
|
11
|
-
declare function PrintView({
|
|
12
|
-
children
|
|
13
|
-
}: {
|
|
14
|
-
children: ReactNode;
|
|
15
|
-
}): react_jsx_runtime0.JSX.Element;
|
|
16
|
-
//#endregion
|
|
17
6
|
//#region src/DrawingLayer.d.ts
|
|
18
7
|
interface DrawingLayerProps {
|
|
19
8
|
/** Whether drawing mode is active */
|
|
@@ -77,7 +66,7 @@ interface ClickNavigationProps {
|
|
|
77
66
|
* Invisible click zones on the left/right edges of the slide.
|
|
78
67
|
* Clicking the left ~15% goes to the previous slide,
|
|
79
68
|
* clicking the right ~15% goes to the next slide.
|
|
80
|
-
* Shows a
|
|
69
|
+
* Shows a gradient overlay and arrow indicator on hover (docswell style).
|
|
81
70
|
*/
|
|
82
71
|
declare function ClickNavigation({
|
|
83
72
|
onPrev,
|
|
@@ -98,9 +87,36 @@ declare function NavigationBar({
|
|
|
98
87
|
onToggleDrawing: () => void;
|
|
99
88
|
}): react_jsx_runtime0.JSX.Element;
|
|
100
89
|
//#endregion
|
|
90
|
+
//#region src/Pointer.d.ts
|
|
91
|
+
interface PointerProps {
|
|
92
|
+
/** Whether pointer mode is active */
|
|
93
|
+
active: boolean;
|
|
94
|
+
/** Pointer color (default: uses --slide-accent or #16a34a) */
|
|
95
|
+
color?: string;
|
|
96
|
+
/** Pointer diameter in pixels */
|
|
97
|
+
size?: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Presentation pointer — a large colored circle that follows the cursor.
|
|
101
|
+
* Clicking creates an expanding ripple effect to highlight a point.
|
|
102
|
+
* Toggle with `q` key (handled in Deck).
|
|
103
|
+
*/
|
|
104
|
+
declare function Pointer({
|
|
105
|
+
active,
|
|
106
|
+
color,
|
|
107
|
+
size
|
|
108
|
+
}: PointerProps): react_jsx_runtime0.JSX.Element;
|
|
109
|
+
//#endregion
|
|
101
110
|
//#region src/ProgressBar.d.ts
|
|
102
111
|
declare function ProgressBar(): react_jsx_runtime0.JSX.Element;
|
|
103
112
|
//#endregion
|
|
113
|
+
//#region src/SlideNumber.d.ts
|
|
114
|
+
/**
|
|
115
|
+
* Displays the current slide number in the bottom-right corner.
|
|
116
|
+
* Automatically reads state from DeckContext.
|
|
117
|
+
*/
|
|
118
|
+
declare function SlideNumber(): react_jsx_runtime0.JSX.Element;
|
|
119
|
+
//#endregion
|
|
104
120
|
//#region src/ReslideEmbed.d.ts
|
|
105
121
|
interface ReslideEmbedProps {
|
|
106
122
|
/** Compiled MDX code from compileMdxSlides() */
|
|
@@ -175,4 +191,4 @@ declare function useDeck(): DeckContextValue;
|
|
|
175
191
|
declare const SlideIndexContext: react.Context<number | null>;
|
|
176
192
|
declare function useSlideIndex(): number;
|
|
177
193
|
//#endregion
|
|
178
|
-
export { Click, ClickNavigation, type ClickProps, ClickSteps, CodeEditor, type CodeEditorProps, Deck, type DeckActions, DeckContext, type DeckContextValue, type DeckProps, type DeckState, Draggable, type DraggableProps, DrawingLayer, type DrawingLayerProps, GlobalLayer, type GlobalLayerProps, Mark, type MarkProps, Mermaid, type MermaidProps, NavigationBar, Notes, type NotesProps, PresenterView, type PresenterViewProps, PrintView, ProgressBar, ReslideEmbed, type ReslideEmbedProps, Slide, SlideIndexContext, type SlideProps, SlotRight, Toc, type TocProps, type TransitionType, isPresenterView, openPresenterWindow, useDeck, useSlideIndex };
|
|
194
|
+
export { Click, ClickNavigation, type ClickProps, ClickSteps, CodeEditor, type CodeEditorProps, Deck, type DeckActions, DeckContext, type DeckContextValue, type DeckProps, type DeckState, Draggable, type DraggableProps, DrawingLayer, type DrawingLayerProps, GlobalLayer, type GlobalLayerProps, Mark, type MarkProps, Mermaid, type MermaidProps, NavigationBar, Notes, type NotesProps, Pointer, type PointerProps, PresenterView, type PresenterViewProps, PrintView, ProgressBar, ReslideEmbed, type ReslideEmbedProps, Slide, SlideIndexContext, SlideNumber, type SlideProps, SlotRight, Toc, type TocProps, type TransitionType, isPresenterView, openPresenterWindow, useDeck, useSlideIndex };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as DeckContext, S as DrawingLayer, T as ClickNavigation, _ as useSlideIndex, a as PresenterView, b as isPresenterView, c as Mark, d as Slide, f as Deck, g as SlideIndexContext, h as PrintView, i as GlobalLayer, l as Click, m as ProgressBar, n as Toc, o as SlotRight, p as SlideNumber, r as Draggable, s as Notes, t as Mermaid, u as ClickSteps, v as Pointer, w as useDeck, x as openPresenterWindow, y as NavigationBar } from "./Mermaid-BfetSkya.mjs";
|
|
2
2
|
import { Fragment, useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import * as runtime from "react/jsx-runtime";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -117,7 +117,10 @@ const builtinComponents = {
|
|
|
117
117
|
Notes,
|
|
118
118
|
SlotRight,
|
|
119
119
|
GlobalLayer,
|
|
120
|
-
Draggable
|
|
120
|
+
Draggable,
|
|
121
|
+
Mermaid,
|
|
122
|
+
Toc,
|
|
123
|
+
CodeEditor
|
|
121
124
|
};
|
|
122
125
|
/**
|
|
123
126
|
* Renders compiled MDX slides as a full reslide presentation.
|
|
@@ -177,4 +180,4 @@ function ReslideEmbed({ code, transition, components: userComponents, className,
|
|
|
177
180
|
});
|
|
178
181
|
}
|
|
179
182
|
//#endregion
|
|
180
|
-
export { Click, ClickNavigation, ClickSteps, CodeEditor, Deck, DeckContext, Draggable, DrawingLayer, GlobalLayer, Mark, Mermaid, NavigationBar, Notes, PresenterView, PrintView, ProgressBar, ReslideEmbed, Slide, SlideIndexContext, SlotRight, Toc, isPresenterView, openPresenterWindow, useDeck, useSlideIndex };
|
|
183
|
+
export { Click, ClickNavigation, ClickSteps, CodeEditor, Deck, DeckContext, Draggable, DrawingLayer, GlobalLayer, Mark, Mermaid, NavigationBar, Notes, Pointer, PresenterView, PrintView, ProgressBar, ReslideEmbed, Slide, SlideIndexContext, SlideNumber, SlotRight, Toc, isPresenterView, openPresenterWindow, useDeck, useSlideIndex };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as Slide, _ as Mark, a as Draggable, d as PresenterView, f as PresenterViewProps, h as Notes, l as isPresenterView, m as SlotRight, p as PrintView, r as Toc, s as GlobalLayer, t as Mermaid, w as Deck, x as ClickSteps, y as Click } from "./Mermaid-DWHv8vPf.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/mdx-components.d.ts
|
|
4
4
|
declare const reslideComponents: {
|
|
@@ -15,4 +15,4 @@ declare const reslideComponents: {
|
|
|
15
15
|
Mermaid: typeof Mermaid;
|
|
16
16
|
};
|
|
17
17
|
//#endregion
|
|
18
|
-
export { PresenterView, type PresenterViewProps, isPresenterView, reslideComponents };
|
|
18
|
+
export { PresenterView, type PresenterViewProps, PrintView, isPresenterView, reslideComponents };
|
package/dist/mdx-components.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as PresenterView, c as Mark, d as Slide, f as Deck, i as GlobalLayer, l as Click, n as Toc, o as SlotRight, r as Draggable, s as Notes, t as Mermaid, u as ClickSteps
|
|
1
|
+
import { a as PresenterView, b as isPresenterView, c as Mark, d as Slide, f as Deck, h as PrintView, i as GlobalLayer, l as Click, n as Toc, o as SlotRight, r as Draggable, s as Notes, t as Mermaid, u as ClickSteps } from "./Mermaid-BfetSkya.mjs";
|
|
2
2
|
//#region src/mdx-components.ts
|
|
3
3
|
const reslideComponents = {
|
|
4
4
|
Deck,
|
|
@@ -14,4 +14,4 @@ const reslideComponents = {
|
|
|
14
14
|
Mermaid
|
|
15
15
|
};
|
|
16
16
|
//#endregion
|
|
17
|
-
export { PresenterView, isPresenterView, reslideComponents };
|
|
17
|
+
export { PresenterView, PrintView, isPresenterView, reslideComponents };
|
package/package.json
CHANGED
package/src/themes/default.css
CHANGED
package/src/themes/print.css
CHANGED
|
@@ -1,58 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Print stylesheet for reslide presentations.
|
|
3
|
-
*
|
|
3
|
+
* Handout layout: 6 slides per page (2 columns × 3 rows).
|
|
4
4
|
*
|
|
5
|
-
* Usage: import '@reslide/core/
|
|
6
|
-
*
|
|
7
|
-
* This renders all slides vertically for printing,
|
|
8
|
-
* each slide on its own page at 16:9 aspect ratio.
|
|
5
|
+
* Usage: import '@reslide-dev/core/print.css'
|
|
9
6
|
*/
|
|
10
|
-
@media print {
|
|
11
|
-
/* Reset the deck for print layout */
|
|
12
|
-
.reslide-deck {
|
|
13
|
-
width: auto !important;
|
|
14
|
-
height: auto !important;
|
|
15
|
-
overflow: visible !important;
|
|
16
|
-
position: static !important;
|
|
17
|
-
}
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
8
|
+
/* ── Screen preview ── */
|
|
9
|
+
.reslide-print-page {
|
|
10
|
+
background: #f0f0f0;
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
padding: 2rem;
|
|
13
|
+
-webkit-print-color-adjust: exact;
|
|
14
|
+
print-color-adjust: exact;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.reslide-print-page .reslide-print-view {
|
|
18
|
+
max-width: 800px;
|
|
19
|
+
margin: 0 auto;
|
|
20
|
+
display: grid;
|
|
21
|
+
grid-template-columns: 1fr 1fr;
|
|
22
|
+
gap: 1.5rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.reslide-print-page .reslide-print-slide-card {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
gap: 0.25rem;
|
|
29
|
+
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
.reslide-print-page .reslide-print-slide-frame {
|
|
32
|
+
aspect-ratio: 16 / 9;
|
|
33
|
+
border: 1px solid #ccc;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
background: var(--slide-bg, white);
|
|
37
|
+
position: relative;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.reslide-print-page .reslide-print-slide-content {
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.reslide-print-page .reslide-print-slide-label {
|
|
45
|
+
font-size: 0.75rem;
|
|
46
|
+
color: #888;
|
|
47
|
+
text-align: center;
|
|
48
|
+
font-family: system-ui, sans-serif;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Show all click content */
|
|
52
|
+
.reslide-print-page .reslide-click {
|
|
53
|
+
opacity: 1 !important;
|
|
54
|
+
visibility: visible !important;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* ── Print layout ── */
|
|
58
|
+
@media print {
|
|
59
|
+
.reslide-print-page {
|
|
60
|
+
background: white !important;
|
|
61
|
+
padding: 0 !important;
|
|
33
62
|
}
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
.reslide-print-page .reslide-print-view {
|
|
65
|
+
max-width: none !important;
|
|
66
|
+
display: grid !important;
|
|
67
|
+
grid-template-columns: 1fr 1fr !important;
|
|
68
|
+
gap: 8mm !important;
|
|
69
|
+
padding: 10mm !important;
|
|
41
70
|
}
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
visibility: visible !important;
|
|
72
|
+
.reslide-print-page .reslide-print-slide-frame {
|
|
73
|
+
border: 0.5pt solid #ccc !important;
|
|
74
|
+
border-radius: 0 !important;
|
|
47
75
|
}
|
|
48
76
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
.reslide-print-page .reslide-print-slide-label {
|
|
78
|
+
font-size: 7pt !important;
|
|
79
|
+
color: #666 !important;
|
|
80
|
+
margin-top: 1mm !important;
|
|
52
81
|
}
|
|
53
82
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
/* 6 slides per page (2×3) — force page break after every 6th card */
|
|
84
|
+
.reslide-print-page .reslide-print-slide-card:nth-child(6n) {
|
|
85
|
+
page-break-after: always;
|
|
57
86
|
}
|
|
58
87
|
}
|