@thanh01.pmt/presentation-kit 0.2.9 → 0.2.11
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/dist/ai/index.cjs +190 -7
- package/dist/ai/index.cjs.map +1 -1
- package/dist/ai/index.d.cts +123 -3
- package/dist/ai/index.d.ts +123 -3
- package/dist/ai/index.js +177 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/chunk-3VGV5KYZ.js +3446 -0
- package/dist/chunk-3VGV5KYZ.js.map +1 -0
- package/dist/chunk-53GLUCYC.cjs +3464 -0
- package/dist/chunk-53GLUCYC.cjs.map +1 -0
- package/dist/{click-css-BP9oqy_1.d.cts → click-css-Ch_jyWaw.d.cts} +1 -1
- package/dist/{click-css-BbhqdtGr.d.ts → click-css-CktIYP3H.d.ts} +1 -1
- package/dist/index.cjs +30 -10
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +2 -2
- package/dist/{presets-B_TY4i2a.d.ts → presets-BUzebXzj.d.ts} +1 -1
- package/dist/{presets-BIw_4ZXR.d.cts → presets-Bkh6NzWf.d.cts} +1 -1
- package/dist/react/index.cjs +365 -107
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +25 -5
- package/dist/react/index.d.ts +25 -5
- package/dist/react/index.js +312 -59
- package/dist/react/index.js.map +1 -1
- package/dist/{types-C1121XLt.d.cts → types-8AVYoEUn.d.cts} +67 -1
- package/dist/{types-C1121XLt.d.ts → types-8AVYoEUn.d.ts} +67 -1
- package/package.json +2 -2
- package/dist/chunk-TV6NFIIV.cjs +0 -878
- package/dist/chunk-TV6NFIIV.cjs.map +0 -1
- package/dist/chunk-ZPASRL75.js +0 -865
- package/dist/chunk-ZPASRL75.js.map +0 -1
package/dist/react/index.cjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkTGI6YSZP_cjs = require('../chunk-TGI6YSZP.cjs');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var chunk53GLUCYC_cjs = require('../chunk-53GLUCYC.cjs');
|
|
5
|
+
var React2 = require('react');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
11
|
+
|
|
8
12
|
function useSlideState(options) {
|
|
9
13
|
const {
|
|
10
14
|
markdown,
|
|
@@ -13,12 +17,12 @@ function useSlideState(options) {
|
|
|
13
17
|
containerId,
|
|
14
18
|
enableClicks = false
|
|
15
19
|
} = options;
|
|
16
|
-
const [currentIndex, setCurrentIndex] =
|
|
17
|
-
const [mode, setMode] =
|
|
18
|
-
const [theme, setTheme] =
|
|
19
|
-
const [currentClick, setCurrentClick] =
|
|
20
|
-
const renderCountRef =
|
|
21
|
-
const result =
|
|
20
|
+
const [currentIndex, setCurrentIndex] = React2.useState(0);
|
|
21
|
+
const [mode, setMode] = React2.useState(initialMode);
|
|
22
|
+
const [theme, setTheme] = React2.useState(initialTheme);
|
|
23
|
+
const [currentClick, setCurrentClick] = React2.useState(0);
|
|
24
|
+
const renderCountRef = React2.useRef(0);
|
|
25
|
+
const result = React2.useMemo(() => {
|
|
22
26
|
if (!markdown || !markdown.trim()) return null;
|
|
23
27
|
renderCountRef.current++;
|
|
24
28
|
return chunkTGI6YSZP_cjs.renderSlide(markdown, {
|
|
@@ -31,49 +35,49 @@ function useSlideState(options) {
|
|
|
31
35
|
const slideCount = result?.slideCount ?? 1;
|
|
32
36
|
const maxClicks = result?.maxClicks ?? 0;
|
|
33
37
|
const isReady = result !== null;
|
|
34
|
-
|
|
38
|
+
React2.useEffect(() => {
|
|
35
39
|
if (result && currentIndex >= result.slideCount) {
|
|
36
40
|
setCurrentIndex(Math.max(0, result.slideCount - 1));
|
|
37
41
|
}
|
|
38
42
|
}, [result?.slideCount, currentIndex]);
|
|
39
|
-
|
|
43
|
+
React2.useEffect(() => {
|
|
40
44
|
setCurrentClick(0);
|
|
41
45
|
}, [currentIndex]);
|
|
42
|
-
|
|
46
|
+
React2.useCallback(
|
|
43
47
|
(index) => {
|
|
44
48
|
setCurrentIndex(Math.max(0, Math.min(index, slideCount - 1)));
|
|
45
49
|
},
|
|
46
50
|
[slideCount]
|
|
47
51
|
);
|
|
48
|
-
|
|
52
|
+
React2.useCallback(() => {
|
|
49
53
|
setCurrentIndex((prev) => Math.min(prev + 1, slideCount - 1));
|
|
50
54
|
}, [slideCount]);
|
|
51
|
-
|
|
55
|
+
React2.useCallback(() => {
|
|
52
56
|
setCurrentIndex((prev) => Math.max(prev - 1, 0));
|
|
53
57
|
}, []);
|
|
54
|
-
const nextClick =
|
|
58
|
+
const nextClick = React2.useCallback(() => {
|
|
55
59
|
setCurrentClick((prev) => Math.min(prev + 1, maxClicks));
|
|
56
60
|
}, [maxClicks]);
|
|
57
|
-
const prevClick =
|
|
61
|
+
const prevClick = React2.useCallback(() => {
|
|
58
62
|
setCurrentClick((prev) => Math.max(prev - 1, 0));
|
|
59
63
|
}, []);
|
|
60
|
-
const resetClicks =
|
|
64
|
+
const resetClicks = React2.useCallback(() => {
|
|
61
65
|
setCurrentClick(0);
|
|
62
66
|
}, []);
|
|
63
|
-
const setClickIndex =
|
|
67
|
+
const setClickIndex = React2.useCallback(
|
|
64
68
|
(index) => {
|
|
65
69
|
setCurrentClick(Math.max(0, Math.min(index, maxClicks)));
|
|
66
70
|
},
|
|
67
71
|
[maxClicks]
|
|
68
72
|
);
|
|
69
|
-
const goToSlideWithReset =
|
|
73
|
+
const goToSlideWithReset = React2.useCallback(
|
|
70
74
|
(index) => {
|
|
71
75
|
setCurrentClick(0);
|
|
72
76
|
setCurrentIndex(Math.max(0, Math.min(index, slideCount - 1)));
|
|
73
77
|
},
|
|
74
78
|
[slideCount]
|
|
75
79
|
);
|
|
76
|
-
const nextWithClick =
|
|
80
|
+
const nextWithClick = React2.useCallback(() => {
|
|
77
81
|
if (currentClick < maxClicks) {
|
|
78
82
|
setCurrentClick((prev) => prev + 1);
|
|
79
83
|
} else {
|
|
@@ -81,14 +85,14 @@ function useSlideState(options) {
|
|
|
81
85
|
setCurrentIndex((prev) => Math.min(prev + 1, slideCount - 1));
|
|
82
86
|
}
|
|
83
87
|
}, [currentClick, maxClicks, slideCount]);
|
|
84
|
-
const prevWithClick =
|
|
88
|
+
const prevWithClick = React2.useCallback(() => {
|
|
85
89
|
if (currentClick > 0) {
|
|
86
90
|
setCurrentClick((prev) => prev - 1);
|
|
87
91
|
} else {
|
|
88
92
|
setCurrentIndex((prev) => Math.max(prev - 1, 0));
|
|
89
93
|
}
|
|
90
94
|
}, [currentClick]);
|
|
91
|
-
const rerender =
|
|
95
|
+
const rerender = React2.useCallback(() => {
|
|
92
96
|
setCurrentIndex((prev) => prev);
|
|
93
97
|
}, []);
|
|
94
98
|
return {
|
|
@@ -228,8 +232,8 @@ function NavigationBar({
|
|
|
228
232
|
showCopy = true,
|
|
229
233
|
markdown
|
|
230
234
|
}) {
|
|
231
|
-
const [copied, setCopied] =
|
|
232
|
-
const handleCopy =
|
|
235
|
+
const [copied, setCopied] = React2.useState(false);
|
|
236
|
+
const handleCopy = React2.useCallback(async () => {
|
|
233
237
|
if (!markdown) return;
|
|
234
238
|
try {
|
|
235
239
|
await navigator.clipboard.writeText(markdown);
|
|
@@ -411,8 +415,45 @@ function DeckView({
|
|
|
411
415
|
themeCss,
|
|
412
416
|
layoutCss,
|
|
413
417
|
slideCount,
|
|
414
|
-
className
|
|
418
|
+
className,
|
|
419
|
+
notes,
|
|
420
|
+
showNotes = false
|
|
415
421
|
}) {
|
|
422
|
+
const hostRef = React2__default.default.useRef(null);
|
|
423
|
+
React2__default.default.useEffect(() => {
|
|
424
|
+
if (!hostRef.current) return;
|
|
425
|
+
hostRef.current.querySelectorAll(".deck-slide-note-item").forEach((el) => el.remove());
|
|
426
|
+
if (!showNotes || !notes || notes.length === 0) return;
|
|
427
|
+
const svgs = hostRef.current.querySelectorAll("svg[data-marpit-svg], svg");
|
|
428
|
+
svgs.forEach((svg, idx) => {
|
|
429
|
+
const slideNotes = notes[idx];
|
|
430
|
+
if (slideNotes && slideNotes.length > 0) {
|
|
431
|
+
const noteBox = document.createElement("div");
|
|
432
|
+
noteBox.className = "deck-slide-note-item";
|
|
433
|
+
noteBox.style.cssText = `
|
|
434
|
+
width: 100%;
|
|
435
|
+
max-width: 960px;
|
|
436
|
+
margin: -16px auto 32px auto;
|
|
437
|
+
background: rgba(24, 24, 32, 0.85);
|
|
438
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
439
|
+
border-left: 4px solid #0284c7;
|
|
440
|
+
border-radius: 8px;
|
|
441
|
+
padding: 14px 18px;
|
|
442
|
+
color: #e4e4e7;
|
|
443
|
+
font-size: 14px;
|
|
444
|
+
line-height: 1.6;
|
|
445
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
|
446
|
+
`;
|
|
447
|
+
noteBox.innerHTML = `
|
|
448
|
+
<div style="font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #38bdf8; margin-bottom: 4px; display: flex; align-items: center; gap: 6px;">
|
|
449
|
+
<span>\u{1F4DD}</span><span>Presenter Notes (Slide ${idx + 1})</span>
|
|
450
|
+
</div>
|
|
451
|
+
<div style="white-space: pre-wrap; font-size: 13px; line-height: 1.6; color: inherit;">${slideNotes.join("\n")}</div>
|
|
452
|
+
`;
|
|
453
|
+
svg.after(noteBox);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}, [html, notes, showNotes]);
|
|
416
457
|
const deckCss = `
|
|
417
458
|
.slide-deck-host {
|
|
418
459
|
display: flex !important;
|
|
@@ -452,6 +493,46 @@ function DeckView({
|
|
|
452
493
|
transform: translateY(-2px);
|
|
453
494
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important;
|
|
454
495
|
}
|
|
496
|
+
|
|
497
|
+
@media print {
|
|
498
|
+
@page {
|
|
499
|
+
size: landscape;
|
|
500
|
+
margin: 0;
|
|
501
|
+
}
|
|
502
|
+
body {
|
|
503
|
+
background: #ffffff !important;
|
|
504
|
+
color: #000000 !important;
|
|
505
|
+
}
|
|
506
|
+
.slide-deck-host {
|
|
507
|
+
padding: 0 !important;
|
|
508
|
+
}
|
|
509
|
+
.slide-deck-host #marp-container svg[data-marpit-svg],
|
|
510
|
+
.slide-deck-host #marp-container > svg,
|
|
511
|
+
.slide-deck-host > div > svg {
|
|
512
|
+
width: 100vw !important;
|
|
513
|
+
max-width: none !important;
|
|
514
|
+
height: 56.25vw !important;
|
|
515
|
+
page-break-inside: avoid !important;
|
|
516
|
+
break-inside: avoid !important;
|
|
517
|
+
page-break-after: always !important;
|
|
518
|
+
break-after: page !important;
|
|
519
|
+
margin: 0 auto !important;
|
|
520
|
+
box-shadow: none !important;
|
|
521
|
+
border-radius: 0 !important;
|
|
522
|
+
}
|
|
523
|
+
.deck-slide-note-item {
|
|
524
|
+
page-break-after: always !important;
|
|
525
|
+
break-after: page !important;
|
|
526
|
+
background: #f8fafc !important;
|
|
527
|
+
border: 1px solid #cbd5e1 !important;
|
|
528
|
+
border-left: 4px solid #2563eb !important;
|
|
529
|
+
color: #1e293b !important;
|
|
530
|
+
padding: 12px 18px !important;
|
|
531
|
+
margin: 12px auto 32px auto !important;
|
|
532
|
+
width: 90vw !important;
|
|
533
|
+
font-size: 11pt !important;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
455
536
|
`;
|
|
456
537
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { width: "100%" }, children: [
|
|
457
538
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
|
|
@@ -461,6 +542,7 @@ function DeckView({
|
|
|
461
542
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
462
543
|
"div",
|
|
463
544
|
{
|
|
545
|
+
ref: hostRef,
|
|
464
546
|
className: "slide-deck-host",
|
|
465
547
|
dangerouslySetInnerHTML: { __html: html }
|
|
466
548
|
}
|
|
@@ -594,27 +676,29 @@ function SlideDesignOverlay({
|
|
|
594
676
|
onSelectBoxId
|
|
595
677
|
}) {
|
|
596
678
|
if (!isActive) return null;
|
|
597
|
-
const [boxes, setBoxes] =
|
|
598
|
-
const [selectedId, setSelectedIdState] =
|
|
599
|
-
const [editingId, setEditingId] =
|
|
600
|
-
const containerRef =
|
|
601
|
-
const setSelectedId =
|
|
679
|
+
const [boxes, setBoxes] = React2.useState(layout?.boxes ?? []);
|
|
680
|
+
const [selectedId, setSelectedIdState] = React2.useState(selectedBoxId ?? null);
|
|
681
|
+
const [editingId, setEditingId] = React2.useState(null);
|
|
682
|
+
const containerRef = React2.useRef(null);
|
|
683
|
+
const setSelectedId = React2.useCallback((id) => {
|
|
602
684
|
setSelectedIdState(id);
|
|
603
685
|
if (onSelectBoxId) onSelectBoxId(id);
|
|
604
686
|
}, [onSelectBoxId]);
|
|
605
|
-
|
|
687
|
+
React2.useEffect(() => {
|
|
606
688
|
if (selectedBoxId !== void 0) {
|
|
607
689
|
setSelectedIdState(selectedBoxId);
|
|
608
690
|
}
|
|
609
691
|
}, [selectedBoxId]);
|
|
610
|
-
|
|
692
|
+
React2.useEffect(() => {
|
|
611
693
|
setBoxes(layout?.boxes ?? []);
|
|
612
694
|
}, [layout, slideIndex]);
|
|
613
695
|
const selectedBox = boxes.find((b) => b.id === selectedId) ?? null;
|
|
614
|
-
const
|
|
696
|
+
const hasMovedRef = React2.useRef(false);
|
|
697
|
+
const [dragging, setDragging] = React2.useState(null);
|
|
615
698
|
const handleMouseDown = (e, box, type) => {
|
|
616
699
|
e.preventDefault();
|
|
617
700
|
e.stopPropagation();
|
|
701
|
+
hasMovedRef.current = false;
|
|
618
702
|
setSelectedId(box.id);
|
|
619
703
|
setDragging({
|
|
620
704
|
type,
|
|
@@ -624,13 +708,16 @@ function SlideDesignOverlay({
|
|
|
624
708
|
startPos: [...box.pos]
|
|
625
709
|
});
|
|
626
710
|
};
|
|
627
|
-
const handleMouseMove =
|
|
711
|
+
const handleMouseMove = React2.useCallback((e) => {
|
|
628
712
|
if (!dragging || !containerRef.current) return;
|
|
629
713
|
const rect = containerRef.current.getBoundingClientRect();
|
|
630
714
|
const scaleX = 1280 / rect.width;
|
|
631
715
|
const scaleY = 720 / rect.height;
|
|
632
716
|
const dx = (e.clientX - dragging.startX) * scaleX;
|
|
633
717
|
const dy = (e.clientY - dragging.startY) * scaleY;
|
|
718
|
+
if (Math.abs(dx) > 1 || Math.abs(dy) > 1) {
|
|
719
|
+
hasMovedRef.current = true;
|
|
720
|
+
}
|
|
634
721
|
const [origX, origY, origW, origH] = dragging.startPos;
|
|
635
722
|
setBoxes((prev) => {
|
|
636
723
|
const updated = prev.map((b) => {
|
|
@@ -682,13 +769,14 @@ function SlideDesignOverlay({
|
|
|
682
769
|
return updated;
|
|
683
770
|
});
|
|
684
771
|
}, [dragging, onLayoutChange]);
|
|
685
|
-
const handleMouseUp =
|
|
686
|
-
if (dragging && onSave) {
|
|
772
|
+
const handleMouseUp = React2.useCallback(() => {
|
|
773
|
+
if (dragging && hasMovedRef.current && onSave) {
|
|
687
774
|
onSave({ boxes });
|
|
688
775
|
}
|
|
776
|
+
hasMovedRef.current = false;
|
|
689
777
|
setDragging(null);
|
|
690
778
|
}, [dragging, boxes, onSave]);
|
|
691
|
-
|
|
779
|
+
React2.useEffect(() => {
|
|
692
780
|
if (dragging) {
|
|
693
781
|
window.addEventListener("mousemove", handleMouseMove);
|
|
694
782
|
window.addEventListener("mouseup", handleMouseUp);
|
|
@@ -698,7 +786,7 @@ function SlideDesignOverlay({
|
|
|
698
786
|
};
|
|
699
787
|
}
|
|
700
788
|
}, [dragging, handleMouseMove, handleMouseUp]);
|
|
701
|
-
const removeSelectedBox =
|
|
789
|
+
const removeSelectedBox = React2.useCallback(() => {
|
|
702
790
|
if (!selectedId) return;
|
|
703
791
|
const boxToRemove = boxes.find((b) => b.id === selectedId);
|
|
704
792
|
setBoxes((prev) => {
|
|
@@ -712,7 +800,7 @@ function SlideDesignOverlay({
|
|
|
712
800
|
onDeleteBox(boxToRemove);
|
|
713
801
|
}
|
|
714
802
|
}, [selectedId, boxes, onLayoutChange, onSave, onDeleteBox, setSelectedId]);
|
|
715
|
-
|
|
803
|
+
React2.useEffect(() => {
|
|
716
804
|
const handleKeyDown = (e) => {
|
|
717
805
|
if (editingId) return;
|
|
718
806
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
|
@@ -823,7 +911,7 @@ function SlideDesignOverlay({
|
|
|
823
911
|
"select",
|
|
824
912
|
{
|
|
825
913
|
onChange: (e) => {
|
|
826
|
-
const preset =
|
|
914
|
+
const preset = chunk53GLUCYC_cjs.SLIDE_LAYOUT_PRESETS.find((p) => p.id === e.target.value);
|
|
827
915
|
if (preset) {
|
|
828
916
|
const clonedBoxes = JSON.parse(JSON.stringify(preset.layout.boxes));
|
|
829
917
|
setBoxes(clonedBoxes);
|
|
@@ -843,7 +931,7 @@ function SlideDesignOverlay({
|
|
|
843
931
|
title: "\xC1p d\u1EE5ng m\u1EABu b\u1ED1 c\u1EE5c c\xF3 s\u1EB5n",
|
|
844
932
|
children: [
|
|
845
933
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, style: { backgroundColor: isLight ? "#ffffff" : "#1e1f29", color: subtextColor }, children: "M\u1EABu b\u1ED1 c\u1EE5c (Presets)..." }),
|
|
846
|
-
|
|
934
|
+
chunk53GLUCYC_cjs.SLIDE_LAYOUT_PRESETS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, style: { backgroundColor: isLight ? "#ffffff" : "#1e1f29", color: isLight ? "#0f172a" : "#f8fafc" }, children: p.name }, p.id))
|
|
847
935
|
]
|
|
848
936
|
}
|
|
849
937
|
),
|
|
@@ -914,8 +1002,9 @@ function SlideDesignOverlay({
|
|
|
914
1002
|
style: {
|
|
915
1003
|
width: "100%",
|
|
916
1004
|
height: "100%",
|
|
917
|
-
|
|
918
|
-
border: "
|
|
1005
|
+
backgroundColor: isLight ? "#ffffff" : isNavy ? "#0b132b" : "#181920",
|
|
1006
|
+
border: isLight ? "1px solid #0284c7" : "1px solid #38bdf8",
|
|
1007
|
+
borderRadius: "4px",
|
|
919
1008
|
outline: "none",
|
|
920
1009
|
resize: "none",
|
|
921
1010
|
fontSize: box.fontSize ?? (box.target === "h1" ? "2.2rem" : "1.1rem"),
|
|
@@ -924,8 +1013,9 @@ function SlideDesignOverlay({
|
|
|
924
1013
|
fontFamily: "Inter, sans-serif",
|
|
925
1014
|
fontWeight: box.target === "h1" ? 700 : 400,
|
|
926
1015
|
lineHeight: 1.25,
|
|
927
|
-
padding:
|
|
928
|
-
margin: 0
|
|
1016
|
+
padding: "4px 6px",
|
|
1017
|
+
margin: 0,
|
|
1018
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.25)"
|
|
929
1019
|
}
|
|
930
1020
|
}
|
|
931
1021
|
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -939,20 +1029,9 @@ function SlideDesignOverlay({
|
|
|
939
1029
|
style: {
|
|
940
1030
|
width: "100%",
|
|
941
1031
|
height: "100%",
|
|
942
|
-
|
|
943
|
-
color: box.color ?? (isLight ? "#0f172a" : "#f8fafc"),
|
|
944
|
-
textAlign: box.textAlign ?? "left",
|
|
945
|
-
fontFamily: "Inter, sans-serif",
|
|
946
|
-
fontWeight: box.target === "h1" ? 700 : 400,
|
|
947
|
-
lineHeight: 1.25,
|
|
948
|
-
wordBreak: "break-word",
|
|
949
|
-
userSelect: "none",
|
|
950
|
-
cursor: "pointer",
|
|
951
|
-
display: "flex",
|
|
952
|
-
alignItems: "center"
|
|
1032
|
+
cursor: "pointer"
|
|
953
1033
|
},
|
|
954
|
-
title: "Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa ch\u1EEF"
|
|
955
|
-
children: box.content ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: box.content }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.45, fontStyle: "italic", fontSize: "12px" }, children: box.target === "h1" ? "Ti\xEAu \u0111\u1EC1 (Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa)..." : "N\u1ED9i dung (Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa)..." })
|
|
1034
|
+
title: "Click \u0111\xFAp \u0111\u1EC3 s\u1EEDa ch\u1EEF"
|
|
956
1035
|
}
|
|
957
1036
|
)
|
|
958
1037
|
}
|
|
@@ -1139,10 +1218,10 @@ function SlideViewer({
|
|
|
1139
1218
|
onRenderComplete,
|
|
1140
1219
|
onSaveMarkdown
|
|
1141
1220
|
}) {
|
|
1142
|
-
const [isFullscreen, setIsFullscreen] =
|
|
1143
|
-
const [showNotesDrawer, setShowNotesDrawer] =
|
|
1144
|
-
const [isDesignMode, setIsDesignMode] =
|
|
1145
|
-
const [liveLayout, setLiveLayout] =
|
|
1221
|
+
const [isFullscreen, setIsFullscreen] = React2.useState(false);
|
|
1222
|
+
const [showNotesDrawer, setShowNotesDrawer] = React2.useState(false);
|
|
1223
|
+
const [isDesignMode, setIsDesignMode] = React2.useState(false);
|
|
1224
|
+
const [liveLayout, setLiveLayout] = React2.useState(null);
|
|
1146
1225
|
const state = useSlideState({
|
|
1147
1226
|
markdown,
|
|
1148
1227
|
initialTheme,
|
|
@@ -1166,19 +1245,19 @@ function SlideViewer({
|
|
|
1166
1245
|
prevClick,
|
|
1167
1246
|
resetClicks
|
|
1168
1247
|
} = state;
|
|
1169
|
-
|
|
1248
|
+
React2.useEffect(() => {
|
|
1170
1249
|
onThemeChange?.(theme);
|
|
1171
1250
|
}, [theme, onThemeChange]);
|
|
1172
|
-
|
|
1251
|
+
React2.useEffect(() => {
|
|
1173
1252
|
onSlideChange?.(currentIndex);
|
|
1174
1253
|
}, [currentIndex, onSlideChange]);
|
|
1175
|
-
|
|
1254
|
+
React2.useEffect(() => {
|
|
1176
1255
|
onModeChange?.(mode);
|
|
1177
1256
|
}, [mode, onModeChange]);
|
|
1178
|
-
|
|
1257
|
+
React2.useEffect(() => {
|
|
1179
1258
|
if (result) onRenderComplete?.(result);
|
|
1180
1259
|
}, [result, onRenderComplete]);
|
|
1181
|
-
|
|
1260
|
+
React2.useEffect(() => {
|
|
1182
1261
|
if (!enableKeyboard) return;
|
|
1183
1262
|
const handleKey = (e) => {
|
|
1184
1263
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
|
@@ -1223,12 +1302,19 @@ function SlideViewer({
|
|
|
1223
1302
|
setIsFullscreen((prev) => !prev);
|
|
1224
1303
|
}
|
|
1225
1304
|
break;
|
|
1305
|
+
case "p":
|
|
1306
|
+
case "P":
|
|
1307
|
+
if (showNotes) {
|
|
1308
|
+
e.preventDefault();
|
|
1309
|
+
setShowNotesDrawer((prev) => !prev);
|
|
1310
|
+
}
|
|
1311
|
+
break;
|
|
1226
1312
|
}
|
|
1227
1313
|
};
|
|
1228
1314
|
window.addEventListener("keydown", handleKey);
|
|
1229
1315
|
return () => window.removeEventListener("keydown", handleKey);
|
|
1230
1316
|
}, [enableKeyboard, mode, fullscreen, isFullscreen, enableClicks, currentClick, maxClicks, nextClick, prevClick, nextSlide, prevSlide, goToSlide, slideCount]);
|
|
1231
|
-
|
|
1317
|
+
React2.useEffect(() => {
|
|
1232
1318
|
if (!enableClicks || !result || mode !== "presentation") return;
|
|
1233
1319
|
const timer = setTimeout(() => {
|
|
1234
1320
|
const container = document.querySelector(".slide-presentation-host > div");
|
|
@@ -1241,7 +1327,7 @@ function SlideViewer({
|
|
|
1241
1327
|
}, 50);
|
|
1242
1328
|
return () => clearTimeout(timer);
|
|
1243
1329
|
}, [enableClicks, currentClick, result, currentIndex, mode]);
|
|
1244
|
-
|
|
1330
|
+
React2.useEffect(() => {
|
|
1245
1331
|
if (!result || result.mermaidBlocks.length === 0) return;
|
|
1246
1332
|
const timer = setTimeout(() => {
|
|
1247
1333
|
const containers = document.querySelectorAll(".slide-presentation-host > div, .slide-deck-host > div");
|
|
@@ -1255,13 +1341,13 @@ function SlideViewer({
|
|
|
1255
1341
|
}, 100);
|
|
1256
1342
|
return () => clearTimeout(timer);
|
|
1257
1343
|
}, [result, theme, currentIndex, mode]);
|
|
1258
|
-
const handleThemeChange =
|
|
1344
|
+
const handleThemeChange = React2.useCallback(
|
|
1259
1345
|
(themeId) => {
|
|
1260
1346
|
setTheme(themeId);
|
|
1261
1347
|
},
|
|
1262
1348
|
[setTheme]
|
|
1263
1349
|
);
|
|
1264
|
-
const toggleFullscreen =
|
|
1350
|
+
const toggleFullscreen = React2.useCallback(() => {
|
|
1265
1351
|
setIsFullscreen((prev) => !prev);
|
|
1266
1352
|
}, []);
|
|
1267
1353
|
if (!result) {
|
|
@@ -1444,45 +1530,62 @@ function SlideViewer({
|
|
|
1444
1530
|
transition: "width 0.2s ease"
|
|
1445
1531
|
} }) })
|
|
1446
1532
|
] }),
|
|
1447
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1448
|
-
|
|
1449
|
-
PresentationView,
|
|
1450
|
-
{
|
|
1451
|
-
html: result.html,
|
|
1452
|
-
css: result.css,
|
|
1453
|
-
themeCss: result.themeCss,
|
|
1454
|
-
layoutCss: result.layoutCss,
|
|
1455
|
-
currentIndex,
|
|
1456
|
-
slideCount
|
|
1457
|
-
}
|
|
1458
|
-
),
|
|
1459
|
-
isDesignMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1460
|
-
SlideDesignOverlay,
|
|
1461
|
-
{
|
|
1462
|
-
slideIndex: currentIndex,
|
|
1463
|
-
layout: liveLayout ?? result.layouts?.[currentIndex] ?? null,
|
|
1464
|
-
onLayoutChange: setLiveLayout,
|
|
1465
|
-
onSave: (layout) => {
|
|
1466
|
-
setLiveLayout(layout);
|
|
1467
|
-
if (onSaveMarkdown) {
|
|
1468
|
-
const updated = chunkTV6NFIIV_cjs.updateSlideLayoutInMarkdown(markdown, currentIndex, layout);
|
|
1469
|
-
onSaveMarkdown(updated);
|
|
1470
|
-
}
|
|
1471
|
-
},
|
|
1472
|
-
onClose: () => setIsDesignMode(false),
|
|
1473
|
-
isActive: isDesignMode
|
|
1474
|
-
}
|
|
1475
|
-
)
|
|
1476
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1477
|
-
DeckView,
|
|
1533
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1534
|
+
"div",
|
|
1478
1535
|
{
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1536
|
+
style: { position: "relative", width: "100%" },
|
|
1537
|
+
onClick: (e) => {
|
|
1538
|
+
if (mode !== "presentation" || isDesignMode) return;
|
|
1539
|
+
if (e.target.closest("button, a, input, select, textarea, [contenteditable], .slide-design-overlay")) return;
|
|
1540
|
+
if (enableClicks && currentClick < maxClicks) {
|
|
1541
|
+
nextClick();
|
|
1542
|
+
} else {
|
|
1543
|
+
nextSlide();
|
|
1544
|
+
}
|
|
1545
|
+
},
|
|
1546
|
+
children: mode === "presentation" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1547
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1548
|
+
PresentationView,
|
|
1549
|
+
{
|
|
1550
|
+
html: result.html,
|
|
1551
|
+
css: result.css,
|
|
1552
|
+
themeCss: result.themeCss,
|
|
1553
|
+
layoutCss: result.layoutCss,
|
|
1554
|
+
currentIndex,
|
|
1555
|
+
slideCount
|
|
1556
|
+
}
|
|
1557
|
+
),
|
|
1558
|
+
isDesignMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1559
|
+
SlideDesignOverlay,
|
|
1560
|
+
{
|
|
1561
|
+
slideIndex: currentIndex,
|
|
1562
|
+
layout: liveLayout ?? result.layouts?.[currentIndex] ?? null,
|
|
1563
|
+
onLayoutChange: setLiveLayout,
|
|
1564
|
+
onSave: (layout) => {
|
|
1565
|
+
setLiveLayout(layout);
|
|
1566
|
+
if (onSaveMarkdown) {
|
|
1567
|
+
const updated = chunk53GLUCYC_cjs.updateSlideLayoutInMarkdown(markdown, currentIndex, layout);
|
|
1568
|
+
onSaveMarkdown(updated);
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
onClose: () => setIsDesignMode(false),
|
|
1572
|
+
isActive: isDesignMode
|
|
1573
|
+
}
|
|
1574
|
+
)
|
|
1575
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1576
|
+
DeckView,
|
|
1577
|
+
{
|
|
1578
|
+
html: result.html,
|
|
1579
|
+
css: result.css,
|
|
1580
|
+
themeCss: result.themeCss,
|
|
1581
|
+
layoutCss: result.layoutCss,
|
|
1582
|
+
slideCount,
|
|
1583
|
+
notes: result.notes,
|
|
1584
|
+
showNotes: showNotesDrawer
|
|
1585
|
+
}
|
|
1586
|
+
)
|
|
1484
1587
|
}
|
|
1485
|
-
)
|
|
1588
|
+
),
|
|
1486
1589
|
showNotes && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1487
1590
|
PresenterNotesDrawer,
|
|
1488
1591
|
{
|
|
@@ -1498,6 +1601,160 @@ function SlideViewer({
|
|
|
1498
1601
|
}
|
|
1499
1602
|
);
|
|
1500
1603
|
}
|
|
1604
|
+
var HtmlSlideViewer = ({
|
|
1605
|
+
htmlContent: initialHtml,
|
|
1606
|
+
deckData,
|
|
1607
|
+
title: propTitle,
|
|
1608
|
+
className = "",
|
|
1609
|
+
onSlideChange,
|
|
1610
|
+
showTopBar = true
|
|
1611
|
+
}) => {
|
|
1612
|
+
const iframeRef = React2.useRef(null);
|
|
1613
|
+
const containerRef = React2.useRef(null);
|
|
1614
|
+
const [currentSlide, setCurrentSlide] = React2.useState(0);
|
|
1615
|
+
const [totalSlides, setTotalSlides] = React2.useState(deckData?.slides?.length || 1);
|
|
1616
|
+
const [isFullscreen, setIsFullscreen] = React2.useState(false);
|
|
1617
|
+
const fullHtml = React2.useMemo(() => {
|
|
1618
|
+
if (initialHtml) return initialHtml;
|
|
1619
|
+
if (deckData) {
|
|
1620
|
+
return chunk53GLUCYC_cjs.compileHtmlDeck(deckData).html;
|
|
1621
|
+
}
|
|
1622
|
+
return "";
|
|
1623
|
+
}, [initialHtml, deckData]);
|
|
1624
|
+
const deckTitle = propTitle || deckData?.title || "Presentation";
|
|
1625
|
+
React2.useEffect(() => {
|
|
1626
|
+
const handleMessage = (e) => {
|
|
1627
|
+
if (!e.data || typeof e.data !== "object") return;
|
|
1628
|
+
if (e.data.type === "PRESENTATION_SLIDE_CHANGE") {
|
|
1629
|
+
const cur = Number(e.data.currentIndex) || 0;
|
|
1630
|
+
const tot = Number(e.data.totalSlides) || 1;
|
|
1631
|
+
setCurrentSlide(cur);
|
|
1632
|
+
setTotalSlides(tot);
|
|
1633
|
+
if (onSlideChange) {
|
|
1634
|
+
onSlideChange(cur, tot);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
window.addEventListener("message", handleMessage);
|
|
1639
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
1640
|
+
}, [onSlideChange]);
|
|
1641
|
+
React2.useEffect(() => {
|
|
1642
|
+
const onFsChange = () => {
|
|
1643
|
+
setIsFullscreen(Boolean(document.fullscreenElement));
|
|
1644
|
+
};
|
|
1645
|
+
document.addEventListener("fullscreenchange", onFsChange);
|
|
1646
|
+
return () => document.removeEventListener("fullscreenchange", onFsChange);
|
|
1647
|
+
}, []);
|
|
1648
|
+
const sendIframeMessage = React2.useCallback((msg) => {
|
|
1649
|
+
if (iframeRef.current?.contentWindow) {
|
|
1650
|
+
iframeRef.current.contentWindow.postMessage(msg, "*");
|
|
1651
|
+
}
|
|
1652
|
+
}, []);
|
|
1653
|
+
const handleNext = () => sendIframeMessage({ type: "NEXT_SLIDE" });
|
|
1654
|
+
const handlePrev = () => sendIframeMessage({ type: "PREV_SLIDE" });
|
|
1655
|
+
const handleToggleFullscreen = () => {
|
|
1656
|
+
if (!containerRef.current) return;
|
|
1657
|
+
if (!document.fullscreenElement) {
|
|
1658
|
+
containerRef.current.requestFullscreen().catch(() => {
|
|
1659
|
+
});
|
|
1660
|
+
} else {
|
|
1661
|
+
document.exitFullscreen().catch(() => {
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
const handleDownloadHtml = () => {
|
|
1666
|
+
if (!fullHtml) return;
|
|
1667
|
+
const blob = new Blob([fullHtml], { type: "text/html;charset=utf-8" });
|
|
1668
|
+
const url = URL.createObjectURL(blob);
|
|
1669
|
+
const a = document.createElement("a");
|
|
1670
|
+
a.href = url;
|
|
1671
|
+
a.download = `${deckTitle.toLowerCase().replace(/[^a-z0-9]/g, "-") || "slide-deck"}.html`;
|
|
1672
|
+
document.body.appendChild(a);
|
|
1673
|
+
a.click();
|
|
1674
|
+
document.body.removeChild(a);
|
|
1675
|
+
URL.revokeObjectURL(url);
|
|
1676
|
+
};
|
|
1677
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1678
|
+
"div",
|
|
1679
|
+
{
|
|
1680
|
+
ref: containerRef,
|
|
1681
|
+
className: `relative flex flex-col w-full h-full bg-neutral-950 text-neutral-100 rounded-xl overflow-hidden shadow-2xl ${className}`,
|
|
1682
|
+
style: { minHeight: "480px" },
|
|
1683
|
+
children: [
|
|
1684
|
+
showTopBar && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-neutral-900/90 backdrop-blur border-b border-neutral-800 text-xs select-none z-20", children: [
|
|
1685
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 font-medium truncate max-w-md", children: [
|
|
1686
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2 h-2 rounded-full bg-blue-500 inline-block" }),
|
|
1687
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-neutral-200", children: deckTitle })
|
|
1688
|
+
] }),
|
|
1689
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1690
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 bg-neutral-800/80 px-2 py-1 rounded border border-neutral-700 font-mono text-[11px]", children: [
|
|
1691
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1692
|
+
"button",
|
|
1693
|
+
{
|
|
1694
|
+
onClick: handlePrev,
|
|
1695
|
+
disabled: currentSlide <= 0,
|
|
1696
|
+
className: "hover:text-white disabled:opacity-40 p-0.5",
|
|
1697
|
+
title: "Previous Slide",
|
|
1698
|
+
children: "\u25C0"
|
|
1699
|
+
}
|
|
1700
|
+
),
|
|
1701
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-1 font-semibold text-neutral-200", children: [
|
|
1702
|
+
String(currentSlide + 1).padStart(2, "0"),
|
|
1703
|
+
" / ",
|
|
1704
|
+
String(totalSlides).padStart(2, "0")
|
|
1705
|
+
] }),
|
|
1706
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1707
|
+
"button",
|
|
1708
|
+
{
|
|
1709
|
+
onClick: handleNext,
|
|
1710
|
+
disabled: currentSlide >= totalSlides - 1,
|
|
1711
|
+
className: "hover:text-white disabled:opacity-40 p-0.5",
|
|
1712
|
+
title: "Next Slide",
|
|
1713
|
+
children: "\u25B6"
|
|
1714
|
+
}
|
|
1715
|
+
)
|
|
1716
|
+
] }),
|
|
1717
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1718
|
+
"button",
|
|
1719
|
+
{
|
|
1720
|
+
onClick: handleDownloadHtml,
|
|
1721
|
+
className: "flex items-center gap-1 px-2.5 py-1 rounded bg-neutral-800 hover:bg-neutral-700 text-neutral-300 hover:text-white border border-neutral-700 transition-colors",
|
|
1722
|
+
title: "Download standalone offline HTML file",
|
|
1723
|
+
children: [
|
|
1724
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "7 10 12 15 17 10" }),
|
|
1727
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
|
|
1728
|
+
] }),
|
|
1729
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Export HTML" })
|
|
1730
|
+
]
|
|
1731
|
+
}
|
|
1732
|
+
),
|
|
1733
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1734
|
+
"button",
|
|
1735
|
+
{
|
|
1736
|
+
onClick: handleToggleFullscreen,
|
|
1737
|
+
className: "p-1 rounded bg-neutral-800 hover:bg-neutral-700 text-neutral-300 hover:text-white border border-neutral-700 transition-colors",
|
|
1738
|
+
title: "Toggle Fullscreen (F)",
|
|
1739
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" }) })
|
|
1740
|
+
}
|
|
1741
|
+
)
|
|
1742
|
+
] })
|
|
1743
|
+
] }),
|
|
1744
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 w-full h-full min-h-0 bg-black", children: fullHtml ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1745
|
+
"iframe",
|
|
1746
|
+
{
|
|
1747
|
+
ref: iframeRef,
|
|
1748
|
+
srcDoc: fullHtml,
|
|
1749
|
+
title: deckTitle,
|
|
1750
|
+
sandbox: "allow-scripts allow-same-origin allow-popups allow-forms",
|
|
1751
|
+
className: "w-full h-full border-0 block"
|
|
1752
|
+
}
|
|
1753
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-neutral-500 text-sm", children: "No slide deck content loaded." }) })
|
|
1754
|
+
]
|
|
1755
|
+
}
|
|
1756
|
+
);
|
|
1757
|
+
};
|
|
1501
1758
|
|
|
1502
1759
|
Object.defineProperty(exports, "defineTheme", {
|
|
1503
1760
|
enumerable: true,
|
|
@@ -1532,6 +1789,7 @@ Object.defineProperty(exports, "resolveTheme", {
|
|
|
1532
1789
|
get: function () { return chunkTGI6YSZP_cjs.resolveTheme; }
|
|
1533
1790
|
});
|
|
1534
1791
|
exports.DeckView = DeckView;
|
|
1792
|
+
exports.HtmlSlideViewer = HtmlSlideViewer;
|
|
1535
1793
|
exports.NavigationBar = NavigationBar;
|
|
1536
1794
|
exports.PresentationView = PresentationView;
|
|
1537
1795
|
exports.PresenterNotesDrawer = PresenterNotesDrawer;
|