@reslide-dev/core 0.2.2 → 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-CUCMRIkQ.mjs → Mermaid-BfetSkya.mjs} +456 -83
- package/dist/{Mermaid-CUQUPHdK.d.mts → Mermaid-DWHv8vPf.d.mts} +18 -4
- package/dist/index.d.mts +24 -15
- package/dist/index.mjs +2 -2
- 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,7 +864,7 @@ 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
|
});
|
|
@@ -785,14 +985,20 @@ function useFullscreen(ref) {
|
|
|
785
985
|
}
|
|
786
986
|
//#endregion
|
|
787
987
|
//#region src/Deck.tsx
|
|
788
|
-
|
|
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 }) {
|
|
789
992
|
const containerRef = useRef(null);
|
|
993
|
+
const deckRef = useRef(null);
|
|
790
994
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
791
995
|
const [clickStep, setClickStep] = useState(0);
|
|
792
996
|
const [isOverview, setIsOverview] = useState(false);
|
|
793
997
|
const [isDrawing, setIsDrawing] = useState(false);
|
|
998
|
+
const [isPointer, setIsPointer] = useState(false);
|
|
794
999
|
const [isPrinting, setIsPrinting] = useState(false);
|
|
795
1000
|
const [clickStepsMap, setClickStepsMap] = useState({});
|
|
1001
|
+
const [scale, setScale] = useState(1);
|
|
796
1002
|
const { isFullscreen, toggleFullscreen } = useFullscreen(containerRef);
|
|
797
1003
|
const totalSlides = Children.count(children);
|
|
798
1004
|
const totalClickSteps = clickStepsMap[currentSlide] ?? 0;
|
|
@@ -855,6 +1061,7 @@ function Deck({ children, transition = "none" }) {
|
|
|
855
1061
|
useEffect(() => {
|
|
856
1062
|
function handleKeyDown(e) {
|
|
857
1063
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
|
1064
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
858
1065
|
switch (e.key) {
|
|
859
1066
|
case "ArrowRight":
|
|
860
1067
|
case " ":
|
|
@@ -882,6 +1089,12 @@ function Deck({ children, transition = "none" }) {
|
|
|
882
1089
|
case "d":
|
|
883
1090
|
e.preventDefault();
|
|
884
1091
|
setIsDrawing((v) => !v);
|
|
1092
|
+
setIsPointer(false);
|
|
1093
|
+
break;
|
|
1094
|
+
case "q":
|
|
1095
|
+
e.preventDefault();
|
|
1096
|
+
setIsPointer((v) => !v);
|
|
1097
|
+
setIsDrawing(false);
|
|
885
1098
|
break;
|
|
886
1099
|
}
|
|
887
1100
|
}
|
|
@@ -893,6 +1106,36 @@ function Deck({ children, transition = "none" }) {
|
|
|
893
1106
|
toggleOverview,
|
|
894
1107
|
toggleFullscreen
|
|
895
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
|
+
]);
|
|
896
1139
|
useEffect(() => {
|
|
897
1140
|
function onBeforePrint() {
|
|
898
1141
|
setIsPrinting(true);
|
|
@@ -907,6 +1150,18 @@ function Deck({ children, transition = "none" }) {
|
|
|
907
1150
|
window.removeEventListener("afterprint", onAfterPrint);
|
|
908
1151
|
};
|
|
909
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
|
+
}, []);
|
|
910
1165
|
const contextValue = useMemo(() => ({
|
|
911
1166
|
currentSlide,
|
|
912
1167
|
totalSlides,
|
|
@@ -934,21 +1189,32 @@ function Deck({ children, transition = "none" }) {
|
|
|
934
1189
|
toggleFullscreen,
|
|
935
1190
|
registerClickSteps
|
|
936
1191
|
]);
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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, {
|
|
952
1218
|
totalSlides,
|
|
953
1219
|
goTo,
|
|
954
1220
|
children
|
|
@@ -956,23 +1222,61 @@ function Deck({ children, transition = "none" }) {
|
|
|
956
1222
|
currentSlide,
|
|
957
1223
|
transition,
|
|
958
1224
|
children
|
|
959
|
-
}),
|
|
960
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ClickNavigation, {
|
|
961
|
-
onPrev: prev,
|
|
962
|
-
onNext: next,
|
|
963
|
-
disabled: isDrawing
|
|
964
|
-
}),
|
|
965
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(ProgressBar, {}),
|
|
966
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(SlideNumber, {}),
|
|
967
|
-
!isOverview && !isPrinting && /* @__PURE__ */ jsx(DrawingLayer, {
|
|
968
|
-
active: isDrawing,
|
|
969
|
-
currentSlide
|
|
970
|
-
}),
|
|
971
|
-
!isPrinting && /* @__PURE__ */ jsx(NavigationBar, {
|
|
972
|
-
isDrawing,
|
|
973
|
-
onToggleDrawing: () => setIsDrawing((v) => !v)
|
|
974
1225
|
})
|
|
975
|
-
|
|
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
|
+
})
|
|
976
1280
|
})
|
|
977
1281
|
});
|
|
978
1282
|
}
|
|
@@ -993,7 +1297,7 @@ function OverviewGrid({ children, totalSlides, goTo }) {
|
|
|
993
1297
|
type: "button",
|
|
994
1298
|
onClick: () => goTo(i),
|
|
995
1299
|
style: {
|
|
996
|
-
border: "1px solid var(--slide-accent, #
|
|
1300
|
+
border: "1px solid var(--slide-accent, #16a34a)",
|
|
997
1301
|
borderRadius: "0.5rem",
|
|
998
1302
|
overflow: "hidden",
|
|
999
1303
|
cursor: "pointer",
|
|
@@ -1140,7 +1444,7 @@ function Slide({ children, layout = "default", image, className, style }) {
|
|
|
1140
1444
|
alignItems: "center",
|
|
1141
1445
|
textAlign: "center",
|
|
1142
1446
|
padding: "3rem 4rem",
|
|
1143
|
-
backgroundColor: "var(--slide-accent, #
|
|
1447
|
+
backgroundColor: "var(--slide-accent, #16a34a)",
|
|
1144
1448
|
color: "var(--slide-section-text, #fff)",
|
|
1145
1449
|
...style
|
|
1146
1450
|
},
|
|
@@ -1159,7 +1463,7 @@ function Slide({ children, layout = "default", image, className, style }) {
|
|
|
1159
1463
|
style: {
|
|
1160
1464
|
fontSize: "1.5em",
|
|
1161
1465
|
fontStyle: "italic",
|
|
1162
|
-
borderLeft: "4px solid var(--slide-accent, #
|
|
1466
|
+
borderLeft: "4px solid var(--slide-accent, #16a34a)",
|
|
1163
1467
|
paddingLeft: "1.5rem",
|
|
1164
1468
|
margin: 0
|
|
1165
1469
|
},
|
|
@@ -1284,6 +1588,51 @@ SlotRight.displayName = "SlotRight";
|
|
|
1284
1588
|
SlotRight.__reslideSlot = "right";
|
|
1285
1589
|
//#endregion
|
|
1286
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
|
+
}
|
|
1287
1636
|
/**
|
|
1288
1637
|
* Presenter view that syncs with the main presentation window.
|
|
1289
1638
|
* Shows: current slide, next slide preview, notes, and timer.
|
|
@@ -1374,16 +1723,17 @@ function PresenterView({ children, notes }) {
|
|
|
1374
1723
|
children: [
|
|
1375
1724
|
/* @__PURE__ */ jsx("div", {
|
|
1376
1725
|
style: {
|
|
1377
|
-
border: "2px solid #
|
|
1726
|
+
border: "2px solid #16a34a",
|
|
1378
1727
|
borderRadius: "0.5rem",
|
|
1379
1728
|
overflow: "hidden",
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1729
|
+
display: "flex",
|
|
1730
|
+
alignItems: "center",
|
|
1731
|
+
justifyContent: "center",
|
|
1732
|
+
backgroundColor: "#000"
|
|
1383
1733
|
},
|
|
1384
1734
|
children: /* @__PURE__ */ jsx(SlideIndexContext.Provider, {
|
|
1385
1735
|
value: currentSlide,
|
|
1386
|
-
children: slides[currentSlide]
|
|
1736
|
+
children: /* @__PURE__ */ jsx(ScaledSlide, { children: slides[currentSlide] })
|
|
1387
1737
|
})
|
|
1388
1738
|
}),
|
|
1389
1739
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -1395,25 +1745,29 @@ function PresenterView({ children, notes }) {
|
|
|
1395
1745
|
},
|
|
1396
1746
|
children: [/* @__PURE__ */ jsx("div", {
|
|
1397
1747
|
style: {
|
|
1398
|
-
flex: "0 0 40%",
|
|
1399
1748
|
border: "1px solid #334155",
|
|
1400
1749
|
borderRadius: "0.5rem",
|
|
1401
1750
|
overflow: "hidden",
|
|
1402
1751
|
opacity: .8,
|
|
1403
|
-
|
|
1404
|
-
|
|
1752
|
+
display: "flex",
|
|
1753
|
+
alignItems: "center",
|
|
1754
|
+
justifyContent: "center",
|
|
1755
|
+
backgroundColor: "#000"
|
|
1405
1756
|
},
|
|
1406
|
-
children: currentSlide < totalSlides - 1
|
|
1757
|
+
children: currentSlide < totalSlides - 1 ? /* @__PURE__ */ jsx(SlideIndexContext.Provider, {
|
|
1407
1758
|
value: currentSlide + 1,
|
|
1408
|
-
children: /* @__PURE__ */ jsx(
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
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"
|
|
1417
1771
|
})
|
|
1418
1772
|
}), /* @__PURE__ */ jsxs("div", {
|
|
1419
1773
|
style: {
|
|
@@ -1626,7 +1980,7 @@ function extractHeading(node) {
|
|
|
1626
1980
|
if (!isValidElement(node)) return null;
|
|
1627
1981
|
const el = node;
|
|
1628
1982
|
const type = el.type;
|
|
1629
|
-
if (type === "h1" || type === "h2") return extractText(el.props.children);
|
|
1983
|
+
if (type === "h1" || type === "h2") return extractText$1(el.props.children);
|
|
1630
1984
|
const children = el.props.children;
|
|
1631
1985
|
if (children == null) return null;
|
|
1632
1986
|
let result = null;
|
|
@@ -1638,12 +1992,12 @@ function extractHeading(node) {
|
|
|
1638
1992
|
return result;
|
|
1639
1993
|
}
|
|
1640
1994
|
/** Extract plain text from a React node tree */
|
|
1641
|
-
function extractText(node) {
|
|
1995
|
+
function extractText$1(node) {
|
|
1642
1996
|
if (node == null) return "";
|
|
1643
1997
|
if (typeof node === "string") return node;
|
|
1644
1998
|
if (typeof node === "number") return String(node);
|
|
1645
|
-
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
1646
|
-
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);
|
|
1647
2001
|
return "";
|
|
1648
2002
|
}
|
|
1649
2003
|
/**
|
|
@@ -1693,7 +2047,7 @@ function Toc({ children, className, style }) {
|
|
|
1693
2047
|
fontSize: "0.875rem",
|
|
1694
2048
|
lineHeight: 1.4,
|
|
1695
2049
|
fontFamily: "inherit",
|
|
1696
|
-
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))",
|
|
1697
2051
|
backgroundColor: isActive ? "var(--toc-active-bg, rgba(59, 130, 246, 0.1))" : "transparent",
|
|
1698
2052
|
fontWeight: isActive ? 600 : 400,
|
|
1699
2053
|
transition: "background-color 0.15s, color 0.15s"
|
|
@@ -1721,6 +2075,25 @@ function Toc({ children, className, style }) {
|
|
|
1721
2075
|
//#endregion
|
|
1722
2076
|
//#region src/Mermaid.tsx
|
|
1723
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
|
+
/**
|
|
1724
2097
|
* Renders a Mermaid diagram.
|
|
1725
2098
|
*
|
|
1726
2099
|
* Usage in MDX:
|
|
@@ -1739,7 +2112,9 @@ function Mermaid({ children }) {
|
|
|
1739
2112
|
const [svg, setSvg] = useState("");
|
|
1740
2113
|
const [error, setError] = useState("");
|
|
1741
2114
|
const id = useId().replace(/:/g, "_");
|
|
2115
|
+
const code = extractText(children).trim();
|
|
1742
2116
|
useEffect(() => {
|
|
2117
|
+
if (!code) return;
|
|
1743
2118
|
let cancelled = false;
|
|
1744
2119
|
async function render() {
|
|
1745
2120
|
try {
|
|
@@ -1749,8 +2124,6 @@ function Mermaid({ children }) {
|
|
|
1749
2124
|
theme: "default",
|
|
1750
2125
|
securityLevel: "loose"
|
|
1751
2126
|
});
|
|
1752
|
-
const code = typeof children === "string" ? children.trim() : "";
|
|
1753
|
-
if (!code) return;
|
|
1754
2127
|
const { svg: rendered } = await mermaid.default.render(`mermaid-${id}`, code);
|
|
1755
2128
|
if (!cancelled) {
|
|
1756
2129
|
setSvg(rendered);
|
|
@@ -1764,7 +2137,7 @@ function Mermaid({ children }) {
|
|
|
1764
2137
|
return () => {
|
|
1765
2138
|
cancelled = true;
|
|
1766
2139
|
};
|
|
1767
|
-
}, [
|
|
2140
|
+
}, [code, id]);
|
|
1768
2141
|
if (error) return /* @__PURE__ */ jsx("div", {
|
|
1769
2142
|
className: "reslide-mermaid reslide-mermaid--error",
|
|
1770
2143
|
children: /* @__PURE__ */ jsx("pre", { children: error })
|
|
@@ -1776,4 +2149,4 @@ function Mermaid({ children }) {
|
|
|
1776
2149
|
});
|
|
1777
2150
|
}
|
|
1778
2151
|
//#endregion
|
|
1779
|
-
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,6 +87,26 @@ 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
|
|
@@ -182,4 +191,4 @@ declare function useDeck(): DeckContextValue;
|
|
|
182
191
|
declare const SlideIndexContext: react.Context<number | null>;
|
|
183
192
|
declare function useSlideIndex(): number;
|
|
184
193
|
//#endregion
|
|
185
|
-
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, SlideNumber, 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";
|
|
@@ -180,4 +180,4 @@ function ReslideEmbed({ code, transition, components: userComponents, className,
|
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
//#endregion
|
|
183
|
-
export { Click, ClickNavigation, ClickSteps, CodeEditor, Deck, DeckContext, Draggable, DrawingLayer, GlobalLayer, Mark, Mermaid, NavigationBar, Notes, PresenterView, PrintView, ProgressBar, ReslideEmbed, Slide, SlideIndexContext, SlideNumber, 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
|
}
|