@kgalexander/mcreate 0.0.10 → 0.0.12

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/index.js CHANGED
@@ -267,7 +267,7 @@ var init_format = __esm({
267
267
  function propertyCardMockMjml(block, context) {
268
268
  const a = block.attributes;
269
269
  const trackingClasses = context.mode === "editing" ? getTrackingClasses(context.idx, "property-card") : "";
270
- const href = a["href"] || a["link"] || "#";
270
+ const href = a["href"] || "";
271
271
  const price = formatPrice(a["price"] || "$0");
272
272
  const address = a["address"] || "123 Main Street";
273
273
  const city = a["city"] || "City, ST 00000";
@@ -443,7 +443,7 @@ function propertyCardSingleTwoMockMjml(block, context) {
443
443
  const a = block.attributes;
444
444
  const trackingClasses = context.mode === "editing" ? getTrackingClasses(context.idx, "property-card-single-two") : "";
445
445
  const uniqueId = Math.random().toString(36).slice(2, 8);
446
- const href = a["href"] || a["link"] || "#";
446
+ const href = a["href"] || "";
447
447
  const price = formatPrice(a["price"] || "$0");
448
448
  const address = a["address"] || "123 Main Street";
449
449
  const city = a["city"] || "City, ST 00000";
@@ -644,7 +644,7 @@ var init_mock_property_singletwo = __esm({
644
644
  // src/render/Mockup/mock-property-triple-better.ts
645
645
  function renderCard(child, childIdx, context, uniqueId, borderRadius, imageHeight, border, fontFamily, textColor, backgroundColor, innerBorderRadius) {
646
646
  const attrs = child.attributes;
647
- const href = attrs["href"] || attrs["link"] || "#";
647
+ const href = attrs["href"] || "";
648
648
  const price = formatPrice(attrs["price"] || "$0");
649
649
  const beds = formatNumber(attrs["beds"] || "--");
650
650
  const baths = formatNumber(attrs["baths"] || "--");
@@ -948,7 +948,7 @@ function propertyCardTripleToMjml(block, context) {
948
948
  const cardNum = index + 1;
949
949
  const childAttrs = child.attributes || {};
950
950
  productionAttrs[`image-src-${cardNum}`] = childAttrs["image-src"] || "";
951
- productionAttrs[`href-${cardNum}`] = childAttrs["href"] || childAttrs["link"] || "#";
951
+ productionAttrs[`href-${cardNum}`] = childAttrs["href"] || "";
952
952
  productionAttrs[`price-${cardNum}`] = childAttrs["price"] || "$0";
953
953
  productionAttrs[`beds-${cardNum}`] = childAttrs["beds"] || "--";
954
954
  productionAttrs[`baths-${cardNum}`] = childAttrs["baths"] || "--";
@@ -1076,7 +1076,7 @@ function rootChildrenToMjml(children, parentContext) {
1076
1076
  function json2mjml(template, mode = "production", options = {}) {
1077
1077
  const showCompanyFooter = template.content[0]?.data?.value?.showCompanyFooter ?? true;
1078
1078
  const footerMutation = needsCompanyFooterMutation(template);
1079
- const needsProductionFilter = mode === "production" && options?.isPaid && !showCompanyFooter;
1079
+ const needsProductionFilter = mode === "production" && (options?.isPaidLevel ?? 0) >= 1 && !showCompanyFooter;
1080
1080
  if (footerMutation || needsProductionFilter) {
1081
1081
  template = typeof structuredClone !== "undefined" ? structuredClone(template) : JSON.parse(JSON.stringify(template));
1082
1082
  }
@@ -1129,7 +1129,6 @@ function json2mjml(template, mode = "production", options = {}) {
1129
1129
  p { margin: 0px 0px 0px 0px !important; }
1130
1130
  a { color: ${linkColor}; text-decoration: none; }
1131
1131
  a span { text-decoration: underline; }
1132
- p span { text-decoration: underline; }
1133
1132
 
1134
1133
  .property-details-separator {font-family:Arial,sans-serif;color:#C3C3C3;padding:0 4px 0 2px;}
1135
1134
 
@@ -1492,7 +1491,7 @@ function createPropertyCardElement(payload) {
1492
1491
  "beds": "--",
1493
1492
  "baths": "--",
1494
1493
  "sqft": "--",
1495
- "link": "#",
1494
+ "href": "",
1496
1495
  "status": "Empty",
1497
1496
  "is-status": "show",
1498
1497
  "status-color": "#B8B8B8",
@@ -1521,7 +1520,7 @@ function createPropertyCardSingleTwoElement(payload) {
1521
1520
  "beds": "--",
1522
1521
  "baths": "--",
1523
1522
  "sqft": "--",
1524
- "link": "#",
1523
+ "href": "",
1525
1524
  "status": "Empty",
1526
1525
  "is-status": "show",
1527
1526
  "status-color": "#B8B8B8",
@@ -1608,7 +1607,7 @@ function createPropertyCardTripleItemElement(payload) {
1608
1607
  data: { value: {} },
1609
1608
  attributes: {
1610
1609
  "image-src": payload?.attributes?.["image-src"] || "https://cornerstonepropertymgmt.com/wp-content/themes/cornerstone/assets/img/nofeaturedimage.jpg",
1611
- "link": payload?.attributes?.["link"] || "#",
1610
+ "href": payload?.attributes?.["href"] || "",
1612
1611
  "price": payload?.attributes?.["price"] || "$0",
1613
1612
  "beds": payload?.attributes?.["beds"] || "--",
1614
1613
  "baths": payload?.attributes?.["baths"] || "--",
@@ -1628,6 +1627,7 @@ function createPropertyCardTripleElement(payload) {
1628
1627
  "gap": "24px",
1629
1628
  "border-radius": "0px",
1630
1629
  "border": "1px solid #d1d1d5",
1630
+ "background-color": "#ffffff",
1631
1631
  "image-height": "102px",
1632
1632
  ...payload?.attributes
1633
1633
  },
@@ -2015,7 +2015,7 @@ function setValueAtPath(template, path, value) {
2015
2015
  (0, import_set.default)(template, lodashPath, value);
2016
2016
  }
2017
2017
  }
2018
- var import_zustand, import_immer, import_middleware, import_get2, import_set, import_cloneDeep, import_immer2, import_sonner, SECTION_INDEX_REGEX, MAX_TEMPLATE_SIZE, defaultTemplate, useEditorStore;
2018
+ var import_zustand, import_immer, import_middleware, import_get2, import_set, import_cloneDeep, import_immer2, import_sonner, SECTION_INDEX_REGEX, MAX_TEMPLATE_SIZE, defaultTemplate, MAX_PAID_LEVEL, useEditorStore;
2019
2019
  var init_editor = __esm({
2020
2020
  "src/core/editor/state/editor.ts"() {
2021
2021
  "use strict";
@@ -2040,6 +2040,7 @@ var init_editor = __esm({
2040
2040
  id: page.id || generateId()
2041
2041
  }))
2042
2042
  };
2043
+ MAX_PAID_LEVEL = 3;
2043
2044
  useEditorStore = (0, import_zustand.create)()(
2044
2045
  (0, import_middleware.devtools)(
2045
2046
  (0, import_immer.immer)((set) => ({
@@ -2068,12 +2069,15 @@ var init_editor = __esm({
2068
2069
  // Auto-save tracking
2069
2070
  lastSavedSnapshot: null,
2070
2071
  isSaving: false,
2072
+ // Subscription level (0 = free, 1-3 = paid tiers)
2073
+ isPaidLevel: 0,
2071
2074
  // Initialize store with external template (for npm package usage)
2072
- initializeWithTemplate: (templateId, template, onSave) => {
2075
+ initializeWithTemplate: (templateId, template, onSave, isPaidLevel) => {
2073
2076
  set((state) => {
2074
2077
  state.templateId = templateId;
2075
2078
  state.template = template;
2076
2079
  state.onSave = onSave ?? null;
2080
+ state.isPaidLevel = isPaidLevel ?? 0;
2077
2081
  state.templateSize = calculateTemplateSize(template);
2078
2082
  state.isAtSizeLimit = false;
2079
2083
  state.history = [(0, import_cloneDeep.default)(template)];
@@ -3613,579 +3617,6 @@ function getEditorStyles(isDragButtonHovered, textEditingIdx) {
3613
3617
  cssCache.set(cacheKey, css);
3614
3618
  return css;
3615
3619
  }
3616
- function ShadowDomRenderer({
3617
- html,
3618
- focusIdx,
3619
- hoverIdx,
3620
- dropIndicator,
3621
- dropTargetIdx,
3622
- isDragging = false,
3623
- isDragButtonHovered = false,
3624
- textEditingIdx = null,
3625
- showCompanyFooter = true,
3626
- onElementClick,
3627
- onElementHover,
3628
- onContentInput,
3629
- onEditingStart,
3630
- onEditingEnd,
3631
- onDragOver,
3632
- onDrop,
3633
- onDragLeave,
3634
- onSelectionRectChange,
3635
- onShadowRootRef,
3636
- onSlashCommand,
3637
- onSlashCommandClose,
3638
- onTextEditStart
3639
- }) {
3640
- const containerRef = (0, import_react.useRef)(null);
3641
- const shadowRootRef = (0, import_react.useRef)(null);
3642
- const rafRef = (0, import_react.useRef)(null);
3643
- const lastBlockNodeRef = (0, import_react.useRef)(null);
3644
- const callbackRefs = (0, import_react.useRef)({
3645
- onElementClick,
3646
- onElementHover,
3647
- onContentInput,
3648
- onEditingStart,
3649
- onEditingEnd,
3650
- onDragOver,
3651
- onDrop,
3652
- onDragLeave,
3653
- onSlashCommand,
3654
- onSlashCommandClose,
3655
- onTextEditStart
3656
- });
3657
- (0, import_react.useEffect)(() => {
3658
- callbackRefs.current = {
3659
- onElementClick,
3660
- onElementHover,
3661
- onContentInput,
3662
- onEditingStart,
3663
- onEditingEnd,
3664
- onDragOver,
3665
- onDrop,
3666
- onDragLeave,
3667
- onSlashCommand,
3668
- onSlashCommandClose,
3669
- onTextEditStart
3670
- };
3671
- });
3672
- (0, import_react.useEffect)(() => {
3673
- if (containerRef.current && !shadowRootRef.current) {
3674
- shadowRootRef.current = containerRef.current.attachShadow({ mode: "open" });
3675
- onShadowRootRef?.(shadowRootRef.current);
3676
- }
3677
- }, [onShadowRootRef]);
3678
- (0, import_react.useEffect)(() => {
3679
- if (!shadowRootRef.current) return;
3680
- const styleEl = document.createElement("style");
3681
- styleEl.id = "editor-styles";
3682
- styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
3683
- const contentWrapper = document.createElement("div");
3684
- contentWrapper.className = "shadow-content";
3685
- contentWrapper.innerHTML = html;
3686
- contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
3687
- contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
3688
- contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
3689
- const template = useEditorStore.getState().template;
3690
- const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
3691
- contentWrapper.style.setProperty("--link-color", linkColor2);
3692
- injectContentEditable(contentWrapper);
3693
- shadowRootRef.current.innerHTML = "";
3694
- shadowRootRef.current.appendChild(styleEl);
3695
- shadowRootRef.current.appendChild(contentWrapper);
3696
- }, [html]);
3697
- (0, import_react.useEffect)(() => {
3698
- if (!shadowRootRef.current) return;
3699
- const styleEl = shadowRootRef.current.querySelector("#editor-styles");
3700
- if (styleEl) {
3701
- styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
3702
- }
3703
- }, [isDragButtonHovered, textEditingIdx]);
3704
- (0, import_react.useEffect)(() => {
3705
- if (!shadowRootRef.current) return;
3706
- const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
3707
- if (contentWrapper) {
3708
- contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
3709
- contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
3710
- contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
3711
- const template = useEditorStore.getState().template;
3712
- const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
3713
- contentWrapper.style.setProperty("--link-color", linkColor2);
3714
- }
3715
- }, [isDragging, showCompanyFooter]);
3716
- const linkColor = useEditorStore((state) => state.template?.content?.[0]?.data?.value?.linkColor);
3717
- (0, import_react.useEffect)(() => {
3718
- if (!shadowRootRef.current) return;
3719
- const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
3720
- if (contentWrapper) {
3721
- contentWrapper.style.setProperty("--link-color", linkColor || "#0000ff");
3722
- }
3723
- }, [linkColor]);
3724
- (0, import_react.useEffect)(() => {
3725
- if (!shadowRootRef.current) return;
3726
- shadowRootRef.current.querySelectorAll(".is-selected").forEach((el) => {
3727
- el.classList.remove("is-selected");
3728
- });
3729
- shadowRootRef.current.querySelectorAll(".is-column-selected").forEach((el) => {
3730
- el.classList.remove("is-column-selected");
3731
- });
3732
- shadowRootRef.current.querySelectorAll(".is-card-selected").forEach((el) => {
3733
- el.classList.remove("is-card-selected");
3734
- });
3735
- if (focusIdx) {
3736
- const selectedEl = shadowRootRef.current.querySelector(
3737
- `.node-idx-${CSS.escape(focusIdx)}`
3738
- );
3739
- if (selectedEl) {
3740
- if (focusIdx !== "content") {
3741
- selectedEl.classList.add("is-selected");
3742
- }
3743
- if (selectedEl.classList.contains("node-type-section-column")) {
3744
- selectedEl.querySelectorAll(".node-type-column").forEach((col) => {
3745
- col.classList.add("is-column-selected");
3746
- });
3747
- } else if (selectedEl.classList.contains("node-type-column")) {
3748
- const parentSection = selectedEl.closest(".node-type-section-column");
3749
- if (parentSection) {
3750
- parentSection.querySelectorAll(".node-type-column").forEach((col) => {
3751
- col.classList.add("is-column-selected");
3752
- });
3753
- }
3754
- } else {
3755
- const parentSectionColumn = selectedEl.closest(".node-type-section-column");
3756
- if (parentSectionColumn) {
3757
- const parentColumn = selectedEl.closest(".node-type-column");
3758
- if (parentColumn) {
3759
- parentColumn.classList.add("is-column-selected");
3760
- }
3761
- }
3762
- }
3763
- if (selectedEl.classList.contains("node-type-section-property-triple")) {
3764
- selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
3765
- card.classList.add("is-card-selected");
3766
- });
3767
- } else if (selectedEl.classList.contains("node-type-property-card-triple")) {
3768
- selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
3769
- card.classList.add("is-card-selected");
3770
- });
3771
- } else if (selectedEl.classList.contains("node-type-property-card-triple-item")) {
3772
- selectedEl.classList.add("is-card-selected");
3773
- }
3774
- onSelectionRectChange?.(selectedEl.getBoundingClientRect());
3775
- } else {
3776
- onSelectionRectChange?.(null);
3777
- }
3778
- } else {
3779
- onSelectionRectChange?.(null);
3780
- }
3781
- }, [focusIdx, html, onSelectionRectChange]);
3782
- (0, import_react.useEffect)(() => {
3783
- if (!shadowRootRef.current) return;
3784
- shadowRootRef.current.querySelectorAll(".is-hovered").forEach((el) => {
3785
- el.classList.remove("is-hovered");
3786
- });
3787
- shadowRootRef.current.querySelectorAll(".is-column-hover-parent").forEach((el) => {
3788
- el.classList.remove("is-column-hover-parent");
3789
- });
3790
- if (hoverIdx && hoverIdx !== focusIdx && hoverIdx !== "content") {
3791
- const hoveredEl = shadowRootRef.current.querySelector(
3792
- `.node-idx-${CSS.escape(hoverIdx)}`
3793
- );
3794
- if (hoveredEl) {
3795
- hoveredEl.classList.add("is-hovered");
3796
- if (!hoveredEl.classList.contains("node-type-column")) {
3797
- const parentSectionColumn = hoveredEl.closest(".node-type-section-column");
3798
- if (parentSectionColumn) {
3799
- const parentColumn = hoveredEl.closest(".node-type-column");
3800
- if (parentColumn) {
3801
- parentColumn.classList.add("is-column-hover-parent");
3802
- }
3803
- }
3804
- }
3805
- }
3806
- }
3807
- }, [focusIdx, hoverIdx, html]);
3808
- (0, import_react.useEffect)(() => {
3809
- if (!shadowRootRef.current) return;
3810
- shadowRootRef.current.querySelectorAll(".is-drop-target").forEach((el) => {
3811
- el.classList.remove("is-drop-target");
3812
- });
3813
- if (dropTargetIdx && isDragging) {
3814
- const targetEl = shadowRootRef.current.querySelector(
3815
- `.node-idx-${CSS.escape(dropTargetIdx)}`
3816
- );
3817
- if (targetEl) {
3818
- targetEl.classList.add("is-drop-target");
3819
- }
3820
- }
3821
- }, [dropTargetIdx, isDragging]);
3822
- (0, import_react.useEffect)(() => {
3823
- const shadowRoot = shadowRootRef.current;
3824
- if (!shadowRoot) return;
3825
- const handleMouseDown = (e) => {
3826
- const target = e.target;
3827
- const mouseEvent = e;
3828
- if (target.closest('[contenteditable="true"]')) {
3829
- return;
3830
- }
3831
- const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3832
- if (blockNode && callbackRefs.current.onElementClick) {
3833
- const idx = getIdxFromElement(blockNode);
3834
- if (idx) {
3835
- callbackRefs.current.onElementClick(idx);
3836
- if (blockNode.classList.contains("node-type-text") && callbackRefs.current.onTextEditStart) {
3837
- const contentDiv = blockNode.querySelector("div");
3838
- if (contentDiv) {
3839
- const blockEl = blockNode;
3840
- const blockRect = blockEl.getBoundingClientRect();
3841
- const blockStyle = window.getComputedStyle(blockEl);
3842
- const content = contentDiv.innerHTML;
3843
- const clickX = mouseEvent.clientX;
3844
- const clickY = mouseEvent.clientY;
3845
- const contentStyle = window.getComputedStyle(contentDiv);
3846
- const styles = {
3847
- fontFamily: contentStyle.fontFamily,
3848
- fontSize: contentStyle.fontSize,
3849
- fontWeight: contentStyle.fontWeight,
3850
- lineHeight: contentStyle.lineHeight,
3851
- color: contentStyle.color,
3852
- textAlign: contentStyle.textAlign,
3853
- padding: blockStyle.padding,
3854
- // Use td's padding
3855
- // Additional text-rendering properties for exact matching
3856
- letterSpacing: contentStyle.letterSpacing,
3857
- wordSpacing: contentStyle.wordSpacing,
3858
- textRendering: contentStyle.textRendering,
3859
- fontStyle: contentStyle.fontStyle,
3860
- textDecoration: contentStyle.textDecoration,
3861
- textTransform: contentStyle.textTransform,
3862
- fontVariant: contentStyle.fontVariant,
3863
- textIndent: contentStyle.textIndent
3864
- };
3865
- callbackRefs.current.onTextEditStart?.(idx, blockRect.width, blockRect.height, clickX, clickY, content, styles);
3866
- }
3867
- }
3868
- }
3869
- }
3870
- };
3871
- const handleMouseOver = (e) => {
3872
- const target = e.target;
3873
- const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3874
- if (blockNode && callbackRefs.current.onElementHover) {
3875
- const idx = getIdxFromElement(blockNode);
3876
- callbackRefs.current.onElementHover(idx || null);
3877
- }
3878
- };
3879
- const handleMouseOut = (e) => {
3880
- const relatedTarget = e.relatedTarget;
3881
- if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
3882
- callbackRefs.current.onElementHover?.(null);
3883
- }
3884
- };
3885
- const handleInput = (e) => {
3886
- const target = e.target;
3887
- if (target.getAttribute("contenteditable") === "true" && callbackRefs.current.onContentInput) {
3888
- const contentIdx = target.getAttribute("data-content-idx");
3889
- if (contentIdx) {
3890
- const isRichText = target.closest(".node-type-text");
3891
- const content = isRichText ? target.innerHTML : target.textContent || "";
3892
- callbackRefs.current.onContentInput(contentIdx, content);
3893
- if (isRichText) {
3894
- if (isTextContentEmpty(target)) {
3895
- target.classList.add("is-empty");
3896
- } else {
3897
- target.classList.remove("is-empty");
3898
- }
3899
- }
3900
- }
3901
- }
3902
- };
3903
- const handleFocus = (e) => {
3904
- const target = e.target;
3905
- if (target.getAttribute("contenteditable") === "true") {
3906
- callbackRefs.current.onEditingStart?.();
3907
- const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3908
- if (blockNode && callbackRefs.current.onElementClick) {
3909
- const idx = getIdxFromElement(blockNode);
3910
- if (idx) {
3911
- callbackRefs.current.onElementClick(idx);
3912
- }
3913
- }
3914
- }
3915
- };
3916
- const handleBlur = (e) => {
3917
- const target = e.target;
3918
- if (target.getAttribute("contenteditable") === "true") {
3919
- callbackRefs.current.onEditingEnd?.();
3920
- }
3921
- };
3922
- const getParagraphInfo = (blockNode, mouseY) => {
3923
- if (!blockNode.classList.contains("node-type-text")) return void 0;
3924
- const contentDiv = blockNode.querySelector("div");
3925
- if (!contentDiv) return void 0;
3926
- const paragraphs = Array.from(contentDiv.querySelectorAll("p"));
3927
- if (paragraphs.length < 2) return void 0;
3928
- for (let i = 0; i < paragraphs.length; i++) {
3929
- const pRect = paragraphs[i].getBoundingClientRect();
3930
- if (mouseY >= pRect.top && mouseY <= pRect.bottom) {
3931
- const midpoint = pRect.top + pRect.height / 2;
3932
- return {
3933
- paragraphIndex: i,
3934
- totalParagraphs: paragraphs.length,
3935
- paragraphRect: pRect,
3936
- isAboveMidpoint: mouseY < midpoint
3937
- };
3938
- }
3939
- }
3940
- return void 0;
3941
- };
3942
- const handleDragOver = (e) => {
3943
- const dragEvent = e;
3944
- dragEvent.preventDefault();
3945
- const target = dragEvent.target;
3946
- const mouseY = dragEvent.clientY;
3947
- const mouseX = dragEvent.clientX;
3948
- let blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3949
- if (blockNode && getTypeFromElement(blockNode) === "social-item") {
3950
- blockNode = blockNode.parentElement?.closest(`.${EMAIL_BLOCK_CLASS_NAME}`) || null;
3951
- }
3952
- if (rafRef.current) return;
3953
- rafRef.current = requestAnimationFrame(() => {
3954
- rafRef.current = null;
3955
- if (blockNode === lastBlockNodeRef.current && blockNode) {
3956
- const dragInfo2 = {
3957
- idx: getIdxFromElement(blockNode),
3958
- type: getTypeFromElement(blockNode),
3959
- rect: blockNode.getBoundingClientRect(),
3960
- mouseY,
3961
- // Use captured value
3962
- mouseX,
3963
- // Use captured value
3964
- paragraphInfo: getParagraphInfo(blockNode, mouseY)
3965
- };
3966
- callbackRefs.current.onDragOver?.(dragEvent, dragInfo2);
3967
- return;
3968
- }
3969
- lastBlockNodeRef.current = blockNode;
3970
- const dragInfo = {
3971
- idx: blockNode ? getIdxFromElement(blockNode) : null,
3972
- type: blockNode ? getTypeFromElement(blockNode) : null,
3973
- rect: blockNode ? blockNode.getBoundingClientRect() : null,
3974
- mouseY,
3975
- // Use captured value
3976
- mouseX,
3977
- // Use captured value
3978
- paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
3979
- };
3980
- callbackRefs.current.onDragOver?.(dragEvent, dragInfo);
3981
- });
3982
- };
3983
- const handleDragLeave = (e) => {
3984
- const dragEvent = e;
3985
- const relatedTarget = dragEvent.relatedTarget;
3986
- if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
3987
- lastBlockNodeRef.current = null;
3988
- callbackRefs.current.onDragLeave?.();
3989
- }
3990
- };
3991
- const handleDrop = (e) => {
3992
- const dragEvent = e;
3993
- dragEvent.preventDefault();
3994
- const target = dragEvent.target;
3995
- const mouseY = dragEvent.clientY;
3996
- const mouseX = dragEvent.clientX;
3997
- const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3998
- const dragInfo = {
3999
- idx: blockNode ? getIdxFromElement(blockNode) : null,
4000
- type: blockNode ? getTypeFromElement(blockNode) : null,
4001
- rect: blockNode ? blockNode.getBoundingClientRect() : null,
4002
- mouseY,
4003
- mouseX,
4004
- paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
4005
- };
4006
- callbackRefs.current.onDrop?.(dragEvent, dragInfo);
4007
- };
4008
- const handleLinkClick = (e) => {
4009
- const target = e.target;
4010
- if (target.tagName === "A" || target.closest("a")) {
4011
- e.preventDefault();
4012
- }
4013
- };
4014
- const handleKeyDown = (e) => {
4015
- const keyEvent = e;
4016
- const target = keyEvent.target;
4017
- if (target.getAttribute("contenteditable") === "true" && target.closest(".node-type-button")) {
4018
- const isAtSizeLimit = useEditorStore.getState().isAtSizeLimit;
4019
- if (isAtSizeLimit) {
4020
- const allowedKeys = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Escape", "Tab"];
4021
- const isModifier = keyEvent.ctrlKey || keyEvent.metaKey || keyEvent.altKey;
4022
- const isAllowed = allowedKeys.includes(keyEvent.key) || isModifier;
4023
- if (!isAllowed && (keyEvent.key.length === 1 || keyEvent.key === "Enter")) {
4024
- keyEvent.preventDefault();
4025
- const now = Date.now();
4026
- if (now - lastSizeLimitToastTime > 2e3) {
4027
- lastSizeLimitToastTime = now;
4028
- import_sonner2.toast.error("Template size limit reached");
4029
- }
4030
- return;
4031
- }
4032
- }
4033
- return;
4034
- }
4035
- const relevantKeys = ["/", "Escape", " ", "Backspace"];
4036
- if (!relevantKeys.includes(keyEvent.key)) return;
4037
- if (target.getAttribute("contenteditable") !== "true") return;
4038
- if (!target.closest(".node-type-text")) return;
4039
- if (keyEvent.key === "Escape") {
4040
- callbackRefs.current.onSlashCommandClose?.();
4041
- return;
4042
- }
4043
- if (keyEvent.key === " ") {
4044
- callbackRefs.current.onSlashCommandClose?.();
4045
- return;
4046
- }
4047
- if (keyEvent.key === "Backspace") {
4048
- callbackRefs.current.onSlashCommandClose?.();
4049
- return;
4050
- }
4051
- if (keyEvent.key !== "/") return;
4052
- const selection = window.getSelection();
4053
- if (!selection || !selection.rangeCount) return;
4054
- const range = selection.getRangeAt(0);
4055
- const textContent = target.textContent || "";
4056
- const cursorOffset = range.startOffset;
4057
- const charBefore = cursorOffset > 0 ? textContent[cursorOffset - 1] : "";
4058
- if (cursorOffset > 0 && charBefore !== " " && charBefore !== "\xA0") {
4059
- return;
4060
- }
4061
- const targetRect = target.getBoundingClientRect();
4062
- const rangeRect = range.getBoundingClientRect();
4063
- const hasValidRangeRect = rangeRect.height > 0 || (rangeRect.top !== 0 || rangeRect.left !== 0);
4064
- const rect = hasValidRangeRect ? rangeRect : {
4065
- top: targetRect.top,
4066
- left: targetRect.left,
4067
- bottom: targetRect.top + 20,
4068
- right: targetRect.left,
4069
- width: 0,
4070
- height: 20,
4071
- x: targetRect.left,
4072
- y: targetRect.top,
4073
- toJSON: () => ({})
4074
- };
4075
- callbackRefs.current.onSlashCommand?.(rect);
4076
- };
4077
- const handlePaste = (e) => {
4078
- const clipboardEvent = e;
4079
- const target = clipboardEvent.target;
4080
- if (target.getAttribute("contenteditable") !== "true") return;
4081
- if (!target.closest(".node-type-button")) return;
4082
- const { templateSize } = useEditorStore.getState();
4083
- const pastedText = clipboardEvent.clipboardData?.getData("text/plain") || "";
4084
- const pasteSize = new Blob([pastedText]).size;
4085
- if (templateSize + pasteSize > MAX_TEMPLATE_SIZE) {
4086
- clipboardEvent.preventDefault();
4087
- const now = Date.now();
4088
- if (now - lastSizeLimitToastTime > 2e3) {
4089
- lastSizeLimitToastTime = now;
4090
- import_sonner2.toast.error("Template size limit reached");
4091
- }
4092
- }
4093
- };
4094
- shadowRoot.addEventListener("mousedown", handleMouseDown);
4095
- shadowRoot.addEventListener("click", handleLinkClick, true);
4096
- shadowRoot.addEventListener("mouseover", handleMouseOver);
4097
- shadowRoot.addEventListener("mouseout", handleMouseOut);
4098
- shadowRoot.addEventListener("input", handleInput);
4099
- shadowRoot.addEventListener("focusin", handleFocus);
4100
- shadowRoot.addEventListener("focusout", handleBlur);
4101
- shadowRoot.addEventListener("keydown", handleKeyDown);
4102
- shadowRoot.addEventListener("paste", handlePaste);
4103
- shadowRoot.addEventListener("dragover", handleDragOver);
4104
- shadowRoot.addEventListener("dragleave", handleDragLeave);
4105
- shadowRoot.addEventListener("drop", handleDrop);
4106
- return () => {
4107
- if (rafRef.current) {
4108
- cancelAnimationFrame(rafRef.current);
4109
- rafRef.current = null;
4110
- }
4111
- lastBlockNodeRef.current = null;
4112
- shadowRoot.removeEventListener("mousedown", handleMouseDown);
4113
- shadowRoot.removeEventListener("click", handleLinkClick, true);
4114
- shadowRoot.removeEventListener("mouseover", handleMouseOver);
4115
- shadowRoot.removeEventListener("mouseout", handleMouseOut);
4116
- shadowRoot.removeEventListener("input", handleInput);
4117
- shadowRoot.removeEventListener("focusin", handleFocus);
4118
- shadowRoot.removeEventListener("focusout", handleBlur);
4119
- shadowRoot.removeEventListener("keydown", handleKeyDown);
4120
- shadowRoot.removeEventListener("paste", handlePaste);
4121
- shadowRoot.removeEventListener("dragover", handleDragOver);
4122
- shadowRoot.removeEventListener("dragleave", handleDragLeave);
4123
- shadowRoot.removeEventListener("drop", handleDrop);
4124
- };
4125
- }, []);
4126
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
4127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4128
- "div",
4129
- {
4130
- ref: containerRef,
4131
- className: "shadow-dom-renderer",
4132
- style: {
4133
- width: "100%",
4134
- height: "100%",
4135
- overflow: "auto"
4136
- }
4137
- }
4138
- ),
4139
- dropIndicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4140
- "div",
4141
- {
4142
- className: "drop-indicator-line",
4143
- style: {
4144
- position: "fixed",
4145
- // Horizontal indicator (existing default)
4146
- ...(dropIndicator.orientation === "horizontal" || !dropIndicator.orientation) && {
4147
- top: dropIndicator.top - 1.5,
4148
- left: dropIndicator.left,
4149
- width: dropIndicator.width,
4150
- height: 3
4151
- },
4152
- // Vertical indicator (NEW)
4153
- ...dropIndicator.orientation === "vertical" && {
4154
- top: dropIndicator.top,
4155
- left: dropIndicator.left - 1.5,
4156
- width: 3,
4157
- height: dropIndicator.height
4158
- },
4159
- backgroundColor: isDragging ? "rgb(147, 51, 234)" : "#3b82f6",
4160
- borderRadius: 2,
4161
- pointerEvents: "none",
4162
- zIndex: 9999,
4163
- boxShadow: isDragging ? "0 0 4px rgba(147, 51, 234, 0.5)" : "0 0 4px rgba(59, 130, 246, 0.5)"
4164
- },
4165
- children: (dropIndicator.isNewSection || dropIndicator.isSplitSection) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4166
- "div",
4167
- {
4168
- style: {
4169
- position: "absolute",
4170
- top: "50%",
4171
- left: "50%",
4172
- transform: "translate(-50%, -50%)",
4173
- backgroundColor: "rgb(147, 51, 234)",
4174
- color: "white",
4175
- fontSize: 12,
4176
- fontWeight: 600,
4177
- padding: "4px 10px",
4178
- borderRadius: 9999,
4179
- whiteSpace: "nowrap",
4180
- pointerEvents: "none"
4181
- },
4182
- children: dropIndicator.isNewSection ? "New section" : "Split section"
4183
- }
4184
- )
4185
- }
4186
- )
4187
- ] });
4188
- }
4189
3620
  function getIdxFromElement(element) {
4190
3621
  const classList = element.classList;
4191
3622
  for (let i = 0; i < classList.length; i++) {
@@ -4243,7 +3674,7 @@ function injectContentEditable(container) {
4243
3674
  }
4244
3675
  });
4245
3676
  }
4246
- var import_react, import_sonner2, import_jsx_runtime, lastSizeLimitToastTime, cssCache;
3677
+ var import_react, import_sonner2, import_jsx_runtime, lastSizeLimitToastTime, cssCache, ShadowDomRenderer;
4247
3678
  var init_ShadowDomRenderer = __esm({
4248
3679
  "src/core/editor/components/ShadowDomRenderer.tsx"() {
4249
3680
  "use strict";
@@ -4256,6 +3687,591 @@ var init_ShadowDomRenderer = __esm({
4256
3687
  import_jsx_runtime = require("react/jsx-runtime");
4257
3688
  lastSizeLimitToastTime = 0;
4258
3689
  cssCache = /* @__PURE__ */ new Map();
3690
+ ShadowDomRenderer = (0, import_react.memo)(function ShadowDomRenderer2({
3691
+ html,
3692
+ focusIdx,
3693
+ hoverIdx,
3694
+ dropIndicator,
3695
+ dropTargetIdx,
3696
+ isDragging = false,
3697
+ isDragButtonHovered = false,
3698
+ textEditingIdx = null,
3699
+ showCompanyFooter = true,
3700
+ onElementClick,
3701
+ onElementHover,
3702
+ onContentInput,
3703
+ onEditingStart,
3704
+ onEditingEnd,
3705
+ onDragOver,
3706
+ onDrop,
3707
+ onDragLeave,
3708
+ onSelectionRectChange,
3709
+ onShadowRootRef,
3710
+ onSlashCommand,
3711
+ onSlashCommandClose,
3712
+ onTextEditStart
3713
+ }) {
3714
+ const containerRef = (0, import_react.useRef)(null);
3715
+ const shadowRootRef = (0, import_react.useRef)(null);
3716
+ const rafRef = (0, import_react.useRef)(null);
3717
+ const lastBlockNodeRef = (0, import_react.useRef)(null);
3718
+ const hoverRafRef = (0, import_react.useRef)(null);
3719
+ const lastHoverIdxRef = (0, import_react.useRef)(null);
3720
+ const callbackRefs = (0, import_react.useRef)({
3721
+ onElementClick,
3722
+ onElementHover,
3723
+ onContentInput,
3724
+ onEditingStart,
3725
+ onEditingEnd,
3726
+ onDragOver,
3727
+ onDrop,
3728
+ onDragLeave,
3729
+ onSlashCommand,
3730
+ onSlashCommandClose,
3731
+ onTextEditStart
3732
+ });
3733
+ (0, import_react.useEffect)(() => {
3734
+ callbackRefs.current = {
3735
+ onElementClick,
3736
+ onElementHover,
3737
+ onContentInput,
3738
+ onEditingStart,
3739
+ onEditingEnd,
3740
+ onDragOver,
3741
+ onDrop,
3742
+ onDragLeave,
3743
+ onSlashCommand,
3744
+ onSlashCommandClose,
3745
+ onTextEditStart
3746
+ };
3747
+ });
3748
+ (0, import_react.useEffect)(() => {
3749
+ if (containerRef.current && !shadowRootRef.current) {
3750
+ shadowRootRef.current = containerRef.current.attachShadow({ mode: "open" });
3751
+ onShadowRootRef?.(shadowRootRef.current);
3752
+ }
3753
+ }, [onShadowRootRef]);
3754
+ (0, import_react.useEffect)(() => {
3755
+ if (!shadowRootRef.current) return;
3756
+ const styleEl = document.createElement("style");
3757
+ styleEl.id = "editor-styles";
3758
+ styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
3759
+ const contentWrapper = document.createElement("div");
3760
+ contentWrapper.className = "shadow-content";
3761
+ contentWrapper.innerHTML = html;
3762
+ contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
3763
+ contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
3764
+ contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
3765
+ const template = useEditorStore.getState().template;
3766
+ const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
3767
+ contentWrapper.style.setProperty("--link-color", linkColor2);
3768
+ injectContentEditable(contentWrapper);
3769
+ shadowRootRef.current.innerHTML = "";
3770
+ shadowRootRef.current.appendChild(styleEl);
3771
+ shadowRootRef.current.appendChild(contentWrapper);
3772
+ }, [html]);
3773
+ (0, import_react.useEffect)(() => {
3774
+ if (!shadowRootRef.current) return;
3775
+ const styleEl = shadowRootRef.current.querySelector("#editor-styles");
3776
+ if (styleEl) {
3777
+ styleEl.textContent = getEditorStyles(isDragButtonHovered, textEditingIdx);
3778
+ }
3779
+ }, [isDragButtonHovered, textEditingIdx]);
3780
+ (0, import_react.useEffect)(() => {
3781
+ if (!shadowRootRef.current) return;
3782
+ const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
3783
+ if (contentWrapper) {
3784
+ contentWrapper.style.setProperty("--outline-color", isDragging ? "rgb(147, 51, 234)" : "rgb(59, 130, 246)");
3785
+ contentWrapper.style.setProperty("--outline-color-hover", isDragging ? "rgba(147, 51, 234, 0.5)" : "rgba(59, 130, 246, 0.5)");
3786
+ contentWrapper.style.setProperty("--company-footer-opacity", showCompanyFooter ? "1" : "0.25");
3787
+ const template = useEditorStore.getState().template;
3788
+ const linkColor2 = template?.content?.[0]?.data?.value?.linkColor || "#0000ff";
3789
+ contentWrapper.style.setProperty("--link-color", linkColor2);
3790
+ }
3791
+ }, [isDragging, showCompanyFooter]);
3792
+ const linkColor = useEditorStore((state) => state.template?.content?.[0]?.data?.value?.linkColor);
3793
+ (0, import_react.useEffect)(() => {
3794
+ if (!shadowRootRef.current) return;
3795
+ const contentWrapper = shadowRootRef.current.querySelector(".shadow-content");
3796
+ if (contentWrapper) {
3797
+ contentWrapper.style.setProperty("--link-color", linkColor || "#0000ff");
3798
+ }
3799
+ }, [linkColor]);
3800
+ (0, import_react.useEffect)(() => {
3801
+ if (!shadowRootRef.current) return;
3802
+ shadowRootRef.current.querySelectorAll(".is-selected").forEach((el) => {
3803
+ el.classList.remove("is-selected");
3804
+ });
3805
+ shadowRootRef.current.querySelectorAll(".is-column-selected").forEach((el) => {
3806
+ el.classList.remove("is-column-selected");
3807
+ });
3808
+ shadowRootRef.current.querySelectorAll(".is-card-selected").forEach((el) => {
3809
+ el.classList.remove("is-card-selected");
3810
+ });
3811
+ if (focusIdx) {
3812
+ const selectedEl = shadowRootRef.current.querySelector(
3813
+ `.node-idx-${CSS.escape(focusIdx)}`
3814
+ );
3815
+ if (selectedEl) {
3816
+ if (focusIdx !== "content") {
3817
+ selectedEl.classList.add("is-selected");
3818
+ }
3819
+ if (selectedEl.classList.contains("node-type-section-column")) {
3820
+ selectedEl.querySelectorAll(".node-type-column").forEach((col) => {
3821
+ col.classList.add("is-column-selected");
3822
+ });
3823
+ } else if (selectedEl.classList.contains("node-type-column")) {
3824
+ const parentSection = selectedEl.closest(".node-type-section-column");
3825
+ if (parentSection) {
3826
+ parentSection.querySelectorAll(".node-type-column").forEach((col) => {
3827
+ col.classList.add("is-column-selected");
3828
+ });
3829
+ }
3830
+ } else {
3831
+ const parentSectionColumn = selectedEl.closest(".node-type-section-column");
3832
+ if (parentSectionColumn) {
3833
+ const parentColumn = selectedEl.closest(".node-type-column");
3834
+ if (parentColumn) {
3835
+ parentColumn.classList.add("is-column-selected");
3836
+ }
3837
+ }
3838
+ }
3839
+ if (selectedEl.classList.contains("node-type-section-property-triple")) {
3840
+ selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
3841
+ card.classList.add("is-card-selected");
3842
+ });
3843
+ } else if (selectedEl.classList.contains("node-type-property-card-triple")) {
3844
+ selectedEl.querySelectorAll(".node-type-property-card-triple-item").forEach((card) => {
3845
+ card.classList.add("is-card-selected");
3846
+ });
3847
+ } else if (selectedEl.classList.contains("node-type-property-card-triple-item")) {
3848
+ selectedEl.classList.add("is-card-selected");
3849
+ }
3850
+ onSelectionRectChange?.(selectedEl.getBoundingClientRect());
3851
+ } else {
3852
+ onSelectionRectChange?.(null);
3853
+ }
3854
+ } else {
3855
+ onSelectionRectChange?.(null);
3856
+ }
3857
+ }, [focusIdx, html, onSelectionRectChange]);
3858
+ (0, import_react.useEffect)(() => {
3859
+ if (!shadowRootRef.current) return;
3860
+ shadowRootRef.current.querySelectorAll(".is-hovered").forEach((el) => {
3861
+ el.classList.remove("is-hovered");
3862
+ });
3863
+ shadowRootRef.current.querySelectorAll(".is-column-hover-parent").forEach((el) => {
3864
+ el.classList.remove("is-column-hover-parent");
3865
+ });
3866
+ if (hoverIdx && hoverIdx !== focusIdx && hoverIdx !== "content") {
3867
+ const hoveredEl = shadowRootRef.current.querySelector(
3868
+ `.node-idx-${CSS.escape(hoverIdx)}`
3869
+ );
3870
+ if (hoveredEl) {
3871
+ hoveredEl.classList.add("is-hovered");
3872
+ if (!hoveredEl.classList.contains("node-type-column")) {
3873
+ const parentSectionColumn = hoveredEl.closest(".node-type-section-column");
3874
+ if (parentSectionColumn) {
3875
+ const parentColumn = hoveredEl.closest(".node-type-column");
3876
+ if (parentColumn) {
3877
+ parentColumn.classList.add("is-column-hover-parent");
3878
+ }
3879
+ }
3880
+ }
3881
+ }
3882
+ }
3883
+ }, [focusIdx, hoverIdx, html]);
3884
+ (0, import_react.useEffect)(() => {
3885
+ if (!shadowRootRef.current) return;
3886
+ shadowRootRef.current.querySelectorAll(".is-drop-target").forEach((el) => {
3887
+ el.classList.remove("is-drop-target");
3888
+ });
3889
+ if (dropTargetIdx && isDragging) {
3890
+ const targetEl = shadowRootRef.current.querySelector(
3891
+ `.node-idx-${CSS.escape(dropTargetIdx)}`
3892
+ );
3893
+ if (targetEl) {
3894
+ targetEl.classList.add("is-drop-target");
3895
+ }
3896
+ }
3897
+ }, [dropTargetIdx, isDragging]);
3898
+ (0, import_react.useEffect)(() => {
3899
+ const shadowRoot = shadowRootRef.current;
3900
+ if (!shadowRoot) return;
3901
+ const handleMouseDown = (e) => {
3902
+ const target = e.target;
3903
+ const mouseEvent = e;
3904
+ if (target.closest('[contenteditable="true"]')) {
3905
+ return;
3906
+ }
3907
+ const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3908
+ if (blockNode && callbackRefs.current.onElementClick) {
3909
+ const idx = getIdxFromElement(blockNode);
3910
+ if (idx) {
3911
+ callbackRefs.current.onElementClick(idx);
3912
+ if (blockNode.classList.contains("node-type-text") && callbackRefs.current.onTextEditStart) {
3913
+ const contentDiv = blockNode.querySelector("div");
3914
+ if (contentDiv) {
3915
+ const blockEl = blockNode;
3916
+ const blockRect = blockEl.getBoundingClientRect();
3917
+ const blockStyle = window.getComputedStyle(blockEl);
3918
+ const content = contentDiv.innerHTML;
3919
+ const clickX = mouseEvent.clientX;
3920
+ const clickY = mouseEvent.clientY;
3921
+ const contentStyle = window.getComputedStyle(contentDiv);
3922
+ const styles = {
3923
+ fontFamily: contentStyle.fontFamily,
3924
+ fontSize: contentStyle.fontSize,
3925
+ fontWeight: contentStyle.fontWeight,
3926
+ lineHeight: contentStyle.lineHeight,
3927
+ color: contentStyle.color,
3928
+ textAlign: contentStyle.textAlign,
3929
+ padding: blockStyle.padding,
3930
+ // Use td's padding
3931
+ // Additional text-rendering properties for exact matching
3932
+ letterSpacing: contentStyle.letterSpacing,
3933
+ wordSpacing: contentStyle.wordSpacing,
3934
+ textRendering: contentStyle.textRendering,
3935
+ fontStyle: contentStyle.fontStyle,
3936
+ textDecoration: contentStyle.textDecoration,
3937
+ textTransform: contentStyle.textTransform,
3938
+ fontVariant: contentStyle.fontVariant,
3939
+ textIndent: contentStyle.textIndent
3940
+ };
3941
+ callbackRefs.current.onTextEditStart?.(idx, blockRect.width, blockRect.height, clickX, clickY, content, styles);
3942
+ }
3943
+ }
3944
+ }
3945
+ }
3946
+ };
3947
+ const handleMouseOver = (e) => {
3948
+ const target = e.target;
3949
+ const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3950
+ const newIdx = blockNode ? getIdxFromElement(blockNode) : null;
3951
+ if (newIdx === lastHoverIdxRef.current) return;
3952
+ if (hoverRafRef.current) return;
3953
+ hoverRafRef.current = requestAnimationFrame(() => {
3954
+ hoverRafRef.current = null;
3955
+ lastHoverIdxRef.current = newIdx;
3956
+ callbackRefs.current.onElementHover?.(newIdx);
3957
+ });
3958
+ };
3959
+ const handleMouseOut = (e) => {
3960
+ const relatedTarget = e.relatedTarget;
3961
+ if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
3962
+ lastHoverIdxRef.current = null;
3963
+ callbackRefs.current.onElementHover?.(null);
3964
+ }
3965
+ };
3966
+ const handleInput = (e) => {
3967
+ const target = e.target;
3968
+ if (target.getAttribute("contenteditable") === "true" && callbackRefs.current.onContentInput) {
3969
+ const contentIdx = target.getAttribute("data-content-idx");
3970
+ if (contentIdx) {
3971
+ const isRichText = target.closest(".node-type-text");
3972
+ const content = isRichText ? target.innerHTML : target.textContent || "";
3973
+ callbackRefs.current.onContentInput(contentIdx, content);
3974
+ if (isRichText) {
3975
+ if (isTextContentEmpty(target)) {
3976
+ target.classList.add("is-empty");
3977
+ } else {
3978
+ target.classList.remove("is-empty");
3979
+ }
3980
+ }
3981
+ }
3982
+ }
3983
+ };
3984
+ const handleFocus = (e) => {
3985
+ const target = e.target;
3986
+ if (target.getAttribute("contenteditable") === "true") {
3987
+ callbackRefs.current.onEditingStart?.();
3988
+ const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
3989
+ if (blockNode && callbackRefs.current.onElementClick) {
3990
+ const idx = getIdxFromElement(blockNode);
3991
+ if (idx) {
3992
+ callbackRefs.current.onElementClick(idx);
3993
+ }
3994
+ }
3995
+ }
3996
+ };
3997
+ const handleBlur = (e) => {
3998
+ const target = e.target;
3999
+ if (target.getAttribute("contenteditable") === "true") {
4000
+ callbackRefs.current.onEditingEnd?.();
4001
+ }
4002
+ };
4003
+ const getParagraphInfo = (blockNode, mouseY) => {
4004
+ if (!blockNode.classList.contains("node-type-text")) return void 0;
4005
+ const contentDiv = blockNode.querySelector("div");
4006
+ if (!contentDiv) return void 0;
4007
+ const paragraphs = Array.from(contentDiv.querySelectorAll("p"));
4008
+ if (paragraphs.length < 2) return void 0;
4009
+ for (let i = 0; i < paragraphs.length; i++) {
4010
+ const pRect = paragraphs[i].getBoundingClientRect();
4011
+ if (mouseY >= pRect.top && mouseY <= pRect.bottom) {
4012
+ const midpoint = pRect.top + pRect.height / 2;
4013
+ return {
4014
+ paragraphIndex: i,
4015
+ totalParagraphs: paragraphs.length,
4016
+ paragraphRect: pRect,
4017
+ isAboveMidpoint: mouseY < midpoint
4018
+ };
4019
+ }
4020
+ }
4021
+ return void 0;
4022
+ };
4023
+ const handleDragOver = (e) => {
4024
+ const dragEvent = e;
4025
+ dragEvent.preventDefault();
4026
+ const target = dragEvent.target;
4027
+ const mouseY = dragEvent.clientY;
4028
+ const mouseX = dragEvent.clientX;
4029
+ let blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
4030
+ if (blockNode && getTypeFromElement(blockNode) === "social-item") {
4031
+ blockNode = blockNode.parentElement?.closest(`.${EMAIL_BLOCK_CLASS_NAME}`) || null;
4032
+ }
4033
+ if (rafRef.current) return;
4034
+ rafRef.current = requestAnimationFrame(() => {
4035
+ rafRef.current = null;
4036
+ if (blockNode === lastBlockNodeRef.current && blockNode) {
4037
+ const dragInfo2 = {
4038
+ idx: getIdxFromElement(blockNode),
4039
+ type: getTypeFromElement(blockNode),
4040
+ rect: blockNode.getBoundingClientRect(),
4041
+ mouseY,
4042
+ // Use captured value
4043
+ mouseX,
4044
+ // Use captured value
4045
+ paragraphInfo: getParagraphInfo(blockNode, mouseY)
4046
+ };
4047
+ callbackRefs.current.onDragOver?.(dragEvent, dragInfo2);
4048
+ return;
4049
+ }
4050
+ lastBlockNodeRef.current = blockNode;
4051
+ const dragInfo = {
4052
+ idx: blockNode ? getIdxFromElement(blockNode) : null,
4053
+ type: blockNode ? getTypeFromElement(blockNode) : null,
4054
+ rect: blockNode ? blockNode.getBoundingClientRect() : null,
4055
+ mouseY,
4056
+ // Use captured value
4057
+ mouseX,
4058
+ // Use captured value
4059
+ paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
4060
+ };
4061
+ callbackRefs.current.onDragOver?.(dragEvent, dragInfo);
4062
+ });
4063
+ };
4064
+ const handleDragLeave = (e) => {
4065
+ const dragEvent = e;
4066
+ const relatedTarget = dragEvent.relatedTarget;
4067
+ if (!relatedTarget || !shadowRoot.contains(relatedTarget)) {
4068
+ lastBlockNodeRef.current = null;
4069
+ callbackRefs.current.onDragLeave?.();
4070
+ }
4071
+ };
4072
+ const handleDrop = (e) => {
4073
+ const dragEvent = e;
4074
+ dragEvent.preventDefault();
4075
+ const target = dragEvent.target;
4076
+ const mouseY = dragEvent.clientY;
4077
+ const mouseX = dragEvent.clientX;
4078
+ const blockNode = target.closest(`.${EMAIL_BLOCK_CLASS_NAME}`);
4079
+ const dragInfo = {
4080
+ idx: blockNode ? getIdxFromElement(blockNode) : null,
4081
+ type: blockNode ? getTypeFromElement(blockNode) : null,
4082
+ rect: blockNode ? blockNode.getBoundingClientRect() : null,
4083
+ mouseY,
4084
+ mouseX,
4085
+ paragraphInfo: blockNode ? getParagraphInfo(blockNode, mouseY) : void 0
4086
+ };
4087
+ callbackRefs.current.onDrop?.(dragEvent, dragInfo);
4088
+ };
4089
+ const handleLinkClick = (e) => {
4090
+ const target = e.target;
4091
+ if (target.tagName === "A" || target.closest("a")) {
4092
+ e.preventDefault();
4093
+ }
4094
+ };
4095
+ const handleKeyDown = (e) => {
4096
+ const keyEvent = e;
4097
+ const target = keyEvent.target;
4098
+ if (target.getAttribute("contenteditable") === "true" && target.closest(".node-type-button")) {
4099
+ const isAtSizeLimit = useEditorStore.getState().isAtSizeLimit;
4100
+ if (isAtSizeLimit) {
4101
+ const allowedKeys = ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Escape", "Tab"];
4102
+ const isModifier = keyEvent.ctrlKey || keyEvent.metaKey || keyEvent.altKey;
4103
+ const isAllowed = allowedKeys.includes(keyEvent.key) || isModifier;
4104
+ if (!isAllowed && (keyEvent.key.length === 1 || keyEvent.key === "Enter")) {
4105
+ keyEvent.preventDefault();
4106
+ const now = Date.now();
4107
+ if (now - lastSizeLimitToastTime > 2e3) {
4108
+ lastSizeLimitToastTime = now;
4109
+ import_sonner2.toast.error("Template size limit reached");
4110
+ }
4111
+ return;
4112
+ }
4113
+ }
4114
+ return;
4115
+ }
4116
+ const relevantKeys = ["/", "Escape", " ", "Backspace"];
4117
+ if (!relevantKeys.includes(keyEvent.key)) return;
4118
+ if (target.getAttribute("contenteditable") !== "true") return;
4119
+ if (!target.closest(".node-type-text")) return;
4120
+ if (keyEvent.key === "Escape") {
4121
+ callbackRefs.current.onSlashCommandClose?.();
4122
+ return;
4123
+ }
4124
+ if (keyEvent.key === " ") {
4125
+ callbackRefs.current.onSlashCommandClose?.();
4126
+ return;
4127
+ }
4128
+ if (keyEvent.key === "Backspace") {
4129
+ callbackRefs.current.onSlashCommandClose?.();
4130
+ return;
4131
+ }
4132
+ if (keyEvent.key !== "/") return;
4133
+ const selection = window.getSelection();
4134
+ if (!selection || !selection.rangeCount) return;
4135
+ const range = selection.getRangeAt(0);
4136
+ const textContent = target.textContent || "";
4137
+ const cursorOffset = range.startOffset;
4138
+ const charBefore = cursorOffset > 0 ? textContent[cursorOffset - 1] : "";
4139
+ if (cursorOffset > 0 && charBefore !== " " && charBefore !== "\xA0") {
4140
+ return;
4141
+ }
4142
+ const targetRect = target.getBoundingClientRect();
4143
+ const rangeRect = range.getBoundingClientRect();
4144
+ const hasValidRangeRect = rangeRect.height > 0 || (rangeRect.top !== 0 || rangeRect.left !== 0);
4145
+ const rect = hasValidRangeRect ? rangeRect : {
4146
+ top: targetRect.top,
4147
+ left: targetRect.left,
4148
+ bottom: targetRect.top + 20,
4149
+ right: targetRect.left,
4150
+ width: 0,
4151
+ height: 20,
4152
+ x: targetRect.left,
4153
+ y: targetRect.top,
4154
+ toJSON: () => ({})
4155
+ };
4156
+ callbackRefs.current.onSlashCommand?.(rect);
4157
+ };
4158
+ const handlePaste = (e) => {
4159
+ const clipboardEvent = e;
4160
+ const target = clipboardEvent.target;
4161
+ if (target.getAttribute("contenteditable") !== "true") return;
4162
+ if (!target.closest(".node-type-button")) return;
4163
+ const { templateSize } = useEditorStore.getState();
4164
+ const pastedText = clipboardEvent.clipboardData?.getData("text/plain") || "";
4165
+ const pasteSize = new Blob([pastedText]).size;
4166
+ if (templateSize + pasteSize > MAX_TEMPLATE_SIZE) {
4167
+ clipboardEvent.preventDefault();
4168
+ const now = Date.now();
4169
+ if (now - lastSizeLimitToastTime > 2e3) {
4170
+ lastSizeLimitToastTime = now;
4171
+ import_sonner2.toast.error("Template size limit reached");
4172
+ }
4173
+ }
4174
+ };
4175
+ shadowRoot.addEventListener("mousedown", handleMouseDown);
4176
+ shadowRoot.addEventListener("click", handleLinkClick, true);
4177
+ shadowRoot.addEventListener("mouseover", handleMouseOver);
4178
+ shadowRoot.addEventListener("mouseout", handleMouseOut);
4179
+ shadowRoot.addEventListener("input", handleInput);
4180
+ shadowRoot.addEventListener("focusin", handleFocus);
4181
+ shadowRoot.addEventListener("focusout", handleBlur);
4182
+ shadowRoot.addEventListener("keydown", handleKeyDown);
4183
+ shadowRoot.addEventListener("paste", handlePaste);
4184
+ shadowRoot.addEventListener("dragover", handleDragOver);
4185
+ shadowRoot.addEventListener("dragleave", handleDragLeave);
4186
+ shadowRoot.addEventListener("drop", handleDrop);
4187
+ return () => {
4188
+ if (rafRef.current) {
4189
+ cancelAnimationFrame(rafRef.current);
4190
+ rafRef.current = null;
4191
+ }
4192
+ lastBlockNodeRef.current = null;
4193
+ if (hoverRafRef.current) {
4194
+ cancelAnimationFrame(hoverRafRef.current);
4195
+ hoverRafRef.current = null;
4196
+ }
4197
+ lastHoverIdxRef.current = null;
4198
+ shadowRoot.removeEventListener("mousedown", handleMouseDown);
4199
+ shadowRoot.removeEventListener("click", handleLinkClick, true);
4200
+ shadowRoot.removeEventListener("mouseover", handleMouseOver);
4201
+ shadowRoot.removeEventListener("mouseout", handleMouseOut);
4202
+ shadowRoot.removeEventListener("input", handleInput);
4203
+ shadowRoot.removeEventListener("focusin", handleFocus);
4204
+ shadowRoot.removeEventListener("focusout", handleBlur);
4205
+ shadowRoot.removeEventListener("keydown", handleKeyDown);
4206
+ shadowRoot.removeEventListener("paste", handlePaste);
4207
+ shadowRoot.removeEventListener("dragover", handleDragOver);
4208
+ shadowRoot.removeEventListener("dragleave", handleDragLeave);
4209
+ shadowRoot.removeEventListener("drop", handleDrop);
4210
+ };
4211
+ }, []);
4212
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
4213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4214
+ "div",
4215
+ {
4216
+ ref: containerRef,
4217
+ className: "shadow-dom-renderer",
4218
+ style: {
4219
+ width: "100%",
4220
+ height: "100%",
4221
+ overflow: "auto"
4222
+ }
4223
+ }
4224
+ ),
4225
+ dropIndicator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4226
+ "div",
4227
+ {
4228
+ className: "drop-indicator-line",
4229
+ style: {
4230
+ position: "fixed",
4231
+ // Horizontal indicator (existing default)
4232
+ ...(dropIndicator.orientation === "horizontal" || !dropIndicator.orientation) && {
4233
+ top: dropIndicator.top - 1.5,
4234
+ left: dropIndicator.left,
4235
+ width: dropIndicator.width,
4236
+ height: 3
4237
+ },
4238
+ // Vertical indicator (NEW)
4239
+ ...dropIndicator.orientation === "vertical" && {
4240
+ top: dropIndicator.top,
4241
+ left: dropIndicator.left - 1.5,
4242
+ width: 3,
4243
+ height: dropIndicator.height
4244
+ },
4245
+ backgroundColor: isDragging ? "rgb(147, 51, 234)" : "#3b82f6",
4246
+ borderRadius: 2,
4247
+ pointerEvents: "none",
4248
+ zIndex: 9999,
4249
+ boxShadow: isDragging ? "0 0 4px rgba(147, 51, 234, 0.5)" : "0 0 4px rgba(59, 130, 246, 0.5)"
4250
+ },
4251
+ children: (dropIndicator.isNewSection || dropIndicator.isSplitSection) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4252
+ "div",
4253
+ {
4254
+ style: {
4255
+ position: "absolute",
4256
+ top: "50%",
4257
+ left: "50%",
4258
+ transform: "translate(-50%, -50%)",
4259
+ backgroundColor: "rgb(147, 51, 234)",
4260
+ color: "white",
4261
+ fontSize: 12,
4262
+ fontWeight: 600,
4263
+ padding: "4px 10px",
4264
+ borderRadius: 9999,
4265
+ whiteSpace: "nowrap",
4266
+ pointerEvents: "none"
4267
+ },
4268
+ children: dropIndicator.isNewSection ? "New section" : "Split section"
4269
+ }
4270
+ )
4271
+ }
4272
+ )
4273
+ ] });
4274
+ });
4259
4275
  }
4260
4276
  });
4261
4277
 
@@ -12641,7 +12657,7 @@ var init_use_href = __esm({
12641
12657
  }
12642
12658
  }
12643
12659
  }, [href]);
12644
- const hasHref = Boolean(href && href !== "#" && href.trim() !== "");
12660
+ const hasHref = Boolean(href && href !== "" && href.trim() !== "");
12645
12661
  return {
12646
12662
  href,
12647
12663
  setHref,
@@ -14633,23 +14649,9 @@ var init_useMjmlCompiler = __esm({
14633
14649
  }
14634
14650
  });
14635
14651
 
14636
- // src/core/editor/hooks/use-user.ts
14637
- function useUser() {
14638
- return {
14639
- isPaid: true
14640
- // toggle to true for testing paid features
14641
- };
14642
- }
14643
- var init_use_user = __esm({
14644
- "src/core/editor/hooks/use-user.ts"() {
14645
- "use strict";
14646
- }
14647
- });
14648
-
14649
14652
  // src/core/editor/components/preview.tsx
14650
14653
  function Preview() {
14651
- const { isPaid } = useUser();
14652
- const { template, setPreviewMode, previewMode } = useEditorStore();
14654
+ const { template, setPreviewMode, previewMode, isPaidLevel } = useEditorStore();
14653
14655
  const [html, setHtml] = (0, import_react33.useState)("");
14654
14656
  const [isLoading, setIsLoading] = (0, import_react33.useState)(false);
14655
14657
  const lastTemplateRef = (0, import_react33.useRef)("");
@@ -14669,7 +14671,7 @@ function Preview() {
14669
14671
  setIsLoading(true);
14670
14672
  const convertMjml = async () => {
14671
14673
  try {
14672
- const mjmlString = json2mjml(template, "production", { isPaid });
14674
+ const mjmlString = json2mjml(template, "production", { isPaidLevel });
14673
14675
  console.log("MJML string:", mjmlString);
14674
14676
  const result = await compileMjml(mjmlString);
14675
14677
  if (result.errors?.length > 0) {
@@ -14717,7 +14719,6 @@ var init_preview = __esm({
14717
14719
  init_editor();
14718
14720
  init_json2mjml();
14719
14721
  init_useMjmlCompiler();
14720
- init_use_user();
14721
14722
  import_react33 = require("react");
14722
14723
  init_tooltip();
14723
14724
  import_jsx_runtime47 = require("react/jsx-runtime");
@@ -16444,15 +16445,14 @@ var StylesPage = ({ value }) => {
16444
16445
  };
16445
16446
 
16446
16447
  // src/core/editor/components/email-template-v2/components/styles-badge.tsx
16447
- init_use_user();
16448
16448
  init_editor();
16449
16449
  var import_react40 = require("react");
16450
16450
  var import_lucide_react38 = require("lucide-react");
16451
16451
  var import_lucide_react39 = require("lucide-react");
16452
16452
  var import_jsx_runtime67 = require("react/jsx-runtime");
16453
16453
  var StylesBadge = ({ value }) => {
16454
- const { isPaid } = useUser();
16455
- const { template, setShowCompanyFooter } = useEditorStore();
16454
+ const { template, setShowCompanyFooter, isPaidLevel } = useEditorStore();
16455
+ const isPaid = isPaidLevel >= 1;
16456
16456
  const showCompanyFooter = (0, import_react40.useMemo)(() => {
16457
16457
  return template.content[0]?.data?.value?.showCompanyFooter ?? true;
16458
16458
  }, [template]);
@@ -23764,15 +23764,25 @@ var Editor2 = (0, import_react80.lazy)(() => Promise.resolve().then(() => (init_
23764
23764
  function TemplatePage({
23765
23765
  templateId,
23766
23766
  initialTemplate,
23767
- onSave
23767
+ onSave,
23768
+ isPaidLevel = 0
23768
23769
  }) {
23770
+ console.log("Template page rendered!");
23771
+ if (isPaidLevel > MAX_PAID_LEVEL) {
23772
+ console.warn("This paid level is invalid:", isPaidLevel);
23773
+ } else {
23774
+ console.log("This paid level is valid:", isPaidLevel);
23775
+ }
23769
23776
  (0, import_react80.useState)(() => {
23770
- useEditorStore.getState().initializeWithTemplate(templateId, initialTemplate, onSave);
23777
+ useEditorStore.getState().initializeWithTemplate(templateId, initialTemplate, onSave, isPaidLevel);
23771
23778
  });
23772
23779
  useAutoSave();
23773
23780
  const [editorLoading, setEditorLoading] = (0, import_react80.useState)(false);
23774
23781
  const [isPageHovered, setIsPageHovered] = (0, import_react80.useState)(false);
23775
- const { template, previewMode, focusIdx, setFocusIdx } = useEditorStore();
23782
+ const template = useEditorStore((state) => state.template);
23783
+ const previewMode = useEditorStore((state) => state.previewMode);
23784
+ const focusIdx = useEditorStore((state) => state.focusIdx);
23785
+ const setFocusIdx = useEditorStore((state) => state.setFocusIdx);
23776
23786
  const canvasBackgroundColor = template?.content?.[0]?.attributes?.["background-color"] || "#ffffff";
23777
23787
  const handlePageClick = (e) => {
23778
23788
  if (!previewMode && e.target === e.currentTarget) {