@thanh01.pmt/presentation-kit 0.1.0 → 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,47 +349,50 @@ function PresentationView({
349
349
  html,
350
350
  css,
351
351
  themeCss,
352
+ layoutCss,
352
353
  currentIndex,
353
354
  slideCount,
354
355
  className
355
356
  }) {
356
357
  const presentationCss = `
357
358
  .slide-presentation-host {
358
- display: flex;
359
- align-items: center;
360
- justify-content: center;
361
- width: 100%;
359
+ display: flex !important;
360
+ align-items: center !important;
361
+ justify-content: center !important;
362
+ width: 100% !important;
362
363
  }
363
364
  .slide-presentation-host > div {
364
- position: relative;
365
- display: flex;
366
- align-items: center;
367
- justify-content: center;
368
- width: 100%;
365
+ position: relative !important;
366
+ display: flex !important;
367
+ align-items: center !important;
368
+ justify-content: center !important;
369
+ width: 100% !important;
369
370
  }
371
+ .slide-presentation-host #marp-container svg[data-marpit-svg],
372
+ .slide-presentation-host #marp-container > svg,
370
373
  .slide-presentation-host > div > svg {
371
- position: absolute;
372
- visibility: hidden;
373
- opacity: 0;
374
- pointer-events: none;
375
- width: min(100%, calc((100vh - 120px) * (16 / 9)));
376
- max-width: 1400px;
377
- height: auto;
378
- max-height: calc(100vh - 120px);
379
- aspect-ratio: 16 / 9;
380
- margin: 0 auto;
374
+ display: none !important;
381
375
  }
382
- .slide-presentation-host > div > svg:nth-child(${currentIndex + 1}) {
383
- position: relative;
384
- visibility: visible;
385
- opacity: 1;
386
- pointer-events: auto;
387
- display: block;
376
+ .slide-presentation-host #marp-container svg[data-marpit-svg]:nth-of-type(${currentIndex + 1}),
377
+ .slide-presentation-host #marp-container > svg:nth-of-type(${currentIndex + 1}),
378
+ .slide-presentation-host > div > svg:nth-of-type(${currentIndex + 1}) {
379
+ display: block !important;
380
+ position: relative !important;
381
+ visibility: visible !important;
382
+ opacity: 1 !important;
383
+ pointer-events: auto !important;
384
+ width: min(100%, calc((100vh - 120px) * 16 / 9)) !important;
385
+ max-width: 1400px !important;
386
+ height: auto !important;
387
+ max-height: calc(100vh - 120px) !important;
388
+ aspect-ratio: 16 / 9 !important;
389
+ margin: 0 auto !important;
388
390
  }
389
391
  `;
390
392
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { width: "100%" }, children: [
391
393
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
392
394
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: themeCss }),
395
+ layoutCss && /* @__PURE__ */ jsxRuntime.jsx("style", { children: layoutCss }),
393
396
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: presentationCss }),
394
397
  /* @__PURE__ */ jsxRuntime.jsx(
395
398
  "div",
@@ -405,39 +408,54 @@ function DeckView({
405
408
  html,
406
409
  css,
407
410
  themeCss,
411
+ layoutCss,
408
412
  slideCount,
409
413
  className
410
414
  }) {
411
415
  const deckCss = `
412
416
  .slide-deck-host {
413
- display: flex;
414
- flex-direction: column;
415
- align-items: center;
416
- gap: 24px;
417
- padding: 16px 0;
417
+ display: flex !important;
418
+ flex-direction: column !important;
419
+ align-items: center !important;
420
+ width: 100% !important;
421
+ padding: 16px 0 !important;
418
422
  }
419
- .slide-deck-host > div {
420
- display: flex;
421
- flex-direction: column;
422
- align-items: center;
423
- gap: 24px;
423
+ .slide-deck-host > div,
424
+ .slide-deck-host #marp-container {
425
+ display: flex !important;
426
+ flex-direction: column !important;
427
+ align-items: center !important;
428
+ width: 100% !important;
424
429
  }
430
+ .slide-deck-host #marp-container svg[data-marpit-svg],
431
+ .slide-deck-host #marp-container > svg,
425
432
  .slide-deck-host > div > svg {
426
433
  display: block !important;
427
434
  width: 100% !important;
428
- max-width: 960px;
435
+ max-width: 960px !important;
429
436
  height: auto !important;
430
- aspect-ratio: 16 / 9;
431
- border-radius: 12px;
432
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
437
+ aspect-ratio: 16 / 9 !important;
438
+ margin: 0 auto 32px auto !important;
439
+ border-radius: 12px !important;
440
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12) !important;
441
+ transition: transform 0.2s ease, box-shadow 0.2s ease !important;
442
+ }
443
+ .slide-deck-host #marp-container svg[data-marpit-svg]:last-of-type,
444
+ .slide-deck-host #marp-container > svg:last-of-type,
445
+ .slide-deck-host > div > svg:last-of-type {
446
+ margin-bottom: 0 !important;
433
447
  }
448
+ .slide-deck-host #marp-container svg[data-marpit-svg]:hover,
449
+ .slide-deck-host #marp-container > svg:hover,
434
450
  .slide-deck-host > div > svg:hover {
435
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
451
+ transform: translateY(-2px);
452
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important;
436
453
  }
437
454
  `;
438
455
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { width: "100%" }, children: [
439
456
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: css }),
440
457
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: themeCss }),
458
+ layoutCss && /* @__PURE__ */ jsxRuntime.jsx("style", { children: layoutCss }),
441
459
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: deckCss }),
442
460
  /* @__PURE__ */ jsxRuntime.jsx(
443
461
  "div",
@@ -448,6 +466,593 @@ function DeckView({
448
466
  )
449
467
  ] });
450
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
+ }
451
1056
  var DEFAULT_THEME_OPTIONS = [
452
1057
  { id: "dark", label: "Dark" },
453
1058
  { id: "navy", label: "Navy" },
@@ -463,13 +1068,19 @@ function SlideViewer({
463
1068
  enableKeyboard = true,
464
1069
  enableClicks = false,
465
1070
  fullscreen = false,
1071
+ showNotes = true,
1072
+ showDesignMode = false,
466
1073
  themeOptions = DEFAULT_THEME_OPTIONS,
467
1074
  onThemeChange,
468
1075
  onSlideChange,
469
1076
  onModeChange,
470
- onRenderComplete
1077
+ onRenderComplete,
1078
+ onSaveMarkdown
471
1079
  }) {
472
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);
473
1084
  const state = useSlideState({
474
1085
  markdown,
475
1086
  initialTheme,
@@ -575,7 +1186,7 @@ function SlideViewer({
575
1186
  containers.forEach((container) => {
576
1187
  if (container instanceof HTMLElement) {
577
1188
  const themeName2 = typeof theme === "string" ? theme : "dark";
578
- chunkN4Q6GUAQ_cjs.renderMermaidDiagrams(container, themeName2).catch(() => {
1189
+ chunkO4Y3VEOA_cjs.renderMermaidDiagrams(container, themeName2).catch(() => {
579
1190
  });
580
1191
  }
581
1192
  });
@@ -671,6 +1282,54 @@ function SlideViewer({
671
1282
  markdown
672
1283
  }
673
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
+ ),
674
1333
  fullscreen && /* @__PURE__ */ jsxRuntime.jsx(
675
1334
  "button",
676
1335
  {
@@ -723,22 +1382,54 @@ function SlideViewer({
723
1382
  transition: "width 0.2s ease"
724
1383
  } }) })
725
1384
  ] }),
726
- mode === "presentation" ? /* @__PURE__ */ jsxRuntime.jsx(
727
- 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,
728
1416
  {
729
1417
  html: result.html,
730
1418
  css: result.css,
731
1419
  themeCss: result.themeCss,
732
- currentIndex,
1420
+ layoutCss: result.layoutCss,
733
1421
  slideCount
734
1422
  }
735
- ) : /* @__PURE__ */ jsxRuntime.jsx(
736
- DeckView,
1423
+ ) }),
1424
+ showNotes && /* @__PURE__ */ jsxRuntime.jsx(
1425
+ PresenterNotesDrawer,
737
1426
  {
738
- html: result.html,
739
- css: result.css,
740
- themeCss: result.themeCss,
741
- slideCount
1427
+ notes: result.notes?.[currentIndex] ?? [],
1428
+ currentIndex,
1429
+ slideCount,
1430
+ isOpen: showNotesDrawer,
1431
+ onClose: () => setShowNotesDrawer(false),
1432
+ isDark: themeName !== "light"
742
1433
  }
743
1434
  )
744
1435
  ]
@@ -748,39 +1439,41 @@ function SlideViewer({
748
1439
 
749
1440
  Object.defineProperty(exports, "defineTheme", {
750
1441
  enumerable: true,
751
- get: function () { return chunkN4Q6GUAQ_cjs.defineTheme; }
1442
+ get: function () { return chunkO4Y3VEOA_cjs.defineTheme; }
752
1443
  });
753
1444
  Object.defineProperty(exports, "extractMaxClicks", {
754
1445
  enumerable: true,
755
- get: function () { return chunkN4Q6GUAQ_cjs.extractMaxClicks; }
1446
+ get: function () { return chunkO4Y3VEOA_cjs.extractMaxClicks; }
756
1447
  });
757
1448
  Object.defineProperty(exports, "generateClickCss", {
758
1449
  enumerable: true,
759
- get: function () { return chunkN4Q6GUAQ_cjs.generateClickCss; }
1450
+ get: function () { return chunkO4Y3VEOA_cjs.generateClickCss; }
760
1451
  });
761
1452
  Object.defineProperty(exports, "postprocessClicks", {
762
1453
  enumerable: true,
763
- get: function () { return chunkN4Q6GUAQ_cjs.postprocessClicks; }
1454
+ get: function () { return chunkO4Y3VEOA_cjs.postprocessClicks; }
764
1455
  });
765
1456
  Object.defineProperty(exports, "preprocessClicks", {
766
1457
  enumerable: true,
767
- get: function () { return chunkN4Q6GUAQ_cjs.preprocessClicks; }
1458
+ get: function () { return chunkO4Y3VEOA_cjs.preprocessClicks; }
768
1459
  });
769
1460
  Object.defineProperty(exports, "renderSlide", {
770
1461
  enumerable: true,
771
- get: function () { return chunkN4Q6GUAQ_cjs.renderSlide; }
1462
+ get: function () { return chunkO4Y3VEOA_cjs.renderSlide; }
772
1463
  });
773
1464
  Object.defineProperty(exports, "renderSlideAsync", {
774
1465
  enumerable: true,
775
- get: function () { return chunkN4Q6GUAQ_cjs.renderSlideAsync; }
1466
+ get: function () { return chunkO4Y3VEOA_cjs.renderSlideAsync; }
776
1467
  });
777
1468
  Object.defineProperty(exports, "resolveTheme", {
778
1469
  enumerable: true,
779
- get: function () { return chunkN4Q6GUAQ_cjs.resolveTheme; }
1470
+ get: function () { return chunkO4Y3VEOA_cjs.resolveTheme; }
780
1471
  });
781
1472
  exports.DeckView = DeckView;
782
1473
  exports.NavigationBar = NavigationBar;
783
1474
  exports.PresentationView = PresentationView;
1475
+ exports.PresenterNotesDrawer = PresenterNotesDrawer;
1476
+ exports.SlideDesignOverlay = SlideDesignOverlay;
784
1477
  exports.SlideViewer = SlideViewer;
785
1478
  exports.ThemeSwitcher = ThemeSwitcher;
786
1479
  exports.useSlideState = useSlideState;