@thanh01.pmt/presentation-kit 0.1.1 → 0.2.1

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