@thanh01.pmt/presentation-kit 0.1.1 → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkN4Q6GUAQ_cjs = require('../chunk-N4Q6GUAQ.cjs');
3
+ var chunkO4Y3VEOA_cjs = require('../chunk-O4Y3VEOA.cjs');
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
@@ -20,7 +20,7 @@ function useSlideState(options) {
20
20
  const result = react.useMemo(() => {
21
21
  if (!markdown || !markdown.trim()) return null;
22
22
  renderCountRef.current++;
23
- return chunkN4Q6GUAQ_cjs.renderSlide(markdown, {
23
+ return chunkO4Y3VEOA_cjs.renderSlide(markdown, {
24
24
  theme,
25
25
  enableMermaid: true,
26
26
  enableClicks,
@@ -349,6 +349,7 @@ function PresentationView({
349
349
  html,
350
350
  css,
351
351
  themeCss,
352
+ layoutCss,
352
353
  currentIndex,
353
354
  slideCount,
354
355
  className
@@ -391,6 +392,7 @@ function PresentationView({
391
392
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { width: "100%" }, children: [
392
393
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
393
394
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: themeCss }),
395
+ layoutCss && /* @__PURE__ */ jsxRuntime.jsx("style", { children: layoutCss }),
394
396
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: presentationCss }),
395
397
  /* @__PURE__ */ jsxRuntime.jsx(
396
398
  "div",
@@ -406,6 +408,7 @@ function DeckView({
406
408
  html,
407
409
  css,
408
410
  themeCss,
411
+ layoutCss,
409
412
  slideCount,
410
413
  className
411
414
  }) {
@@ -452,6 +455,7 @@ function DeckView({
452
455
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { width: "100%" }, children: [
453
456
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
454
457
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: themeCss }),
458
+ layoutCss && /* @__PURE__ */ jsxRuntime.jsx("style", { children: layoutCss }),
455
459
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: deckCss }),
456
460
  /* @__PURE__ */ jsxRuntime.jsx(
457
461
  "div",
@@ -462,6 +466,593 @@ function DeckView({
462
466
  )
463
467
  ] });
464
468
  }
469
+ function PresenterNotesDrawer({
470
+ notes,
471
+ currentIndex,
472
+ slideCount,
473
+ isOpen,
474
+ onClose,
475
+ isDark = true
476
+ }) {
477
+ if (!isOpen) return null;
478
+ const bg = isDark ? "#181920" : "#ffffff";
479
+ const border = isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.12)";
480
+ const textColor = isDark ? "#f1f5f9" : "#1e293b";
481
+ const mutedColor = isDark ? "#94a3b8" : "#64748b";
482
+ return /* @__PURE__ */ jsxRuntime.jsxs(
483
+ "div",
484
+ {
485
+ style: {
486
+ marginTop: "12px",
487
+ padding: "16px 20px",
488
+ borderRadius: "10px",
489
+ backgroundColor: bg,
490
+ border: `1px solid ${border}`,
491
+ color: textColor,
492
+ boxShadow: isDark ? "0 4px 20px rgba(0, 0, 0, 0.4)" : "0 4px 20px rgba(0, 0, 0, 0.08)",
493
+ display: "flex",
494
+ flexDirection: "column",
495
+ gap: "10px",
496
+ transition: "all 0.2s ease"
497
+ },
498
+ children: [
499
+ /* @__PURE__ */ jsxRuntime.jsxs(
500
+ "div",
501
+ {
502
+ style: {
503
+ display: "flex",
504
+ alignItems: "center",
505
+ justifyContent: "space-between",
506
+ borderBottom: `1px solid ${border}`,
507
+ paddingBottom: "8px"
508
+ },
509
+ children: [
510
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
511
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: "13px", letterSpacing: "0.02em" }, children: "Ghi ch\xFA Gi\u1EA3ng vi\xEAn" }),
512
+ /* @__PURE__ */ jsxRuntime.jsxs(
513
+ "span",
514
+ {
515
+ style: {
516
+ fontSize: "11px",
517
+ padding: "2px 8px",
518
+ borderRadius: "12px",
519
+ backgroundColor: isDark ? "rgba(56, 189, 248, 0.15)" : "rgba(2, 132, 199, 0.1)",
520
+ color: isDark ? "#38bdf8" : "#0284c7",
521
+ fontWeight: 500
522
+ },
523
+ children: [
524
+ "Slide ",
525
+ currentIndex + 1,
526
+ " / ",
527
+ slideCount
528
+ ]
529
+ }
530
+ )
531
+ ] }),
532
+ /* @__PURE__ */ jsxRuntime.jsx(
533
+ "button",
534
+ {
535
+ type: "button",
536
+ onClick: onClose,
537
+ style: {
538
+ background: "none",
539
+ border: "none",
540
+ color: mutedColor,
541
+ cursor: "pointer",
542
+ fontSize: "16px",
543
+ padding: "4px",
544
+ display: "flex",
545
+ alignItems: "center",
546
+ justifyContent: "center",
547
+ borderRadius: "4px"
548
+ },
549
+ title: "\u0110\xF3ng ghi ch\xFA",
550
+ children: "\u2715"
551
+ }
552
+ )
553
+ ]
554
+ }
555
+ ),
556
+ /* @__PURE__ */ jsxRuntime.jsx(
557
+ "div",
558
+ {
559
+ style: {
560
+ maxHeight: "180px",
561
+ overflowY: "auto",
562
+ fontSize: "13px",
563
+ lineHeight: "1.6"
564
+ },
565
+ children: notes.length > 0 ? notes.map((note, idx) => /* @__PURE__ */ jsxRuntime.jsx(
566
+ "div",
567
+ {
568
+ style: {
569
+ marginBottom: idx < notes.length - 1 ? "8px" : "0",
570
+ whiteSpace: "pre-wrap"
571
+ },
572
+ children: note
573
+ },
574
+ idx
575
+ )) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: mutedColor, fontStyle: "italic", fontSize: "12px" }, children: "Kh\xF4ng c\xF3 ghi ch\xFA cho slide n\xE0y." })
576
+ }
577
+ )
578
+ ]
579
+ }
580
+ );
581
+ }
582
+ function SlideDesignOverlay({
583
+ slideIndex,
584
+ layout,
585
+ onLayoutChange,
586
+ onSave,
587
+ onClose,
588
+ isActive
589
+ }) {
590
+ if (!isActive) return null;
591
+ const [boxes, setBoxes] = react.useState(layout?.boxes ?? []);
592
+ const [selectedId, setSelectedId] = react.useState(null);
593
+ const containerRef = react.useRef(null);
594
+ react.useEffect(() => {
595
+ setBoxes(layout?.boxes ?? []);
596
+ }, [layout, slideIndex]);
597
+ const selectedBox = boxes.find((b) => b.id === selectedId) ?? null;
598
+ const [dragging, setDragging] = react.useState(null);
599
+ const handleMouseDown = (e, box, type) => {
600
+ e.preventDefault();
601
+ e.stopPropagation();
602
+ setSelectedId(box.id);
603
+ setDragging({
604
+ type,
605
+ boxId: box.id,
606
+ startX: e.clientX,
607
+ startY: e.clientY,
608
+ startPos: [...box.pos]
609
+ });
610
+ };
611
+ const handleMouseMove = react.useCallback((e) => {
612
+ if (!dragging || !containerRef.current) return;
613
+ const rect = containerRef.current.getBoundingClientRect();
614
+ const scaleX = 1280 / rect.width;
615
+ const scaleY = 720 / rect.height;
616
+ const dx = (e.clientX - dragging.startX) * scaleX;
617
+ const dy = (e.clientY - dragging.startY) * scaleY;
618
+ const [origX, origY, origW, origH] = dragging.startPos;
619
+ setBoxes((prev) => {
620
+ const updated = prev.map((b) => {
621
+ if (b.id !== dragging.boxId) return b;
622
+ let [newX, newY, newW, newH] = [origX, origY, origW, origH];
623
+ switch (dragging.type) {
624
+ case "move":
625
+ newX = Math.max(0, Math.min(1280 - origW, origX + dx));
626
+ newY = Math.max(0, Math.min(720 - origH, origY + dy));
627
+ break;
628
+ case "se":
629
+ newW = Math.max(50, origW + dx);
630
+ newH = Math.max(30, origH + dy);
631
+ break;
632
+ case "e":
633
+ newW = Math.max(50, origW + dx);
634
+ break;
635
+ case "s":
636
+ newH = Math.max(30, origH + dy);
637
+ break;
638
+ case "sw":
639
+ newX = Math.min(origX + origW - 50, origX + dx);
640
+ newW = Math.max(50, origW - dx);
641
+ newH = Math.max(30, origH + dy);
642
+ break;
643
+ case "ne":
644
+ newY = Math.min(origY + origH - 30, origY + dy);
645
+ newW = Math.max(50, origW + dx);
646
+ newH = Math.max(30, origH - dy);
647
+ break;
648
+ case "nw":
649
+ newX = Math.min(origX + origW - 50, origX + dx);
650
+ newY = Math.min(origY + origH - 30, origY + dy);
651
+ newW = Math.max(50, origW - dx);
652
+ newH = Math.max(30, origH - dy);
653
+ break;
654
+ }
655
+ return {
656
+ ...b,
657
+ pos: [Math.round(newX), Math.round(newY), Math.round(newW), Math.round(newH)]
658
+ };
659
+ });
660
+ onLayoutChange({ boxes: updated });
661
+ return updated;
662
+ });
663
+ }, [dragging, onLayoutChange]);
664
+ const handleMouseUp = react.useCallback(() => {
665
+ setDragging(null);
666
+ }, []);
667
+ react.useEffect(() => {
668
+ if (dragging) {
669
+ window.addEventListener("mousemove", handleMouseMove);
670
+ window.addEventListener("mouseup", handleMouseUp);
671
+ return () => {
672
+ window.removeEventListener("mousemove", handleMouseMove);
673
+ window.removeEventListener("mouseup", handleMouseUp);
674
+ };
675
+ }
676
+ }, [dragging, handleMouseMove, handleMouseUp]);
677
+ const addNewBox = (targetTag) => {
678
+ const newBox = {
679
+ id: `box-${Date.now().toString(36)}`,
680
+ target: targetTag,
681
+ pos: [100, 120 + boxes.length * 80, 600, 100],
682
+ fontSize: targetTag === "h1" ? "2.5rem" : "1.2rem",
683
+ color: targetTag === "h1" ? "#38bdf8" : "#e2e8f0",
684
+ textAlign: "left"
685
+ };
686
+ const updated = [...boxes, newBox];
687
+ setBoxes(updated);
688
+ setSelectedId(newBox.id);
689
+ onLayoutChange({ boxes: updated });
690
+ };
691
+ const updateSelectedBox = (updates) => {
692
+ if (!selectedId) return;
693
+ setBoxes((prev) => {
694
+ const updated = prev.map((b) => b.id === selectedId ? { ...b, ...updates } : b);
695
+ onLayoutChange({ boxes: updated });
696
+ return updated;
697
+ });
698
+ };
699
+ const removeSelectedBox = () => {
700
+ if (!selectedId) return;
701
+ setBoxes((prev) => {
702
+ const updated = prev.filter((b) => b.id !== selectedId);
703
+ onLayoutChange({ boxes: updated });
704
+ return updated;
705
+ });
706
+ setSelectedId(null);
707
+ };
708
+ return /* @__PURE__ */ jsxRuntime.jsxs(
709
+ "div",
710
+ {
711
+ style: {
712
+ position: "absolute",
713
+ inset: 0,
714
+ zIndex: 50,
715
+ pointerEvents: "none"
716
+ // Allow passing through to children that have pointerEvents: 'auto'
717
+ },
718
+ children: [
719
+ /* @__PURE__ */ jsxRuntime.jsxs(
720
+ "div",
721
+ {
722
+ style: {
723
+ position: "absolute",
724
+ top: "12px",
725
+ left: "50%",
726
+ transform: "translateX(-50%)",
727
+ zIndex: 60,
728
+ pointerEvents: "auto",
729
+ display: "flex",
730
+ alignItems: "center",
731
+ gap: "8px",
732
+ padding: "6px 14px",
733
+ backgroundColor: "#1e1f29",
734
+ border: "1px solid rgba(56, 189, 248, 0.4)",
735
+ borderRadius: "24px",
736
+ boxShadow: "0 8px 30px rgba(0, 0, 0, 0.5)",
737
+ color: "#f8fafc",
738
+ fontSize: "12px",
739
+ fontFamily: "Inter, sans-serif"
740
+ },
741
+ children: [
742
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "#38bdf8" }, children: "Design Canvas:" }),
743
+ /* @__PURE__ */ jsxRuntime.jsx(
744
+ "button",
745
+ {
746
+ type: "button",
747
+ onClick: () => addNewBox("h1"),
748
+ style: toolbarButtonStyle,
749
+ title: "Th\xEAm khung v\u1ECB tr\xED Ti\xEAu \u0111\u1EC1",
750
+ children: "+ Ti\xEAu \u0111\u1EC1 (H1)"
751
+ }
752
+ ),
753
+ /* @__PURE__ */ jsxRuntime.jsx(
754
+ "button",
755
+ {
756
+ type: "button",
757
+ onClick: () => addNewBox("p"),
758
+ style: toolbarButtonStyle,
759
+ title: "Th\xEAm khung v\u1ECB tr\xED \u0110o\u1EA1n v\u0103n",
760
+ children: "+ \u0110o\u1EA1n v\u0103n (P)"
761
+ }
762
+ ),
763
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: "1px", height: "16px", backgroundColor: "rgba(255,255,255,0.2)" } }),
764
+ onSave && /* @__PURE__ */ jsxRuntime.jsx(
765
+ "button",
766
+ {
767
+ type: "button",
768
+ onClick: () => onSave({ boxes }),
769
+ style: {
770
+ ...toolbarButtonStyle,
771
+ backgroundColor: "#0284c7",
772
+ color: "#ffffff",
773
+ fontWeight: 600
774
+ },
775
+ children: "L\u01B0u b\u1ED1 c\u1EE5c"
776
+ }
777
+ ),
778
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
779
+ "button",
780
+ {
781
+ type: "button",
782
+ onClick: onClose,
783
+ style: {
784
+ ...toolbarButtonStyle,
785
+ backgroundColor: "transparent",
786
+ color: "#94a3b8"
787
+ },
788
+ children: "\u0110\xF3ng"
789
+ }
790
+ )
791
+ ]
792
+ }
793
+ ),
794
+ /* @__PURE__ */ jsxRuntime.jsx(
795
+ "div",
796
+ {
797
+ ref: containerRef,
798
+ style: {
799
+ position: "absolute",
800
+ inset: 0,
801
+ pointerEvents: "auto"
802
+ },
803
+ onClick: () => setSelectedId(null),
804
+ children: boxes.map((box) => {
805
+ const [x, y, w, h] = box.pos;
806
+ const isSelected = box.id === selectedId;
807
+ const leftPercent = x / 1280 * 100;
808
+ const topPercent = y / 720 * 100;
809
+ const widthPercent = w / 1280 * 100;
810
+ const heightPercent = h / 720 * 100;
811
+ return /* @__PURE__ */ jsxRuntime.jsxs(
812
+ "div",
813
+ {
814
+ onClick: (e) => {
815
+ e.stopPropagation();
816
+ setSelectedId(box.id);
817
+ },
818
+ onMouseDown: (e) => handleMouseDown(e, box, "move"),
819
+ style: {
820
+ position: "absolute",
821
+ left: `${leftPercent}%`,
822
+ top: `${topPercent}%`,
823
+ width: `${widthPercent}%`,
824
+ height: `${heightPercent}%`,
825
+ border: isSelected ? "2px solid #38bdf8" : "1px dashed rgba(56, 189, 248, 0.5)",
826
+ backgroundColor: isSelected ? "rgba(56, 189, 248, 0.08)" : "rgba(56, 189, 248, 0.03)",
827
+ cursor: "move",
828
+ userSelect: "none",
829
+ boxSizing: "border-box",
830
+ zIndex: isSelected ? 40 : 20
831
+ },
832
+ children: [
833
+ /* @__PURE__ */ jsxRuntime.jsxs(
834
+ "div",
835
+ {
836
+ style: {
837
+ position: "absolute",
838
+ top: "-18px",
839
+ left: "0",
840
+ fontSize: "10px",
841
+ fontWeight: 600,
842
+ color: isSelected ? "#38bdf8" : "rgba(56, 189, 248, 0.7)",
843
+ backgroundColor: "rgba(0,0,0,0.8)",
844
+ padding: "1px 6px",
845
+ borderRadius: "3px",
846
+ whiteSpace: "nowrap"
847
+ },
848
+ children: [
849
+ box.target || box.id,
850
+ " (",
851
+ Math.round(x),
852
+ ", ",
853
+ Math.round(y),
854
+ ")"
855
+ ]
856
+ }
857
+ ),
858
+ isSelected && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
859
+ /* @__PURE__ */ jsxRuntime.jsx(
860
+ "div",
861
+ {
862
+ style: handleStyle("se"),
863
+ onMouseDown: (e) => handleMouseDown(e, box, "se")
864
+ }
865
+ ),
866
+ /* @__PURE__ */ jsxRuntime.jsx(
867
+ "div",
868
+ {
869
+ style: handleStyle("e"),
870
+ onMouseDown: (e) => handleMouseDown(e, box, "e")
871
+ }
872
+ ),
873
+ /* @__PURE__ */ jsxRuntime.jsx(
874
+ "div",
875
+ {
876
+ style: handleStyle("s"),
877
+ onMouseDown: (e) => handleMouseDown(e, box, "s")
878
+ }
879
+ ),
880
+ /* @__PURE__ */ jsxRuntime.jsx(
881
+ "div",
882
+ {
883
+ style: handleStyle("sw"),
884
+ onMouseDown: (e) => handleMouseDown(e, box, "sw")
885
+ }
886
+ ),
887
+ /* @__PURE__ */ jsxRuntime.jsx(
888
+ "div",
889
+ {
890
+ style: handleStyle("ne"),
891
+ onMouseDown: (e) => handleMouseDown(e, box, "ne")
892
+ }
893
+ ),
894
+ /* @__PURE__ */ jsxRuntime.jsx(
895
+ "div",
896
+ {
897
+ style: handleStyle("nw"),
898
+ onMouseDown: (e) => handleMouseDown(e, box, "nw")
899
+ }
900
+ )
901
+ ] })
902
+ ]
903
+ },
904
+ box.id
905
+ );
906
+ })
907
+ }
908
+ ),
909
+ selectedBox && /* @__PURE__ */ jsxRuntime.jsxs(
910
+ "div",
911
+ {
912
+ style: {
913
+ position: "absolute",
914
+ bottom: "24px",
915
+ left: "50%",
916
+ transform: "translateX(-50%)",
917
+ zIndex: 60,
918
+ pointerEvents: "auto",
919
+ display: "flex",
920
+ alignItems: "center",
921
+ gap: "12px",
922
+ padding: "8px 16px",
923
+ backgroundColor: "#181920",
924
+ border: "1px solid rgba(255,255,255,0.15)",
925
+ borderRadius: "12px",
926
+ boxShadow: "0 10px 30px rgba(0,0,0,0.6)",
927
+ color: "#f8fafc",
928
+ fontSize: "12px",
929
+ fontFamily: "Inter, sans-serif"
930
+ },
931
+ children: [
932
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 600, color: "#38bdf8" }, children: [
933
+ "Khung: ",
934
+ selectedBox.target
935
+ ] }),
936
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
937
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#94a3b8", fontSize: "11px" }, children: "Size:" }),
938
+ /* @__PURE__ */ jsxRuntime.jsx(
939
+ "input",
940
+ {
941
+ type: "text",
942
+ value: selectedBox.fontSize ?? "1.5rem",
943
+ onChange: (e) => updateSelectedBox({ fontSize: e.target.value }),
944
+ style: {
945
+ width: "60px",
946
+ padding: "2px 6px",
947
+ borderRadius: "4px",
948
+ border: "1px solid #334155",
949
+ backgroundColor: "#0f172a",
950
+ color: "#f8fafc",
951
+ fontSize: "11px"
952
+ }
953
+ }
954
+ )
955
+ ] }),
956
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
957
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#94a3b8", fontSize: "11px" }, children: "Color:" }),
958
+ /* @__PURE__ */ jsxRuntime.jsx(
959
+ "input",
960
+ {
961
+ type: "color",
962
+ value: selectedBox.color ?? "#38bdf8",
963
+ onChange: (e) => updateSelectedBox({ color: e.target.value }),
964
+ style: {
965
+ width: "24px",
966
+ height: "24px",
967
+ padding: "0",
968
+ border: "none",
969
+ borderRadius: "4px",
970
+ cursor: "pointer",
971
+ backgroundColor: "transparent"
972
+ }
973
+ }
974
+ )
975
+ ] }),
976
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "2px" }, children: ["left", "center", "right"].map((align) => /* @__PURE__ */ jsxRuntime.jsx(
977
+ "button",
978
+ {
979
+ type: "button",
980
+ onClick: () => updateSelectedBox({ textAlign: align }),
981
+ style: {
982
+ ...toolbarButtonStyle,
983
+ backgroundColor: selectedBox.textAlign === align ? "#0284c7" : "rgba(255,255,255,0.05)",
984
+ textTransform: "capitalize",
985
+ fontSize: "11px",
986
+ padding: "2px 8px"
987
+ },
988
+ children: align
989
+ },
990
+ align
991
+ )) }),
992
+ /* @__PURE__ */ jsxRuntime.jsx(
993
+ "button",
994
+ {
995
+ type: "button",
996
+ onClick: removeSelectedBox,
997
+ style: {
998
+ ...toolbarButtonStyle,
999
+ backgroundColor: "rgba(239, 68, 68, 0.2)",
1000
+ color: "#f87171",
1001
+ borderColor: "rgba(239, 68, 68, 0.4)"
1002
+ },
1003
+ title: "X\xF3a \u0111\u1ECBnh v\u1ECB t\u1EF1 do (tr\u1EDF v\u1EC1 lu\u1ED3ng b\xECnh th\u01B0\u1EDDng)",
1004
+ children: "Tr\u1EDF v\u1EC1 lu\u1ED3ng m\u1EB7c \u0111\u1ECBnh"
1005
+ }
1006
+ )
1007
+ ]
1008
+ }
1009
+ )
1010
+ ]
1011
+ }
1012
+ );
1013
+ }
1014
+ var toolbarButtonStyle = {
1015
+ height: "26px",
1016
+ padding: "0 10px",
1017
+ borderRadius: "14px",
1018
+ fontSize: "11px",
1019
+ fontFamily: "inherit",
1020
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1021
+ backgroundColor: "rgba(255, 255, 255, 0.08)",
1022
+ color: "#e2e8f0",
1023
+ cursor: "pointer",
1024
+ display: "flex",
1025
+ alignItems: "center",
1026
+ justifyContent: "center",
1027
+ transition: "all 0.15s ease"
1028
+ };
1029
+ function handleStyle(pos) {
1030
+ const size = 10;
1031
+ const base = {
1032
+ position: "absolute",
1033
+ width: `${size}px`,
1034
+ height: `${size}px`,
1035
+ backgroundColor: "#ffffff",
1036
+ border: "2px solid #0284c7",
1037
+ borderRadius: "2px",
1038
+ boxSizing: "border-box",
1039
+ zIndex: 50
1040
+ };
1041
+ switch (pos) {
1042
+ case "se":
1043
+ return { ...base, right: `-${size / 2}px`, bottom: `-${size / 2}px`, cursor: "se-resize" };
1044
+ case "e":
1045
+ return { ...base, right: `-${size / 2}px`, top: `calc(50% - ${size / 2}px)`, cursor: "e-resize" };
1046
+ case "s":
1047
+ return { ...base, bottom: `-${size / 2}px`, left: `calc(50% - ${size / 2}px)`, cursor: "s-resize" };
1048
+ case "sw":
1049
+ return { ...base, left: `-${size / 2}px`, bottom: `-${size / 2}px`, cursor: "sw-resize" };
1050
+ case "ne":
1051
+ return { ...base, right: `-${size / 2}px`, top: `-${size / 2}px`, cursor: "ne-resize" };
1052
+ case "nw":
1053
+ return { ...base, left: `-${size / 2}px`, top: `-${size / 2}px`, cursor: "nw-resize" };
1054
+ }
1055
+ }
465
1056
  var DEFAULT_THEME_OPTIONS = [
466
1057
  { id: "dark", label: "Dark" },
467
1058
  { id: "navy", label: "Navy" },
@@ -477,13 +1068,19 @@ function SlideViewer({
477
1068
  enableKeyboard = true,
478
1069
  enableClicks = false,
479
1070
  fullscreen = false,
1071
+ showNotes = true,
1072
+ showDesignMode = false,
480
1073
  themeOptions = DEFAULT_THEME_OPTIONS,
481
1074
  onThemeChange,
482
1075
  onSlideChange,
483
1076
  onModeChange,
484
- onRenderComplete
1077
+ onRenderComplete,
1078
+ onSaveMarkdown
485
1079
  }) {
486
1080
  const [isFullscreen, setIsFullscreen] = react.useState(false);
1081
+ const [showNotesDrawer, setShowNotesDrawer] = react.useState(false);
1082
+ const [isDesignMode, setIsDesignMode] = react.useState(false);
1083
+ const [liveLayout, setLiveLayout] = react.useState(null);
487
1084
  const state = useSlideState({
488
1085
  markdown,
489
1086
  initialTheme,
@@ -589,7 +1186,7 @@ function SlideViewer({
589
1186
  containers.forEach((container) => {
590
1187
  if (container instanceof HTMLElement) {
591
1188
  const themeName2 = typeof theme === "string" ? theme : "dark";
592
- chunkN4Q6GUAQ_cjs.renderMermaidDiagrams(container, themeName2).catch(() => {
1189
+ chunkO4Y3VEOA_cjs.renderMermaidDiagrams(container, themeName2).catch(() => {
593
1190
  });
594
1191
  }
595
1192
  });
@@ -685,6 +1282,54 @@ function SlideViewer({
685
1282
  markdown
686
1283
  }
687
1284
  ),
1285
+ showNotes && /* @__PURE__ */ jsxRuntime.jsx(
1286
+ "button",
1287
+ {
1288
+ type: "button",
1289
+ onClick: () => setShowNotesDrawer((prev) => !prev),
1290
+ style: {
1291
+ height: "28px",
1292
+ padding: "0 10px",
1293
+ borderRadius: "6px",
1294
+ fontSize: "12px",
1295
+ fontFamily: "inherit",
1296
+ display: "flex",
1297
+ alignItems: "center",
1298
+ gap: "5px",
1299
+ border: showNotesDrawer ? "1px solid #0284c7" : "1px solid transparent",
1300
+ cursor: "pointer",
1301
+ backgroundColor: showNotesDrawer ? "rgba(2, 132, 199, 0.15)" : "transparent",
1302
+ color: showNotesDrawer ? "#38bdf8" : isFullscreen ? "#fff" : "rgba(128,128,128,0.7)",
1303
+ transition: "all 0.15s ease"
1304
+ },
1305
+ title: "Ghi ch\xFA cho Gi\u1EA3ng vi\xEAn",
1306
+ children: "Notes"
1307
+ }
1308
+ ),
1309
+ showDesignMode && mode === "presentation" && /* @__PURE__ */ jsxRuntime.jsx(
1310
+ "button",
1311
+ {
1312
+ type: "button",
1313
+ onClick: () => setIsDesignMode((prev) => !prev),
1314
+ style: {
1315
+ height: "28px",
1316
+ padding: "0 10px",
1317
+ borderRadius: "6px",
1318
+ fontSize: "12px",
1319
+ fontFamily: "inherit",
1320
+ display: "flex",
1321
+ alignItems: "center",
1322
+ gap: "5px",
1323
+ border: isDesignMode ? "1px solid #38bdf8" : "1px solid transparent",
1324
+ cursor: "pointer",
1325
+ backgroundColor: isDesignMode ? "rgba(56, 189, 248, 0.15)" : "transparent",
1326
+ color: isDesignMode ? "#38bdf8" : isFullscreen ? "#fff" : "rgba(128,128,128,0.7)",
1327
+ transition: "all 0.15s ease"
1328
+ },
1329
+ title: "Ch\u1EBF \u0111\u1ED9 ch\u1EC9nh s\u1EEDa b\u1ED1 c\u1EE5c (Google Slides-style)",
1330
+ children: "Design"
1331
+ }
1332
+ ),
688
1333
  fullscreen && /* @__PURE__ */ jsxRuntime.jsx(
689
1334
  "button",
690
1335
  {
@@ -737,22 +1382,54 @@ function SlideViewer({
737
1382
  transition: "width 0.2s ease"
738
1383
  } }) })
739
1384
  ] }),
740
- mode === "presentation" ? /* @__PURE__ */ jsxRuntime.jsx(
741
- PresentationView,
1385
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", width: "100%" }, children: mode === "presentation" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1386
+ /* @__PURE__ */ jsxRuntime.jsx(
1387
+ PresentationView,
1388
+ {
1389
+ html: result.html,
1390
+ css: result.css,
1391
+ themeCss: result.themeCss,
1392
+ layoutCss: result.layoutCss,
1393
+ currentIndex,
1394
+ slideCount
1395
+ }
1396
+ ),
1397
+ isDesignMode && /* @__PURE__ */ jsxRuntime.jsx(
1398
+ SlideDesignOverlay,
1399
+ {
1400
+ slideIndex: currentIndex,
1401
+ layout: liveLayout ?? result.layouts?.[currentIndex] ?? null,
1402
+ onLayoutChange: setLiveLayout,
1403
+ onSave: (layout) => {
1404
+ setLiveLayout(layout);
1405
+ if (onSaveMarkdown) {
1406
+ const updated = chunkO4Y3VEOA_cjs.updateSlideLayoutInMarkdown(markdown, currentIndex, layout);
1407
+ onSaveMarkdown(updated);
1408
+ }
1409
+ },
1410
+ onClose: () => setIsDesignMode(false),
1411
+ isActive: isDesignMode
1412
+ }
1413
+ )
1414
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1415
+ DeckView,
742
1416
  {
743
1417
  html: result.html,
744
1418
  css: result.css,
745
1419
  themeCss: result.themeCss,
746
- currentIndex,
1420
+ layoutCss: result.layoutCss,
747
1421
  slideCount
748
1422
  }
749
- ) : /* @__PURE__ */ jsxRuntime.jsx(
750
- DeckView,
1423
+ ) }),
1424
+ showNotes && /* @__PURE__ */ jsxRuntime.jsx(
1425
+ PresenterNotesDrawer,
751
1426
  {
752
- html: result.html,
753
- css: result.css,
754
- themeCss: result.themeCss,
755
- slideCount
1427
+ notes: result.notes?.[currentIndex] ?? [],
1428
+ currentIndex,
1429
+ slideCount,
1430
+ isOpen: showNotesDrawer,
1431
+ onClose: () => setShowNotesDrawer(false),
1432
+ isDark: themeName !== "light"
756
1433
  }
757
1434
  )
758
1435
  ]
@@ -762,39 +1439,41 @@ function SlideViewer({
762
1439
 
763
1440
  Object.defineProperty(exports, "defineTheme", {
764
1441
  enumerable: true,
765
- get: function () { return chunkN4Q6GUAQ_cjs.defineTheme; }
1442
+ get: function () { return chunkO4Y3VEOA_cjs.defineTheme; }
766
1443
  });
767
1444
  Object.defineProperty(exports, "extractMaxClicks", {
768
1445
  enumerable: true,
769
- get: function () { return chunkN4Q6GUAQ_cjs.extractMaxClicks; }
1446
+ get: function () { return chunkO4Y3VEOA_cjs.extractMaxClicks; }
770
1447
  });
771
1448
  Object.defineProperty(exports, "generateClickCss", {
772
1449
  enumerable: true,
773
- get: function () { return chunkN4Q6GUAQ_cjs.generateClickCss; }
1450
+ get: function () { return chunkO4Y3VEOA_cjs.generateClickCss; }
774
1451
  });
775
1452
  Object.defineProperty(exports, "postprocessClicks", {
776
1453
  enumerable: true,
777
- get: function () { return chunkN4Q6GUAQ_cjs.postprocessClicks; }
1454
+ get: function () { return chunkO4Y3VEOA_cjs.postprocessClicks; }
778
1455
  });
779
1456
  Object.defineProperty(exports, "preprocessClicks", {
780
1457
  enumerable: true,
781
- get: function () { return chunkN4Q6GUAQ_cjs.preprocessClicks; }
1458
+ get: function () { return chunkO4Y3VEOA_cjs.preprocessClicks; }
782
1459
  });
783
1460
  Object.defineProperty(exports, "renderSlide", {
784
1461
  enumerable: true,
785
- get: function () { return chunkN4Q6GUAQ_cjs.renderSlide; }
1462
+ get: function () { return chunkO4Y3VEOA_cjs.renderSlide; }
786
1463
  });
787
1464
  Object.defineProperty(exports, "renderSlideAsync", {
788
1465
  enumerable: true,
789
- get: function () { return chunkN4Q6GUAQ_cjs.renderSlideAsync; }
1466
+ get: function () { return chunkO4Y3VEOA_cjs.renderSlideAsync; }
790
1467
  });
791
1468
  Object.defineProperty(exports, "resolveTheme", {
792
1469
  enumerable: true,
793
- get: function () { return chunkN4Q6GUAQ_cjs.resolveTheme; }
1470
+ get: function () { return chunkO4Y3VEOA_cjs.resolveTheme; }
794
1471
  });
795
1472
  exports.DeckView = DeckView;
796
1473
  exports.NavigationBar = NavigationBar;
797
1474
  exports.PresentationView = PresentationView;
1475
+ exports.PresenterNotesDrawer = PresenterNotesDrawer;
1476
+ exports.SlideDesignOverlay = SlideDesignOverlay;
798
1477
  exports.SlideViewer = SlideViewer;
799
1478
  exports.ThemeSwitcher = ThemeSwitcher;
800
1479
  exports.useSlideState = useSlideState;