@templatical/editor 0.43.1 → 0.43.3

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.
Files changed (256) hide show
  1. package/README.md +1 -1
  2. package/dist/{AiChatSidebar-DOCvzGBe.js → AiChatSidebar-BAAypLEP.js} +7 -7
  3. package/dist/{AiFeatureMenu-xCJeD07B.js → AiFeatureMenu-CQcrDR-J.js} +4 -4
  4. package/dist/{BlockIssueBadge-BJ3A6mqh.js → BlockIssueBadge-DYI7ic1j.js} +2 -2
  5. package/dist/{BlockPreviewCanvas-BQXfqhzu.js → BlockPreviewCanvas-x5W-Sdhg.js} +2 -2
  6. package/dist/{CloudHeaderExtras-TFmBJ0Ry.js → CloudHeaderExtras-DnLaX4ea.js} +2 -2
  7. package/dist/{CloudPanels-BnSBbYnw.js → CloudPanels-BHPG3d1o.js} +4 -4
  8. package/dist/{CollaboratorBar-7ry8EdUh.js → CollaboratorBar-DG6KroMt.js} +2 -2
  9. package/dist/{CommentsPanel-CGEhbjx0.js → CommentsPanel-Bzk6VhJg.js} +1 -1
  10. package/dist/{CommentsSidebar-CUpst9ou.js → CommentsSidebar-CaRC3c1b.js} +10 -10
  11. package/dist/{CountdownToolbar-B1a_UIuB.js → CountdownToolbar-B-pAigUh.js} +3 -3
  12. package/dist/{DesignReferenceSidebar-DkAmsBgM.js → DesignReferenceSidebar-DfuJyDBz.js} +5 -5
  13. package/dist/{IssuesPanel-lTpsCV4a.js → IssuesPanel-vWhcDIvb.js} +5 -5
  14. package/dist/{LogicTagInsertButton-B5YymR4U.js → LogicTagInsertButton-D2Fjo444.js} +3 -3
  15. package/dist/{MediaEditModal-B1X8qtJb.js → MediaEditModal-BuP8SJrv.js} +270 -230
  16. package/dist/{MediaPanels-DO30L1KS.js → MediaPanels-BkcF0r_r.js} +1 -1
  17. package/dist/{MergeTagInput-B0M_-X0K.js → MergeTagInput-fEOOY7OU.js} +1 -1
  18. package/dist/{MergeTagModeToggle-B-8W1tK7.js → MergeTagModeToggle-BOeEL52u.js} +3 -3
  19. package/dist/{ParagraphEditor-DVGeU0Am.js → ParagraphEditor-BlJGeBRY.js} +22 -22
  20. package/dist/{RestoreVersionDialog-DOhgYkiA.js → RestoreVersionDialog-DYaoFk5G.js} +3 -3
  21. package/dist/{RichTextEditorContent-AaLAArA0.js → RichTextEditorContent-GacK8qwu.js} +4 -4
  22. package/dist/{SaveBlockDialog-36MzPD2S.js → SaveBlockDialog-DR1UCtQY.js} +8 -8
  23. package/dist/{SavedBlocksBrowserModal-B5nFKszf.js → SavedBlocksBrowserModal-q_-qYW9a.js} +9 -9
  24. package/dist/{SavedBlocksPanels-BZkbP7eB.js → SavedBlocksPanels-nTH8NTAT.js} +1 -1
  25. package/dist/{SavedBlocksPickBar-ChoCMdu7.js → SavedBlocksPickBar-C7vHmc5c.js} +2 -2
  26. package/dist/{TemplateScoringPanel-C6ellAcs.js → TemplateScoringPanel-D9jeQCYl.js} +11 -11
  27. package/dist/{TemplateSettings-C_0qK-zs.js → TemplateSettings-ClfHwxUa.js} +5 -5
  28. package/dist/{TestEmailModal-CKW-VArm.js → TestEmailModal-DEnvrv38.js} +6 -6
  29. package/dist/{TestEmailPanel-BylYzjyM.js → TestEmailPanel-Ca9t6KG7.js} +1 -1
  30. package/dist/{TitleEditor-DoQ286_n.js → TitleEditor-D7mNs3s5.js} +9 -9
  31. package/dist/{ToggleSwitch-CtZ6adlX.js → ToggleSwitch-Z-rqEcig.js} +1 -1
  32. package/dist/{Toolbar-B6dG5_IP.js → Toolbar-DUgyDBkz.js} +28 -28
  33. package/dist/{TplModal-DHwQXySQ.js → TplModal-CYyFAScB.js} +1 -1
  34. package/dist/{VersionHistoryMenu-DdspCRSA.js → VersionHistoryMenu-D42tGKM6.js} +5 -5
  35. package/dist/{VersionHistoryPanels-C6QxmRUH.js → VersionHistoryPanels-o0HGvQJX.js} +1 -1
  36. package/dist/{VersionPreviewBanner-XUuLe36h.js → VersionPreviewBanner-D5RZtvUf.js} +1 -1
  37. package/dist/{WrapperBlock-BumDudbj.js → WrapperBlock-B0_vSfSD.js} +1 -1
  38. package/dist/{blockTypeIcons-Sg8SY1NQ.js → blockTypeIcons-CEk_GHmU.js} +2 -2
  39. package/dist/bundle-stats.json +6 -6
  40. package/dist/{ca-BTf1Zbqb.js → ca-CYQH3J0b.js} +6 -2
  41. package/dist/{ca--zGWn9AU.js → ca-DSwai-th.js} +1 -4
  42. package/dist/cdn/chunks/{AiChatSidebar-DiG-EQ-E.js → AiChatSidebar-oFwacME4.js} +2 -2
  43. package/dist/cdn/chunks/{AiChatSidebar-DiG-EQ-E.js.map → AiChatSidebar-oFwacME4.js.map} +1 -1
  44. package/dist/cdn/chunks/{AiFeatureMenu-4TgzmLb3.js → AiFeatureMenu-BiHFJf9i.js} +3 -3
  45. package/dist/cdn/chunks/{AiFeatureMenu-4TgzmLb3.js.map → AiFeatureMenu-BiHFJf9i.js.map} +1 -1
  46. package/dist/cdn/chunks/{BlockIssueBadge-ChQ0mPf5.js → BlockIssueBadge-CWlcDBFm.js} +2 -2
  47. package/dist/cdn/chunks/{BlockIssueBadge-ChQ0mPf5.js.map → BlockIssueBadge-CWlcDBFm.js.map} +1 -1
  48. package/dist/cdn/chunks/{BlockPreviewCanvas-BpvK3Sh9.js → BlockPreviewCanvas-CIwIA3zq.js} +3 -3
  49. package/dist/cdn/chunks/{BlockPreviewCanvas-BpvK3Sh9.js.map → BlockPreviewCanvas-CIwIA3zq.js.map} +1 -1
  50. package/dist/cdn/chunks/{CloudHeaderExtras-DqgOaWym.js → CloudHeaderExtras-BgsjwWUQ.js} +3 -3
  51. package/dist/cdn/chunks/{CloudHeaderExtras-DqgOaWym.js.map → CloudHeaderExtras-BgsjwWUQ.js.map} +1 -1
  52. package/dist/cdn/chunks/{CloudPanels-INOiwkET.js → CloudPanels-DFCwq8_J.js} +4 -4
  53. package/dist/cdn/chunks/{CloudPanels-INOiwkET.js.map → CloudPanels-DFCwq8_J.js.map} +1 -1
  54. package/dist/cdn/chunks/{CollaboratorBar-CQ9EpYKQ.js → CollaboratorBar-CiMZfMwN.js} +3 -3
  55. package/dist/cdn/chunks/{CollaboratorBar-CQ9EpYKQ.js.map → CollaboratorBar-CiMZfMwN.js.map} +1 -1
  56. package/dist/cdn/chunks/{CommentsPanel-CjhxGMjL.js → CommentsPanel-CL4grbMp.js} +2 -2
  57. package/dist/cdn/chunks/{CommentsPanel-CjhxGMjL.js.map → CommentsPanel-CL4grbMp.js.map} +1 -1
  58. package/dist/cdn/chunks/{CommentsSidebar-D9bqMnlt.js → CommentsSidebar-tWYYKv1s.js} +2 -2
  59. package/dist/cdn/chunks/{CommentsSidebar-D9bqMnlt.js.map → CommentsSidebar-tWYYKv1s.js.map} +1 -1
  60. package/dist/cdn/chunks/{CountdownToolbar-CIRx5w1U.js → CountdownToolbar-DfyqIppF.js} +4 -4
  61. package/dist/cdn/chunks/{CountdownToolbar-CIRx5w1U.js.map → CountdownToolbar-DfyqIppF.js.map} +1 -1
  62. package/dist/cdn/chunks/{DesignReferenceSidebar-C1-SbE_R.js → DesignReferenceSidebar-C6O12hvt.js} +2 -2
  63. package/dist/cdn/chunks/{DesignReferenceSidebar-C1-SbE_R.js.map → DesignReferenceSidebar-C6O12hvt.js.map} +1 -1
  64. package/dist/cdn/chunks/{IssuesPanel-qKY6UDd-.js → IssuesPanel-DYJXIwnw.js} +2 -2
  65. package/dist/cdn/chunks/{IssuesPanel-qKY6UDd-.js.map → IssuesPanel-DYJXIwnw.js.map} +1 -1
  66. package/dist/cdn/chunks/{LogicTagInsertButton-CItDS_U1.js → LogicTagInsertButton-FNMVxm_V.js} +2 -2
  67. package/dist/cdn/chunks/{LogicTagInsertButton-CItDS_U1.js.map → LogicTagInsertButton-FNMVxm_V.js.map} +1 -1
  68. package/dist/cdn/chunks/{MediaEditModal-CzUtXkpk.js → MediaEditModal-C9yZ0AJv.js} +272 -232
  69. package/dist/cdn/chunks/MediaEditModal-C9yZ0AJv.js.map +1 -0
  70. package/dist/cdn/chunks/{MediaPanels-DOLsh7WC.js → MediaPanels-DiKU1r9P.js} +2 -2
  71. package/dist/cdn/chunks/{MediaPanels-DOLsh7WC.js.map → MediaPanels-DiKU1r9P.js.map} +1 -1
  72. package/dist/cdn/chunks/{MergeTagInput-B33u3nxj.js → MergeTagInput-CSC64xNL.js} +2 -2
  73. package/dist/cdn/chunks/{MergeTagInput-B33u3nxj.js.map → MergeTagInput-CSC64xNL.js.map} +1 -1
  74. package/dist/cdn/chunks/{MergeTagModeToggle-Bz1CH3NT.js → MergeTagModeToggle-027RMXDP.js} +2 -2
  75. package/dist/cdn/chunks/{MergeTagModeToggle-Bz1CH3NT.js.map → MergeTagModeToggle-027RMXDP.js.map} +1 -1
  76. package/dist/cdn/chunks/{ParagraphEditor-B6A4yQ0A.js → ParagraphEditor-0Z26-N5u.js} +19 -19
  77. package/dist/cdn/chunks/{ParagraphEditor-B6A4yQ0A.js.map → ParagraphEditor-0Z26-N5u.js.map} +1 -1
  78. package/dist/cdn/chunks/{RestoreVersionDialog-B9rujl9w.js → RestoreVersionDialog-Dd1v1KLK.js} +3 -3
  79. package/dist/cdn/chunks/{RestoreVersionDialog-B9rujl9w.js.map → RestoreVersionDialog-Dd1v1KLK.js.map} +1 -1
  80. package/dist/cdn/chunks/{RichTextEditorContent-dOg1xFqv.js → RichTextEditorContent-BOZ_5I76.js} +4 -4
  81. package/dist/cdn/chunks/{RichTextEditorContent-dOg1xFqv.js.map → RichTextEditorContent-BOZ_5I76.js.map} +1 -1
  82. package/dist/cdn/chunks/{SaveBlockDialog-BNAeb6--.js → SaveBlockDialog-B6WQwNJD.js} +6 -6
  83. package/dist/cdn/chunks/{SaveBlockDialog-BNAeb6--.js.map → SaveBlockDialog-B6WQwNJD.js.map} +1 -1
  84. package/dist/cdn/chunks/{SavedBlocksBrowserModal--FoVqEnT.js → SavedBlocksBrowserModal-C015fVMV.js} +6 -6
  85. package/dist/cdn/chunks/{SavedBlocksBrowserModal--FoVqEnT.js.map → SavedBlocksBrowserModal-C015fVMV.js.map} +1 -1
  86. package/dist/cdn/chunks/{SavedBlocksPanels-qKyPnVgk.js → SavedBlocksPanels-DZmfXW4x.js} +2 -2
  87. package/dist/cdn/chunks/{SavedBlocksPanels-qKyPnVgk.js.map → SavedBlocksPanels-DZmfXW4x.js.map} +1 -1
  88. package/dist/cdn/chunks/{SavedBlocksPickBar-BvLwca4B.js → SavedBlocksPickBar-DtCQFCCW.js} +3 -3
  89. package/dist/cdn/chunks/{SavedBlocksPickBar-BvLwca4B.js.map → SavedBlocksPickBar-DtCQFCCW.js.map} +1 -1
  90. package/dist/cdn/chunks/{TemplateScoringPanel-ebsrVge3.js → TemplateScoringPanel-CeCVyNV1.js} +2 -2
  91. package/dist/cdn/chunks/{TemplateScoringPanel-ebsrVge3.js.map → TemplateScoringPanel-CeCVyNV1.js.map} +1 -1
  92. package/dist/cdn/chunks/{TemplateSettings-BaEVdUTM.js → TemplateSettings-CuXwthI6.js} +4 -4
  93. package/dist/cdn/chunks/{TemplateSettings-BaEVdUTM.js.map → TemplateSettings-CuXwthI6.js.map} +1 -1
  94. package/dist/cdn/chunks/{TestEmailModal-DpYirvEL.js → TestEmailModal-DV1XKWRh.js} +6 -6
  95. package/dist/cdn/chunks/{TestEmailModal-DpYirvEL.js.map → TestEmailModal-DV1XKWRh.js.map} +1 -1
  96. package/dist/cdn/chunks/{TestEmailPanel-PAT70a2g.js → TestEmailPanel-CnVW00RI.js} +2 -2
  97. package/dist/cdn/chunks/{TestEmailPanel-PAT70a2g.js.map → TestEmailPanel-CnVW00RI.js.map} +1 -1
  98. package/dist/cdn/chunks/{TitleEditor-Dc7YB6Uy.js → TitleEditor-B9sKbRd4.js} +8 -8
  99. package/dist/cdn/chunks/{TitleEditor-Dc7YB6Uy.js.map → TitleEditor-B9sKbRd4.js.map} +1 -1
  100. package/dist/cdn/chunks/{ToggleSwitch-D-8dCEbI.js → ToggleSwitch-eHkFBx4o.js} +2 -2
  101. package/dist/cdn/chunks/{ToggleSwitch-D-8dCEbI.js.map → ToggleSwitch-eHkFBx4o.js.map} +1 -1
  102. package/dist/cdn/chunks/{Toolbar-BDzhrmGV.js → Toolbar-DeKbYejp.js} +15 -15
  103. package/dist/cdn/chunks/{Toolbar-BDzhrmGV.js.map → Toolbar-DeKbYejp.js.map} +1 -1
  104. package/dist/cdn/chunks/{TplModal-BbHRUc1Y.js → TplModal-Dqq-Ermd.js} +2 -2
  105. package/dist/cdn/chunks/{TplModal-BbHRUc1Y.js.map → TplModal-Dqq-Ermd.js.map} +1 -1
  106. package/dist/cdn/chunks/{VersionHistoryMenu-C_bBKOYU.js → VersionHistoryMenu-Kf4QFm7m.js} +2 -2
  107. package/dist/cdn/chunks/{VersionHistoryMenu-C_bBKOYU.js.map → VersionHistoryMenu-Kf4QFm7m.js.map} +1 -1
  108. package/dist/cdn/chunks/{VersionHistoryPanels-BoJsPIKa.js → VersionHistoryPanels-Cz8rde-s.js} +2 -2
  109. package/dist/cdn/chunks/{VersionHistoryPanels-BoJsPIKa.js.map → VersionHistoryPanels-Cz8rde-s.js.map} +1 -1
  110. package/dist/cdn/chunks/{VersionPreviewBanner-CKyrcgxJ.js → VersionPreviewBanner-DNYHqbj9.js} +2 -2
  111. package/dist/cdn/chunks/{VersionPreviewBanner-CKyrcgxJ.js.map → VersionPreviewBanner-DNYHqbj9.js.map} +1 -1
  112. package/dist/cdn/chunks/{WrapperBlock-CGg6rBnx.js → WrapperBlock-DIZHUwFn.js} +2 -2
  113. package/dist/cdn/chunks/{WrapperBlock-CGg6rBnx.js.map → WrapperBlock-DIZHUwFn.js.map} +1 -1
  114. package/dist/cdn/chunks/{blockTypeIcons-CdfCfqHE.js → blockTypeIcons-hgzxMFWX.js} +2 -2
  115. package/dist/cdn/chunks/{blockTypeIcons-CdfCfqHE.js.map → blockTypeIcons-hgzxMFWX.js.map} +1 -1
  116. package/dist/cdn/chunks/{ca-BTf1Zbqb.js → ca-CYQH3J0b.js} +7 -3
  117. package/dist/cdn/chunks/ca-CYQH3J0b.js.map +1 -0
  118. package/dist/cdn/chunks/{ca--zGWn9AU.js → ca-DSwai-th.js} +2 -5
  119. package/dist/cdn/chunks/ca-DSwai-th.js.map +1 -0
  120. package/dist/cdn/chunks/{de-BRNLKcDl.js → de-DOhKuphf.js} +2 -5
  121. package/dist/cdn/chunks/de-DOhKuphf.js.map +1 -0
  122. package/dist/cdn/chunks/{de-xzrh9PA3.js → de-JvCTMJPy.js} +7 -3
  123. package/dist/cdn/chunks/de-JvCTMJPy.js.map +1 -0
  124. package/dist/cdn/chunks/dist-BRZhN8qJ.js.map +1 -1
  125. package/dist/cdn/chunks/dist-CXB2_kBa.js.map +1 -1
  126. package/dist/cdn/chunks/draggable-IB4x6w2O.js.map +1 -1
  127. package/dist/cdn/chunks/{editor-modal-CZQArGNj.js → editor-modal-cKeJ4wC2.js} +472 -428
  128. package/dist/cdn/chunks/editor-modal-cKeJ4wC2.js.map +1 -0
  129. package/dist/cdn/chunks/{en-JJjqHMjy.js → en-BE28ZcUF.js} +2 -5
  130. package/dist/cdn/chunks/en-BE28ZcUF.js.map +1 -0
  131. package/dist/cdn/chunks/en-CoNe1psX.js +2 -0
  132. package/dist/cdn/chunks/{es-BT4LSpE2.js → es-CAv-Vldw.js} +7 -3
  133. package/dist/cdn/chunks/es-CAv-Vldw.js.map +1 -0
  134. package/dist/cdn/chunks/{es-BIh8BxV9.js → es-oQI-dfx2.js} +2 -5
  135. package/dist/cdn/chunks/es-oQI-dfx2.js.map +1 -0
  136. package/dist/cdn/chunks/{extensions-Dy251lPd.js → extensions-rPP7qRRm.js} +3 -3
  137. package/dist/cdn/chunks/{extensions-Dy251lPd.js.map → extensions-rPP7qRRm.js.map} +1 -1
  138. package/dist/{fr-CI7OJoql.js → cdn/chunks/fr-DVPtxpR6.js} +8 -2
  139. package/dist/cdn/chunks/fr-DVPtxpR6.js.map +1 -0
  140. package/dist/cdn/chunks/{fr-C5VUZogP.js → fr-rzLdCMSG.js} +2 -5
  141. package/dist/cdn/chunks/fr-rzLdCMSG.js.map +1 -0
  142. package/dist/cdn/chunks/htmlparser-ClUZ6waR.js.map +1 -1
  143. package/dist/cdn/chunks/{icons-CQnjugjy.js → icons-BF0mJX0V.js} +18 -18
  144. package/dist/cdn/chunks/{icons-CQnjugjy.js.map → icons-BF0mJX0V.js.map} +1 -1
  145. package/dist/{ja-DhlWUfke.js → cdn/chunks/ja-dcWzjStz.js} +8 -2
  146. package/dist/cdn/chunks/ja-dcWzjStz.js.map +1 -0
  147. package/dist/{ja-x5JYxaCW.js → cdn/chunks/ja-dyFc6Ghz.js} +3 -4
  148. package/dist/cdn/chunks/ja-dyFc6Ghz.js.map +1 -0
  149. package/dist/cdn/chunks/{liquid.browser-BypTT2oc.js → liquid.browser-DXPjcePu.js} +758 -719
  150. package/dist/cdn/chunks/liquid.browser-DXPjcePu.js.map +1 -0
  151. package/dist/cdn/chunks/{nl-UXxFyXV6.js → nl-BcQhRqKR.js} +2 -5
  152. package/dist/cdn/chunks/nl-BcQhRqKR.js.map +1 -0
  153. package/dist/{nl-D8y5OWGB.js → cdn/chunks/nl-LZNqQgE4.js} +8 -2
  154. package/dist/cdn/chunks/nl-LZNqQgE4.js.map +1 -0
  155. package/dist/cdn/chunks/{pt-BR-DMCbUBvJ.js → pt-BR-D6P_mNwA.js} +7 -3
  156. package/dist/cdn/chunks/pt-BR-D6P_mNwA.js.map +1 -0
  157. package/dist/cdn/chunks/{pt-BR-DHm3SP6W.js → pt-BR-UombhTMO.js} +2 -5
  158. package/dist/cdn/chunks/pt-BR-UombhTMO.js.map +1 -0
  159. package/dist/cdn/chunks/pusher-BbIHJKf3.js.map +1 -1
  160. package/dist/cdn/chunks/{src-BwsQ27UT.js → src-V9_ODH_1.js} +3 -3
  161. package/dist/cdn/chunks/{src-BwsQ27UT.js.map → src-V9_ODH_1.js.map} +1 -1
  162. package/dist/cdn/chunks/{tiptap-D1u8JIUW.js → tiptap-02G9FGkl.js} +3726 -3552
  163. package/dist/cdn/chunks/tiptap-02G9FGkl.js.map +1 -0
  164. package/dist/cdn/chunks/{useEditorCore-DoX2RgTl.js → useEditorCore-DWvAia8X.js} +14 -14
  165. package/dist/cdn/chunks/{useEditorCore-DoX2RgTl.js.map → useEditorCore-DWvAia8X.js.map} +1 -1
  166. package/dist/cdn/editor.css +1 -1
  167. package/dist/cdn/editor.js +337 -359
  168. package/dist/cdn/editor.js.map +1 -1
  169. package/dist/{check-BMx_TLAS.js → check-DEjsanPt.js} +1 -1
  170. package/dist/{chevron-down-KZf5mh9_.js → chevron-down-Cv11TeKQ.js} +1 -1
  171. package/dist/{chevron-right-BLZ60Vxw.js → chevron-right-R8dOpzBZ.js} +1 -1
  172. package/dist/{chevron-up-B9ynB8eU.js → chevron-up-DsDEytX1.js} +1 -1
  173. package/dist/{circle-alert-pjvFFDrF.js → circle-alert-B_NqWETg.js} +1 -1
  174. package/dist/{clock-CiiTBfF3.js → clock-CDGKPx_X.js} +1 -1
  175. package/dist/{copy-BF0_pQjH.js → copy-Dv2VltLv.js} +1 -1
  176. package/dist/{createLucideIcon-C2YOVVWx.js → createLucideIcon-MrFrvtkF.js} +5 -5
  177. package/dist/{de-BRNLKcDl.js → de-DOhKuphf.js} +1 -4
  178. package/dist/{de-xzrh9PA3.js → de-JvCTMJPy.js} +6 -2
  179. package/dist/{dist-CxmLZQso.js → dist-8By0p8ah.js} +5 -5
  180. package/dist/{dist-W_ERo6c_.js → dist-BYg1EL5g.js} +5 -5
  181. package/dist/{dist-C2Ge44fD.js → dist-BgSLW3Hn.js} +1704 -1303
  182. package/dist/{dist-B0dkvy7z.js → dist-CWc7nIkA.js} +5 -5
  183. package/dist/{dist-CIxmOOSG.js → dist-Ceew_u40.js} +448 -699
  184. package/dist/{dist-BwoEME8p.js → dist-CmwBvyW0.js} +172 -171
  185. package/dist/{dist-CJ7RZARo.js → dist-Czzst9ng.js} +302 -279
  186. package/dist/dist-D2m55Lvs.js +5 -0
  187. package/dist/{dist-p-LkiWIQ.js → dist-D4HP93ZF.js} +13 -13
  188. package/dist/{dist-BPWuzECC.js → dist-DdSWMtEP.js} +6 -6
  189. package/dist/{dist-DRmafAUz.js → dist-DmQckB4p.js} +29 -29
  190. package/dist/dist-Dw2_11LA.js +5 -0
  191. package/dist/{editor-modal-99FB40dl.js → editor-modal-DmgxKgII.js} +484 -440
  192. package/dist/{en-JJjqHMjy.js → en-BE28ZcUF.js} +1 -4
  193. package/dist/en-DXYNty6E.js +2 -0
  194. package/dist/{es-BT4LSpE2.js → es-CAv-Vldw.js} +6 -2
  195. package/dist/{es-BIh8BxV9.js → es-oQI-dfx2.js} +1 -4
  196. package/dist/{extensions-DqQXxrvP.js → extensions-CkwL1oWn.js} +61 -61
  197. package/dist/{eye-RdXYgrQZ.js → eye-BVxcDaZZ.js} +1 -1
  198. package/dist/{cdn/chunks/fr-CI7OJoql.js → fr-DVPtxpR6.js} +6 -4
  199. package/dist/{fr-C5VUZogP.js → fr-rzLdCMSG.js} +1 -4
  200. package/dist/{image-up-BwdoReQt.js → image-up-CnC4n-8q.js} +1 -1
  201. package/dist/index.d.ts +7 -1
  202. package/dist/{info-zFMSGrL1.js → info-q7gVjMBC.js} +1 -1
  203. package/dist/{cdn/chunks/ja-DhlWUfke.js → ja-dcWzjStz.js} +6 -4
  204. package/dist/{cdn/chunks/ja-x5JYxaCW.js → ja-dyFc6Ghz.js} +1 -6
  205. package/dist/{link-D7orqFBT.js → link-B1-UqOP6.js} +1 -1
  206. package/dist/{liquid.browser-wPG0n4Cs.js → liquid.browser-Cs4gOjeX.js} +757 -718
  207. package/dist/{list-BGt4Pgxc.js → list-BvlNrd6M.js} +1 -1
  208. package/dist/{loader-circle-BVBgkRGz.js → loader-circle-CJ8klGfl.js} +1 -1
  209. package/dist/{message-circle-D8YLsarv.js → message-circle-Bgepipq_.js} +1 -1
  210. package/dist/{nl-UXxFyXV6.js → nl-BcQhRqKR.js} +1 -4
  211. package/dist/{cdn/chunks/nl-D8y5OWGB.js → nl-LZNqQgE4.js} +6 -4
  212. package/dist/{package-CuFvk0L5.js → package-BYIdon2Q.js} +1 -1
  213. package/dist/{pencil-DRYbmZW7.js → pencil-Bhs9MNTT.js} +1 -1
  214. package/dist/{plus-Dv0b3kh0.js → plus-C0eGj8vT.js} +1 -1
  215. package/dist/{pt-BR-DMCbUBvJ.js → pt-BR-D6P_mNwA.js} +6 -2
  216. package/dist/{pt-BR-DHm3SP6W.js → pt-BR-UombhTMO.js} +1 -4
  217. package/dist/{refresh-cw-gYTIS5bJ.js → refresh-cw-BQTOIZKD.js} +1 -1
  218. package/dist/{search-D84_Ki_a.js → search-CeT7yRBa.js} +1 -1
  219. package/dist/{send-Cn-8_H_U.js → send-D7ck2xEq.js} +1 -1
  220. package/dist/{shield-check-BOoVWJtD.js → shield-check-CF_VbIbH.js} +1 -1
  221. package/dist/{smartphone-4Ej3PBw5.js → smartphone-CkVOj7Qn.js} +1 -1
  222. package/dist/{socialIcons-CzRJskEk.js → socialIcons-QYtdurnO.js} +1 -1
  223. package/dist/{sparkles-D3vnNe7O.js → sparkles-Cfb-Kkgv.js} +1 -1
  224. package/dist/style.css +1 -1
  225. package/dist/templatical-editor.js +475 -497
  226. package/dist/{text-align-start-Ci2h_Xly.js → text-align-start-CkFs2Zig.js} +13 -13
  227. package/dist/{trash-wmi4UKvv.js → trash-C2LPw5pa.js} +1 -1
  228. package/dist/{triangle-alert-DJ0r2wmQ.js → triangle-alert-gaUz4rum.js} +1 -1
  229. package/dist/{upload-XMde-TXf.js → upload-C_DYZD_p.js} +1 -1
  230. package/dist/{useEditorCore-CAhRAya8.js → useEditorCore-Cmv5ll-D.js} +20 -20
  231. package/dist/{useScrollToBlock-B4F7KCrf.js → useScrollToBlock-BsPYYJ8h.js} +1 -1
  232. package/dist/{x-BWrtpVl-.js → x-JhKma5YO.js} +1 -1
  233. package/package.json +25 -25
  234. package/dist/cdn/chunks/MediaEditModal-CzUtXkpk.js.map +0 -1
  235. package/dist/cdn/chunks/ca--zGWn9AU.js.map +0 -1
  236. package/dist/cdn/chunks/ca-BTf1Zbqb.js.map +0 -1
  237. package/dist/cdn/chunks/de-BRNLKcDl.js.map +0 -1
  238. package/dist/cdn/chunks/de-xzrh9PA3.js.map +0 -1
  239. package/dist/cdn/chunks/editor-modal-CZQArGNj.js.map +0 -1
  240. package/dist/cdn/chunks/en-9unxBjV8.js +0 -2
  241. package/dist/cdn/chunks/en-JJjqHMjy.js.map +0 -1
  242. package/dist/cdn/chunks/es-BIh8BxV9.js.map +0 -1
  243. package/dist/cdn/chunks/es-BT4LSpE2.js.map +0 -1
  244. package/dist/cdn/chunks/fr-C5VUZogP.js.map +0 -1
  245. package/dist/cdn/chunks/fr-CI7OJoql.js.map +0 -1
  246. package/dist/cdn/chunks/ja-DhlWUfke.js.map +0 -1
  247. package/dist/cdn/chunks/ja-x5JYxaCW.js.map +0 -1
  248. package/dist/cdn/chunks/liquid.browser-BypTT2oc.js.map +0 -1
  249. package/dist/cdn/chunks/nl-D8y5OWGB.js.map +0 -1
  250. package/dist/cdn/chunks/nl-UXxFyXV6.js.map +0 -1
  251. package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +0 -1
  252. package/dist/cdn/chunks/pt-BR-DMCbUBvJ.js.map +0 -1
  253. package/dist/cdn/chunks/tiptap-D1u8JIUW.js.map +0 -1
  254. package/dist/dist-BUHovM4k.js +0 -5
  255. package/dist/dist-DdOAotfz.js +0 -5
  256. package/dist/en-BvOjRLiw.js +0 -2
@@ -1,9 +1,6 @@
1
1
  //#region src/i18n/locales/en.ts
2
2
  var e = {
3
- footer: {
4
- poweredBy: "Powered by",
5
- openSource: "Open Source"
6
- },
3
+ footer: { poweredBy: "Powered by" },
7
4
  header: {
8
5
  save: "Save",
9
6
  saving: "Saving...",
@@ -636,4 +633,4 @@ var e = {
636
633
  //#endregion
637
634
  export { e as default };
638
635
 
639
- //# sourceMappingURL=en-JJjqHMjy.js.map
636
+ //# sourceMappingURL=en-BE28ZcUF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en-BE28ZcUF.js","names":["en_default"],"sources":["../../../src/i18n/locales/en.ts"],"sourcesContent":["export default {\n // Footer (OSS only)\n footer: {\n poweredBy: \"Powered by\",\n },\n\n // Header — template name + save state. Shown only when a templates provider\n // is configured.\n header: {\n save: \"Save\",\n saving: \"Saving...\",\n saved: \"Saved\",\n unsaved: \"Unsaved\",\n saveFailed: \"Save failed\",\n saveNoTemplate: \"Load or create a template first\",\n templateName: \"Template name\",\n rename: \"Rename template\",\n untitled: \"Untitled\",\n updatedAt: \"Updated {time}\",\n createdAt: \"Created {time}\",\n updatedJustNow: \"Updated just now\",\n createdJustNow: \"Created just now\",\n },\n\n // Relative timestamps, shared by every surface that shows one: the header's\n // updated line, saved blocks, comments and version history.\n time: {\n justNow: \"Just now\",\n minutesAgo: \"{minutes}m ago\",\n hoursAgo: \"{hours}h ago\",\n daysAgo: \"{days}d ago\",\n },\n\n // History (undo/redo)\n history: {\n collabWarning: \"Undo may affect collaborators' recent changes\",\n undo: \"Undo\",\n redo: \"Redo\",\n },\n\n // Viewport toggle\n viewport: {\n label: \"Viewport\",\n desktop: \"Desktop\",\n mobile: \"Mobile\",\n },\n\n // Dark mode preview\n darkMode: {\n enable: \"Dark Mode Preview\",\n disable: \"Light Mode Preview\",\n },\n\n // Preview mode\n previewMode: {\n enable: \"Preview Mode\",\n disable: \"Exit Preview\",\n },\n\n // Sidebar - Block types\n blocks: {\n section: \"Section\",\n image: \"Image\",\n title: \"Title\",\n paragraph: \"Paragraph\",\n button: \"Button\",\n divider: \"Divider\",\n video: \"Video\",\n social: \"Social\",\n spacer: \"Spacer\",\n html: \"HTML\",\n menu: \"Menu\",\n table: \"Table\",\n countdown: \"Countdown\",\n },\n\n // Placeholder text a newly inserted block starts with. Author-facing\n // prompts, meant to be overwritten — so they follow the editor UI locale.\n // Recipient-facing defaults live in `i18n/contentDefaults.ts` and follow\n // the template's own `settings.locale` instead.\n blockDefaults: {\n title: \"Enter your title\",\n paragraph: \"Enter your text here\",\n button: \"Click Here\",\n },\n\n // Right sidebar\n sidebar: {\n content: \"Content\",\n settings: \"Settings\",\n noSelection: \"No element selected\",\n noSelectionHint: \"Select a block on the canvas to edit it\",\n },\n\n // Toolbar - Common\n toolbar: {\n duplicate: \"Duplicate\",\n delete: \"Delete\",\n },\n\n // Title editor toolbar\n titleEditor: {\n toolbar: \"Title formatting\",\n bold: \"Bold (Ctrl+B)\",\n italic: \"Italic (Ctrl+I)\",\n addLink: \"Add Link\",\n },\n\n // Paragraph editor toolbar\n paragraphEditor: {\n toolbar: \"Text formatting\",\n bold: \"Bold (Ctrl+B)\",\n italic: \"Italic (Ctrl+I)\",\n underline: \"Underline (Ctrl+U)\",\n strikethrough: \"Strikethrough\",\n subscript: \"Subscript\",\n superscript: \"Superscript\",\n addLink: \"Add Link\",\n bulletList: \"Bullet List\",\n numberedList: \"Numbered List\",\n alignLeft: \"Align Left\",\n alignCenter: \"Align Center\",\n alignRight: \"Align Right\",\n alignJustify: \"Justify\",\n clearFormatting: \"Clear Formatting\",\n insertEmoji: \"Insert Emoji\",\n fontFamily: \"Font Family\",\n defaultFont: \"Default\",\n fontSize: \"Font Size\",\n defaultSize: \"Default\",\n textColor: \"Text Color\",\n highlightColor: \"Highlight Color\",\n lineHeight: \"Line Height\",\n letterSpacing: \"Letter Spacing\",\n emojiItemLabel: \"Insert emoji {emoji}\",\n },\n\n // Block actions (BlockWrapper)\n blockActions: {\n drag: \"Drag to reorder, or press Space to move with keyboard\",\n dragLifted:\n \"Moving {block}. Use up and down arrow keys to reposition, Space or Enter to drop, Escape to cancel.\",\n duplicate: \"Duplicate block\",\n delete: \"Delete block\",\n hiddenOnViewport: \"Hidden on {viewport}\",\n saveAsBlock: \"Save as Block\",\n conditionToggle: \"Toggle display condition\",\n comments: \"Comments ({count})\",\n lifted: \"{block} lifted. Position {position} of {total}.\",\n moved: \"{block} moved to position {position} of {total}.\",\n dropped: \"{block} dropped at position {position} of {total}.\",\n cancelled: \"Move cancelled. {block} returned to position {position}.\",\n },\n\n // Toolbar - Section\n section: {\n dropHere: \"Drop blocks here\",\n columns: \"Columns\",\n column1: \"1 Column\",\n column2: \"2 Columns\",\n column3: \"3 Columns\",\n ratio12: \"1:2 Ratio\",\n ratio21: \"2:1 Ratio\",\n borderRadius: \"Border Radius\",\n wrapperEnable: \"Add wrapper\",\n wrapperLayoutConflict:\n \"This editor already frames the email. An extra frame on this section isn't supported — preview and export will fail.\",\n stackOnMobile: \"Stack on mobile\",\n },\n\n // Text editor link dialog\n linkDialog: {\n editLink: \"Edit Link\",\n insertLink: \"Insert Link\",\n updateLink: \"Update Link\",\n removeLink: \"Remove Link\",\n cancel: \"Cancel\",\n urlPlaceholder: \"https://example.com\",\n urlLabel: \"URL\",\n colorLabel: \"Link Color\",\n },\n\n // Toolbar - Title\n title: {\n level: \"Heading Level\",\n heading1: \"Heading 1 (36px)\",\n heading2: \"Heading 2 (28px)\",\n heading3: \"Heading 3 (22px)\",\n heading4: \"Heading 4 (18px)\",\n fontFamily: \"Font Family\",\n inheritFont: \"Use template font\",\n color: \"Color\",\n align: \"Align\",\n alignLeft: \"Left\",\n alignCenter: \"Center\",\n alignRight: \"Right\",\n },\n\n // Emoji picker\n emoji: {\n smileys: \"Smileys\",\n gestures: \"Gestures\",\n objects: \"Objects\",\n },\n\n // Toolbar - Image\n image: {\n imageUrl: \"Image URL\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Alt Text\",\n altTextPlaceholder: \"Image description\",\n width: \"Width\",\n fullWidth: \"Full Width\",\n widthCustom: \"Custom\",\n height: \"Height\",\n heightAuto: \"Auto\",\n heightCustom: \"Custom\",\n borderRadius: \"Border Radius\",\n linkUrl: \"Link URL\",\n openInNewTab: \"Open in new tab\",\n placeholderUrl: \"Placeholder Image\",\n optional: \"(optional)\",\n placeholderUrlPlaceholder: \"https://... (design-time only)\",\n placeholderUrlTooltip:\n \"Since the image URL uses a merge tag, you can provide a real image here to preview the layout while designing. This is not included in the final output.\",\n clickToAdd: \"Click to add image URL\",\n browseMedia: \"Browse Media\",\n dropToUpload: \"Drop image to upload\",\n uploading: \"Uploading…\",\n decorative: \"Decorative image\",\n decorativeHint:\n \"Hidden from screen readers. Use only for spacers and visual flourishes.\",\n },\n\n // Toolbar - Video\n video: {\n videoUrl: \"Video URL\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n openInNewTab: \"Open in new tab\",\n customThumbnail: \"Custom Thumbnail\",\n optional: \"(optional)\",\n thumbnailPlaceholder: \"Auto-generated from video URL\",\n altText: \"Alt Text\",\n altTextPlaceholder: \"Video description\",\n width: \"Width\",\n fullWidth: \"Full Width\",\n height: \"Height\",\n heightAuto: \"Auto\",\n heightCustom: \"Custom\",\n placeholderUrl: \"Placeholder Thumbnail\",\n placeholderUrlPlaceholder: \"https://... (design-time only)\",\n placeholderUrlTooltip:\n \"Since the video URL uses a merge tag, you can provide a real thumbnail here to preview the layout while designing. This is not included in the final output.\",\n addVideo: \"Add a video URL\",\n },\n\n // Toolbar - Button\n button: {\n fontFamily: \"Font Family\",\n inheritFont: \"Use template font\",\n text: \"Text\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Open in new tab\",\n background: \"Background\",\n textColor: \"Text Color\",\n borderRadius: \"Border Radius\",\n fontSize: \"Font Size\",\n width: \"Width\",\n widthAuto: \"Fit to content\",\n fullWidth: \"Full Width\",\n widthCustom: \"Custom\",\n },\n\n // Toolbar - Divider\n divider: {\n style: \"Style\",\n solid: \"Solid\",\n dashed: \"Dashed\",\n dotted: \"Dotted\",\n color: \"Color\",\n thickness: \"Thickness\",\n },\n\n // Toolbar - Social Icons\n social: {\n icons: \"Icons\",\n addIcon: \"Add Icon\",\n addIcons: \"Add social icons\",\n removeIcon: \"Remove\",\n urlPlaceholder: \"https://...\",\n style: \"Style\",\n styleSolid: \"Solid\",\n styleOutlined: \"Outlined\",\n styleRounded: \"Rounded\",\n styleSquare: \"Square\",\n styleCircle: \"Circle\",\n size: \"Size\",\n sizeSmall: \"S\",\n sizeMedium: \"M\",\n sizeLarge: \"L\",\n spacing: \"Spacing\",\n align: \"Alignment\",\n platforms: {\n facebook: \"Facebook\",\n twitter: \"X (Twitter)\",\n instagram: \"Instagram\",\n linkedin: \"LinkedIn\",\n youtube: \"YouTube\",\n tiktok: \"TikTok\",\n pinterest: \"Pinterest\",\n email: \"Email\",\n website: \"Website\",\n whatsapp: \"WhatsApp\",\n telegram: \"Telegram\",\n discord: \"Discord\",\n snapchat: \"Snapchat\",\n reddit: \"Reddit\",\n github: \"GitHub\",\n dribbble: \"Dribbble\",\n behance: \"Behance\",\n },\n },\n\n // Toolbar - Menu\n menu: {\n items: \"Menu Items\",\n addItem: \"Add Item\",\n removeItem: \"Remove\",\n text: \"Text\",\n urlPlaceholder: \"https://example.com\",\n openInNewTab: \"Open in new tab\",\n bold: \"Bold\",\n underline: \"Underline\",\n color: \"Color\",\n linkColor: \"Link Color\",\n fontSize: \"Font Size\",\n fontFamily: \"Font Family\",\n separator: \"Separator\",\n separatorColor: \"Separator Color\",\n spacing: \"Spacing\",\n textAlign: \"Alignment\",\n addLinks: \"Add menu links\",\n },\n\n // Toolbar - Table\n table: {\n dimensions: \"Dimensions\",\n rows: \"Rows\",\n columns: \"Columns\",\n hasHeaderRow: \"Header row\",\n headerBackgroundColor: \"Header Background\",\n noHeaderBg: \"No background\",\n borderColor: \"Border Color\",\n borderWidth: \"Border Width\",\n cellPadding: \"Cell Padding\",\n fontFamily: \"Font Family\",\n fontSize: \"Font Size\",\n color: \"Text Color\",\n textAlign: \"Alignment\",\n cellPlaceholder: \"Enter text...\",\n empty: \"Add a table\",\n },\n\n // Toolbar - Paragraph\n paragraph: {\n paragraphSpacing: \"Paragraph Spacing\",\n },\n\n // Toolbar - Spacer\n spacer: {\n height: \"Height\",\n },\n\n // Toolbar - Countdown\n countdown: {\n targetDate: \"Target Date\",\n timezone: \"Timezone\",\n display: \"Display\",\n days: \"Days\",\n hours: \"Hours\",\n minutes: \"Minutes\",\n seconds: \"Seconds\",\n separator: \"Separator\",\n fontFamily: \"Font Family\",\n inheritFont: \"Default\",\n digitFontSize: \"Digit Size\",\n digitColor: \"Digit Color\",\n labelColor: \"Label Color\",\n labelFontSize: \"Label Size\",\n background: \"Background\",\n labels: \"Labels\",\n expiry: \"Expired Message\",\n expiredMessagePlaceholder: \"This offer has expired\",\n expiredImageUrl: \"Expired Image URL\",\n hideOnExpiry: \"Hide when expired\",\n setDate: \"Set a target date in the settings panel\",\n hidden: \"Hidden (expired)\",\n },\n\n // Custom Blocks\n customBlocks: {\n definitionNotFound: \"Unknown block type — this block is not registered\",\n renderError:\n \"This block could not be rendered. Check the block template for errors.\",\n fields: {\n required: \"Required\",\n addItem: \"Add item\",\n removeItem: \"Remove\",\n maxItemsReached: \"Maximum items reached\",\n minItemsRequired: \"Minimum {count} items required\",\n },\n toolbar: {\n noDefinition:\n \"Register this block type in your SDK configuration to edit its properties\",\n },\n dataSource: {\n fetchButton: \"Load content\",\n changeButton: \"Change\",\n fetching: \"Loading...\",\n readOnlyTooltip: \"This value is loaded from your data source\",\n fetchError: \"Failed to load content\",\n },\n },\n\n // Toolbar - HTML\n html: {\n content: \"HTML Content\",\n preview: \"Custom HTML block\",\n empty: \"Add HTML content in the panel\",\n sanitizationHint:\n \"HTML is exported as-is and isn't sanitized — make sure the content is safe.\",\n },\n\n // Toolbar - Common block settings\n blockSettings: {\n spacing: \"Spacing\",\n padding: \"Padding\",\n background: \"Background\",\n borderWidth: \"Border Width\",\n borderStyle: \"Border Style\",\n borderColor: \"Border Color\",\n border: \"Border\",\n borderSide: \"Side\",\n linkSides: \"Same border on all sides\",\n unlinkSides: \"Set each side separately\",\n linkCorners: \"Same radius on all corners\",\n unlinkCorners: \"Set each corner separately\",\n cornerTopLeft: \"Top left\",\n cornerTopRight: \"Top right\",\n cornerBottomRight: \"Bottom right\",\n cornerBottomLeft: \"Bottom left\",\n color: \"Color\",\n display: \"Display\",\n showOnDesktop: \"Show on desktop\",\n showOnMobile: \"Show on mobile\",\n hiddenOnDevice: \"Hidden on {device}\",\n displayCondition: \"Display Condition\",\n selectCondition: \"Select condition\",\n removeCondition: \"Remove condition\",\n noCondition: \"Always visible\",\n conditionApplied: \"Condition applied\",\n customCondition: \"Custom condition\",\n customConditionLabel: \"Condition name\",\n customConditionBefore: \"Before (opening logic)\",\n customConditionAfter: \"After (closing logic)\",\n applyCondition: \"Apply\",\n cancelCondition: \"Cancel\",\n customBadge: \"Custom\",\n restoreHiddenBlocks: \"Show all hidden blocks\",\n },\n\n // Template settings\n templateSettings: {\n layout: \"Layout\",\n widthPreset: \"Width Preset\",\n customWidth: \"Custom Width\",\n appearance: \"Appearance\",\n backgroundColor: \"Background Color\",\n textColor: \"Text Color\",\n linkColor: \"Link Color\",\n linkUnderline: \"Underline Links\",\n fontFamily: \"Font Family\",\n preheaderText: \"Preheader Text\",\n preheaderTextPlaceholder:\n \"Preview text shown after subject line in inbox...\",\n preheaderTextHint:\n \"This text appears after the subject line in email client previews. Supports merge tags.\",\n language: \"Language\",\n contentLocale: \"Content language\",\n contentLocaleHint:\n \"BCP 47 code (e.g. en, de, pt-BR). Sets the rendered email's lang attribute so screen readers pronounce content correctly.\",\n contentDirection: \"Right-to-left\",\n contentDirectionHint:\n \"Writing direction of the email, independent of the editor's own language. When unset, follows the content language (Arabic, Hebrew, …).\",\n tips: \"Tips\",\n tip1: \"600px is the standard width for email templates\",\n tip2: \"Use web-safe fonts for best compatibility\",\n tip3: \"Light backgrounds work best for readability\",\n },\n\n // Spacing control\n spacingControl: {\n lockAll: \"Lock all sides\",\n unlock: \"Unlock sides\",\n top: \"Top\",\n right: \"Right\",\n bottom: \"Bottom\",\n left: \"Left\",\n decreaseTop: \"Decrease top\",\n increaseTop: \"Increase top\",\n decreaseLeft: \"Decrease left\",\n increaseLeft: \"Increase left\",\n decreaseRight: \"Decrease right\",\n increaseRight: \"Increase right\",\n decreaseBottom: \"Decrease bottom\",\n increaseBottom: \"Increase bottom\",\n },\n\n // Color Picker\n colorPicker: {\n pickColor: \"Pick a color\",\n hexValue: \"Hex color value\",\n notSet: \"Not set\",\n clear: \"Clear color\",\n transparent: \"Transparent\",\n presetColors: \"Preset colors\",\n },\n\n // Merge Tag\n mergeTag: {\n editAsText: \"Edit as text\",\n fieldGroup: \"Value with merge tags\",\n remove: \"Remove merge tag\",\n insert: \"Insert merge tag\",\n insertShort: \"Merge tag\",\n editValue: \"Edit merge tag value\",\n changeTag: \"Change merge tag: {label}\",\n unknownLabel: \"Merge tag\",\n deleteMergeTag: \"Delete merge tag\",\n suggestionEmpty: \"No matching merge tags\",\n picker: {\n title: \"Insert merge tag\",\n changeTitle: \"Change merge tag\",\n searchPlaceholder: \"Search merge tags\",\n searchAriaLabel: \"Search merge tags\",\n noResults: \"No matching merge tags\",\n empty: \"No merge tags configured\",\n otherGroup: \"Other\",\n close: \"Close\",\n groupCount: \"{count}\",\n sample: \"Preview: {sample}\",\n },\n },\n\n // Logic tags (standalone, separate from merge tags)\n logicTag: {\n insert: \"Insert logic\",\n insertShort: \"Logic\",\n picker: {\n title: \"Insert logic\",\n searchPlaceholder: \"Search logic tags\",\n searchAriaLabel: \"Search logic tags\",\n noResults: \"No matching logic tags\",\n empty: \"No logic tags configured\",\n otherGroup: \"Other\",\n close: \"Close\",\n },\n },\n\n // Canvas\n canvas: {\n noBlocks: \"No blocks yet\",\n dragHint: \"Start from scratch by dragging blocks from the sidebar\",\n dropHere: \"Drop here\",\n aiHintChat: \"or let\",\n aiHintChatSuffix: \"generate a complete template for you in seconds\",\n aiHintDesign:\n \"Have an existing design? Upload a screenshot, image, or PDF and\",\n aiHintDesignSuffix: \"will instantly recreate it\",\n },\n\n // Sidebar\n savedBlocks: {\n title: \"Saved Blocks\",\n saveAsBlock: \"Save as Block\",\n name: \"Name\",\n namePlaceholder: \"e.g. Header, Footer, CTA...\",\n save: \"Save Block\",\n saving: \"Saving...\",\n cancel: \"Cancel\",\n empty: \"No saved blocks yet\",\n emptyHint: \"Save blocks from your templates to reuse them later.\",\n noResults: \"No saved blocks match your search\",\n search: \"Search saved blocks...\",\n loading: \"Loading saved blocks…\",\n category: \"Category\",\n categoryPlaceholder: \"e.g. Headers, Promos... (optional)\",\n allCategories: \"All categories\",\n filterByCategory: \"Filter by category\",\n insert: \"Insert\",\n rename: \"Rename\",\n delete: \"Delete\",\n deleteConfirm: \"Delete this saved block?\",\n blockCount: \"{count} block(s)\",\n browse: \"Browse Saved Blocks\",\n selectToPreview: \"Select a saved block to preview\",\n insertAtBeginning: \"At beginning\",\n insertAfterBlock: \"After {block}\",\n insertAtEnd: \"At end\",\n insertPosition: \"Insert position\",\n close: \"Close\",\n pickToolbar: \"Saved block selection\",\n pickCount: \"{count} block(s) selected\",\n pickHint: \"Click blocks to add or remove them\",\n savingCount: \"Saving {count} block(s):\",\n reorderHint: \"Drag to reorder — blocks are saved in this order\",\n reorderHandle: \"Reorder {block}, position {position} of {total}\",\n reorderAnnouncement: \"{block} moved to position {position} of {total}\",\n expand: \"Expand\",\n collapse: \"Collapse\",\n expandPreview: \"Expand {block} preview\",\n collapsePreview: \"Collapse {block} preview\",\n },\n comments: {\n button: \"Comments\",\n title: \"Comments\",\n close: \"Close\",\n placeholder: \"Write a comment...\",\n replyPlaceholder: \"Write a reply...\",\n reply: \"Reply\",\n resolve: \"Resolve\",\n unresolve: \"Unresolve\",\n delete: \"Delete\",\n edit: \"Edit\",\n cancel: \"Cancel\",\n save: \"Save\",\n noComments: \"No comments yet\",\n noCommentsHint:\n \"Start a conversation by adding a comment to the template or a specific block.\",\n deleteConfirm: \"Delete this comment?\",\n filterAll: \"All\",\n filterUnresolved: \"Unresolved\",\n filterBlock: \"This block\",\n jumpToBlock: \"Block\",\n ownedByYou: \"You\",\n edited: \"edited\",\n resolvedBy: \"Resolved by {name}\",\n replyOne: \"{count} Reply\",\n replyMany: \"{count} Replies\",\n missingBlock: \"Missing block\",\n saveTemplateFirst: \"Save the template before commenting on this block.\",\n },\n versionHistory: {\n tooltip: \"Version history\",\n dropdownTitle: \"Version history\",\n empty: \"No versions yet\",\n auto: \"auto\",\n olderVersion: \"Older version\",\n newerVersion: \"Newer version\",\n },\n versionPreview: {\n message: \"You are previewing an earlier version of this template.\",\n cancel: \"Cancel\",\n restore: \"Restore this version\",\n restoreConfirm: {\n title: \"Restore this version?\",\n unsavedWithSave:\n \"You have unsaved changes. Save them first and they stay in your history — restore without saving and they are lost.\",\n unsavedNoSave:\n \"You have unsaved changes and nowhere to save them. Restoring this version will lose them.\",\n saveAndRestore: \"Save, then restore\",\n restoreAnyway: \"Restore anyway\",\n cancel: \"Cancel\",\n },\n },\n previewResolution: {\n resolving: \"Resolving preview…\",\n failed: \"Preview couldn't be resolved — showing the unresolved template.\",\n hint: \"Preview uses resolved data from your backend.\",\n },\n mergeTagPreview: {\n label: \"Merge tag view\",\n sample: \"Sample\",\n labelView: \"Label\",\n },\n testEmail: {\n title: \"Send Test Email\",\n recipientLabel: \"Recipient\",\n recipientPlaceholder: \"you@example.com\",\n invalidAddress: \"Enter a valid email address.\",\n preview: \"Preview\",\n previewHint:\n \"Merge tags are shown unresolved — your backend fills them in.\",\n previewHintSample:\n \"Merge tags show example values — your backend fills in the real data.\",\n recipientNotAllowed: \"That address isn't in the allowed list.\",\n send: \"Send\",\n sending: \"Sending...\",\n cancel: \"Cancel\",\n success: \"Test email sent successfully\",\n button: \"Test\",\n },\n sidebarNav: {\n browseSavedBlocks: \"Browse saved blocks\",\n palette: \"Block palette\",\n insertBlock: \"Insert {block} block\",\n },\n\n // Landmark region labels for assistive technology\n landmarks: {\n canvas: \"Email canvas\",\n blockToolbar: \"Block properties\",\n rightSidebar: \"Block properties and template settings\",\n reorderAnnouncements: \"Block reorder announcements\",\n },\n\n // Design Reference (cloud)\n errors: {\n editorLoading: \"Loading editor...\",\n editorLoadFailed: \"Failed to load editor.\",\n retry: \"Retry\",\n },\n\n issues: {\n panelTitle: \"Issues\",\n panelTabLabel: \"Issues\",\n groupErrors: \"Errors\",\n groupWarnings: \"Warnings\",\n groupInfo: \"Info\",\n jump: \"Jump to block\",\n fix: \"Fix\",\n emptyState: \"No issues — looking good.\",\n badgeError: \"Has errors\",\n badgeWarning: \"Has warnings\",\n issueCountTooltip: \"{count} issue(s)\",\n },\n\n smallScreen: {\n title: \"Larger screen required\",\n message:\n \"The editor needs more space than this screen offers. Open it on a tablet or desktop to start editing.\",\n },\n};\n"],"mappings":";AAAA,IAAAA,IAAe;CAEb,QAAQ,EACN,WAAW,aACb;CAIA,QAAQ;EACN,MAAM;EACN,QAAQ;EACR,OAAO;EACP,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,cAAc;EACd,QAAQ;EACR,UAAU;EACV,WAAW;EACX,WAAW;EACX,gBAAgB;EAChB,gBAAgB;CAClB;CAIA,MAAM;EACJ,SAAS;EACT,YAAY;EACZ,UAAU;EACV,SAAS;CACX;CAGA,SAAS;EACP,eAAe;EACf,MAAM;EACN,MAAM;CACR;CAGA,UAAU;EACR,OAAO;EACP,SAAS;EACT,QAAQ;CACV;CAGA,UAAU;EACR,QAAQ;EACR,SAAS;CACX;CAGA,aAAa;EACX,QAAQ;EACR,SAAS;CACX;CAGA,QAAQ;EACN,SAAS;EACT,OAAO;EACP,OAAO;EACP,WAAW;EACX,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,MAAM;EACN,MAAM;EACN,OAAO;EACP,WAAW;CACb;CAMA,eAAe;EACb,OAAO;EACP,WAAW;EACX,QAAQ;CACV;CAGA,SAAS;EACP,SAAS;EACT,UAAU;EACV,aAAa;EACb,iBAAiB;CACnB;CAGA,SAAS;EACP,WAAW;EACX,QAAQ;CACV;CAGA,aAAa;EACX,SAAS;EACT,MAAM;EACN,QAAQ;EACR,SAAS;CACX;CAGA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,QAAQ;EACR,WAAW;EACX,eAAe;EACf,WAAW;EACX,aAAa;EACb,SAAS;EACT,YAAY;EACZ,cAAc;EACd,WAAW;EACX,aAAa;EACb,YAAY;EACZ,cAAc;EACd,iBAAiB;EACjB,aAAa;EACb,YAAY;EACZ,aAAa;EACb,UAAU;EACV,aAAa;EACb,WAAW;EACX,gBAAgB;EAChB,YAAY;EACZ,eAAe;EACf,gBAAgB;CAClB;CAGA,cAAc;EACZ,MAAM;EACN,YACE;EACF,WAAW;EACX,QAAQ;EACR,kBAAkB;EAClB,aAAa;EACb,iBAAiB;EACjB,UAAU;EACV,QAAQ;EACR,OAAO;EACP,SAAS;EACT,WAAW;CACb;CAGA,SAAS;EACP,UAAU;EACV,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,cAAc;EACd,eAAe;EACf,uBACE;EACF,eAAe;CACjB;CAGA,YAAY;EACV,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,YAAY;EACZ,QAAQ;EACR,gBAAgB;EAChB,UAAU;EACV,YAAY;CACd;CAGA,OAAO;EACL,OAAO;EACP,UAAU;EACV,UAAU;EACV,UAAU;EACV,UAAU;EACV,YAAY;EACZ,aAAa;EACb,OAAO;EACP,OAAO;EACP,WAAW;EACX,aAAa;EACb,YAAY;CACd;CAGA,OAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS;CACX;CAGA,OAAO;EACL,UAAU;EACV,qBAAqB;EACrB,SAAS;EACT,oBAAoB;EACpB,OAAO;EACP,WAAW;EACX,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,cAAc;EACd,cAAc;EACd,SAAS;EACT,cAAc;EACd,gBAAgB;EAChB,UAAU;EACV,2BAA2B;EAC3B,uBACE;EACF,YAAY;EACZ,aAAa;EACb,cAAc;EACd,WAAW;EACX,YAAY;EACZ,gBACE;CACJ;CAGA,OAAO;EACL,UAAU;EACV,qBAAqB;EACrB,cAAc;EACd,iBAAiB;EACjB,UAAU;EACV,sBAAsB;EACtB,SAAS;EACT,oBAAoB;EACpB,OAAO;EACP,WAAW;EACX,QAAQ;EACR,YAAY;EACZ,cAAc;EACd,gBAAgB;EAChB,2BAA2B;EAC3B,uBACE;EACF,UAAU;CACZ;CAGA,QAAQ;EACN,YAAY;EACZ,aAAa;EACb,MAAM;EACN,KAAK;EACL,gBAAgB;EAChB,cAAc;EACd,YAAY;EACZ,WAAW;EACX,cAAc;EACd,UAAU;EACV,OAAO;EACP,WAAW;EACX,WAAW;EACX,aAAa;CACf;CAGA,SAAS;EACP,OAAO;EACP,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,WAAW;CACb;CAGA,QAAQ;EACN,OAAO;EACP,SAAS;EACT,UAAU;EACV,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,YAAY;EACZ,eAAe;EACf,cAAc;EACd,aAAa;EACb,aAAa;EACb,MAAM;EACN,WAAW;EACX,YAAY;EACZ,WAAW;EACX,SAAS;EACT,OAAO;EACP,WAAW;GACT,UAAU;GACV,SAAS;GACT,WAAW;GACX,UAAU;GACV,SAAS;GACT,QAAQ;GACR,WAAW;GACX,OAAO;GACP,SAAS;GACT,UAAU;GACV,UAAU;GACV,SAAS;GACT,UAAU;GACV,QAAQ;GACR,QAAQ;GACR,UAAU;GACV,SAAS;EACX;CACF;CAGA,MAAM;EACJ,OAAO;EACP,SAAS;EACT,YAAY;EACZ,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,MAAM;EACN,WAAW;EACX,OAAO;EACP,WAAW;EACX,UAAU;EACV,YAAY;EACZ,WAAW;EACX,gBAAgB;EAChB,SAAS;EACT,WAAW;EACX,UAAU;CACZ;CAGA,OAAO;EACL,YAAY;EACZ,MAAM;EACN,SAAS;EACT,cAAc;EACd,uBAAuB;EACvB,YAAY;EACZ,aAAa;EACb,aAAa;EACb,aAAa;EACb,YAAY;EACZ,UAAU;EACV,OAAO;EACP,WAAW;EACX,iBAAiB;EACjB,OAAO;CACT;CAGA,WAAW,EACT,kBAAkB,oBACpB;CAGA,QAAQ,EACN,QAAQ,SACV;CAGA,WAAW;EACT,YAAY;EACZ,UAAU;EACV,SAAS;EACT,MAAM;EACN,OAAO;EACP,SAAS;EACT,SAAS;EACT,WAAW;EACX,YAAY;EACZ,aAAa;EACb,eAAe;EACf,YAAY;EACZ,YAAY;EACZ,eAAe;EACf,YAAY;EACZ,QAAQ;EACR,QAAQ;EACR,2BAA2B;EAC3B,iBAAiB;EACjB,cAAc;EACd,SAAS;EACT,QAAQ;CACV;CAGA,cAAc;EACZ,oBAAoB;EACpB,aACE;EACF,QAAQ;GACN,UAAU;GACV,SAAS;GACT,YAAY;GACZ,iBAAiB;GACjB,kBAAkB;EACpB;EACA,SAAS,EACP,cACE,4EACJ;EACA,YAAY;GACV,aAAa;GACb,cAAc;GACd,UAAU;GACV,iBAAiB;GACjB,YAAY;EACd;CACF;CAGA,MAAM;EACJ,SAAS;EACT,SAAS;EACT,OAAO;EACP,kBACE;CACJ;CAGA,eAAe;EACb,SAAS;EACT,SAAS;EACT,YAAY;EACZ,aAAa;EACb,aAAa;EACb,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,aAAa;EACb,aAAa;EACb,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,mBAAmB;EACnB,kBAAkB;EAClB,OAAO;EACP,SAAS;EACT,eAAe;EACf,cAAc;EACd,gBAAgB;EAChB,kBAAkB;EAClB,iBAAiB;EACjB,iBAAiB;EACjB,aAAa;EACb,kBAAkB;EAClB,iBAAiB;EACjB,sBAAsB;EACtB,uBAAuB;EACvB,sBAAsB;EACtB,gBAAgB;EAChB,iBAAiB;EACjB,aAAa;EACb,qBAAqB;CACvB;CAGA,kBAAkB;EAChB,QAAQ;EACR,aAAa;EACb,aAAa;EACb,YAAY;EACZ,iBAAiB;EACjB,WAAW;EACX,WAAW;EACX,eAAe;EACf,YAAY;EACZ,eAAe;EACf,0BACE;EACF,mBACE;EACF,UAAU;EACV,eAAe;EACf,mBACE;EACF,kBAAkB;EAClB,sBACE;EACF,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;CACR;CAGA,gBAAgB;EACd,SAAS;EACT,QAAQ;EACR,KAAK;EACL,OAAO;EACP,QAAQ;EACR,MAAM;EACN,aAAa;EACb,aAAa;EACb,cAAc;EACd,cAAc;EACd,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,gBAAgB;CAClB;CAGA,aAAa;EACX,WAAW;EACX,UAAU;EACV,QAAQ;EACR,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CAGA,UAAU;EACR,YAAY;EACZ,YAAY;EACZ,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,WAAW;EACX,WAAW;EACX,cAAc;EACd,gBAAgB;EAChB,iBAAiB;EACjB,QAAQ;GACN,OAAO;GACP,aAAa;GACb,mBAAmB;GACnB,iBAAiB;GACjB,WAAW;GACX,OAAO;GACP,YAAY;GACZ,OAAO;GACP,YAAY;GACZ,QAAQ;EACV;CACF;CAGA,UAAU;EACR,QAAQ;EACR,aAAa;EACb,QAAQ;GACN,OAAO;GACP,mBAAmB;GACnB,iBAAiB;GACjB,WAAW;GACX,OAAO;GACP,YAAY;GACZ,OAAO;EACT;CACF;CAGA,QAAQ;EACN,UAAU;EACV,UAAU;EACV,UAAU;EACV,YAAY;EACZ,kBAAkB;EAClB,cACE;EACF,oBAAoB;CACtB;CAGA,aAAa;EACX,OAAO;EACP,aAAa;EACb,MAAM;EACN,iBAAiB;EACjB,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,WAAW;EACX,WAAW;EACX,QAAQ;EACR,SAAS;EACT,UAAU;EACV,qBAAqB;EACrB,eAAe;EACf,kBAAkB;EAClB,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,eAAe;EACf,YAAY;EACZ,QAAQ;EACR,iBAAiB;EACjB,mBAAmB;EACnB,kBAAkB;EAClB,aAAa;EACb,gBAAgB;EAChB,OAAO;EACP,aAAa;EACb,WAAW;EACX,UAAU;EACV,aAAa;EACb,aAAa;EACb,eAAe;EACf,qBAAqB;EACrB,QAAQ;EACR,UAAU;EACV,eAAe;EACf,iBAAiB;CACnB;CACA,UAAU;EACR,QAAQ;EACR,OAAO;EACP,OAAO;EACP,aAAa;EACb,kBAAkB;EAClB,OAAO;EACP,SAAS;EACT,WAAW;EACX,QAAQ;EACR,MAAM;EACN,QAAQ;EACR,MAAM;EACN,YAAY;EACZ,gBACE;EACF,eAAe;EACf,WAAW;EACX,kBAAkB;EAClB,aAAa;EACb,aAAa;EACb,YAAY;EACZ,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,WAAW;EACX,cAAc;EACd,mBAAmB;CACrB;CACA,gBAAgB;EACd,SAAS;EACT,eAAe;EACf,OAAO;EACP,MAAM;EACN,cAAc;EACd,cAAc;CAChB;CACA,gBAAgB;EACd,SAAS;EACT,QAAQ;EACR,SAAS;EACT,gBAAgB;GACd,OAAO;GACP,iBACE;GACF,eACE;GACF,gBAAgB;GAChB,eAAe;GACf,QAAQ;EACV;CACF;CACA,mBAAmB;EACjB,WAAW;EACX,QAAQ;EACR,MAAM;CACR;CACA,iBAAiB;EACf,OAAO;EACP,QAAQ;EACR,WAAW;CACb;CACA,WAAW;EACT,OAAO;EACP,gBAAgB;EAChB,sBAAsB;EACtB,gBAAgB;EAChB,SAAS;EACT,aACE;EACF,mBACE;EACF,qBAAqB;EACrB,MAAM;EACN,SAAS;EACT,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA,YAAY;EACV,mBAAmB;EACnB,SAAS;EACT,aAAa;CACf;CAGA,WAAW;EACT,QAAQ;EACR,cAAc;EACd,cAAc;EACd,sBAAsB;CACxB;CAGA,QAAQ;EACN,eAAe;EACf,kBAAkB;EAClB,OAAO;CACT;CAEA,QAAQ;EACN,YAAY;EACZ,eAAe;EACf,aAAa;EACb,eAAe;EACf,WAAW;EACX,MAAM;EACN,KAAK;EACL,YAAY;EACZ,YAAY;EACZ,cAAc;EACd,mBAAmB;CACrB;CAEA,aAAa;EACX,OAAO;EACP,SACE;CACJ;AACF"}
@@ -0,0 +1,2 @@
1
+ import { i as e } from "./editor-modal-cKeJ4wC2.js";
2
+ export { e as default };
@@ -13,7 +13,9 @@ var e = { mediaLibrary: {
13
13
  noFiles: "No se han encontrado archivos",
14
14
  noSearchResults: "Ningún archivo coincide con tu búsqueda",
15
15
  dropOrClick: "Suelta los archivos aquí o haz clic para subirlos",
16
- acceptedFormats: "Imágenes, PDF, vídeo, audio y documentos (máx. 10 MB)",
16
+ acceptedAnyType: "Cualquier tipo de archivo",
17
+ acceptedFormatsMaxSize: "{formats} (máx. {size})",
18
+ uploadRejected: "No se ha subido, tipo o tamaño no admitido: {files}",
17
19
  uploading: "Subiendo...",
18
20
  uploadingProgress: "Subiendo {current} de {total}...",
19
21
  selectImage: "Seleccionar imagen",
@@ -60,12 +62,14 @@ var e = { mediaLibrary: {
60
62
  replaceFile: "Reemplazar archivo",
61
63
  replaceWarningTitle: "Reemplazar archivo",
62
64
  replaceWarningMessage: "Estás a punto de reemplazar este archivo. El archivo de reemplazo debe tener la misma extensión ({extension}).",
63
- replaceWarningUsageNote: "Este archivo se utiliza en {count} plantilla(s). Al reemplazarlo, se actualizarán todas las referencias.",
65
+ replaceWarningUsageNote: "Este archivo se utiliza en {count} plantilla(s). Revísalas después de reemplazarlo.",
64
66
  replaceSelectFile: "Seleccionar archivo de reemplazo",
65
67
  replace: "Reemplazar",
66
68
  replacing: "Reemplazando...",
67
69
  replaceError: "No se ha podido reemplazar el archivo",
68
70
  saving: "Guardando...",
71
+ editSaveError: "No se han podido guardar los cambios. Inténtalo de nuevo.",
72
+ editCropError: "No se ha podido procesar la imagen editada. Inténtalo de nuevo.",
69
73
  cropAspectRatio: "Relación de aspecto",
70
74
  cropFree: "Libre",
71
75
  cropSquare: "1:1",
@@ -91,4 +95,4 @@ var e = { mediaLibrary: {
91
95
  //#endregion
92
96
  export { e as default };
93
97
 
94
- //# sourceMappingURL=es-BT4LSpE2.js.map
98
+ //# sourceMappingURL=es-CAv-Vldw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"es-CAv-Vldw.js","names":["es"],"sources":["../../../../media-library/src/i18n/locales/es.ts"],"sourcesContent":["const es = {\n mediaLibrary: {\n title: \"Biblioteca multimedia\",\n searchPlaceholder: \"Buscar archivos...\",\n allFiles: \"Todos los archivos\",\n filterAll: \"Todos los tipos\",\n filterImages: \"Imágenes\",\n filterDocuments: \"Documentos\",\n filterVideos: \"Vídeos\",\n filterAudio: \"Audio\",\n newFolder: \"Nueva carpeta\",\n folderName: \"Nombre de la carpeta\",\n noFiles: \"No se han encontrado archivos\",\n noSearchResults: \"Ningún archivo coincide con tu búsqueda\",\n dropOrClick: \"Suelta los archivos aquí o haz clic para subirlos\",\n acceptedAnyType: \"Cualquier tipo de archivo\",\n acceptedFormatsMaxSize: \"{formats} (máx. {size})\",\n uploadRejected: \"No se ha subido, tipo o tamaño no admitido: {files}\",\n uploading: \"Subiendo...\",\n uploadingProgress: \"Subiendo {current} de {total}...\",\n selectImage: \"Seleccionar imagen\",\n selectFile: \"Seleccionar archivo\",\n deleteSelected: \"Eliminar\",\n copyUrl: \"Copiar URL\",\n copied: \"¡Copiada!\",\n browseMedia: \"Explorar la biblioteca multimedia\",\n renameFolder: \"Cambiar nombre de la carpeta\",\n addSubfolder: \"Añadir subcarpeta\",\n subfolderName: \"Nombre de la subcarpeta\",\n sortNewest: \"Más recientes primero\",\n sortOldest: \"Más antiguos primero\",\n sortNameAsc: \"Nombre de A a Z\",\n sortNameDesc: \"Nombre de Z a A\",\n sortSizeAsc: \"Más pequeños primero\",\n sortSizeDesc: \"Más grandes primero\",\n moveSelected: \"Mover\",\n moveToRoot: \"Todos los archivos\",\n currentFolder: \"(actual)\",\n confirmDelete: \"¿Confirmar?\",\n renameFile: \"Cambiar nombre\",\n editFile: \"Editar archivo\",\n fileName: \"Nombre del archivo\",\n altText: \"Texto alternativo\",\n altTextPlaceholder: \"Describe esta imagen para mejorar la accesibilidad\",\n saveChanges: \"Guardar\",\n cancel: \"Cancelar\",\n frequentlyUsed: \"Usados frecuentemente\",\n deleteWarningTitle: \"Eliminar archivo\",\n deleteWarningMessage: \"¿Estás seguro? Esta acción es irreversible.\",\n deleteWarningUsageNote:\n \"Los siguientes archivos se utilizan en plantillas. Si los eliminas, es posible que esas plantillas dejen de funcionar correctamente.\",\n deleteAnyway: \"Eliminar\",\n usedInTemplates: \"Usado en {count} plantilla(s)\",\n viewGrid: \"Vista de cuadrícula\",\n viewList: \"Vista de lista\",\n showFolders: \"Mostrar carpetas\",\n hideFolders: \"Ocultar carpetas\",\n importFromUrl: \"Importar desde URL\",\n importUrlPlaceholder: \"https://example.com/image.jpg\",\n import: \"Importar\",\n importing: \"Importando...\",\n importError: \"No se ha podido importar desde la URL\",\n replaceFile: \"Reemplazar archivo\",\n replaceWarningTitle: \"Reemplazar archivo\",\n replaceWarningMessage:\n \"Estás a punto de reemplazar este archivo. El archivo de reemplazo debe tener la misma extensión ({extension}).\",\n replaceWarningUsageNote:\n \"Este archivo se utiliza en {count} plantilla(s). Revísalas después de reemplazarlo.\",\n replaceSelectFile: \"Seleccionar archivo de reemplazo\",\n replace: \"Reemplazar\",\n replacing: \"Reemplazando...\",\n replaceError: \"No se ha podido reemplazar el archivo\",\n saving: \"Guardando...\",\n editSaveError: \"No se han podido guardar los cambios. Inténtalo de nuevo.\",\n editCropError:\n \"No se ha podido procesar la imagen editada. Inténtalo de nuevo.\",\n cropAspectRatio: \"Relación de aspecto\",\n cropFree: \"Libre\",\n cropSquare: \"1:1\",\n cropLandscape43: \"4:3\",\n cropLandscape169: \"16:9\",\n cropOriginal: \"Original\",\n cropMaxWidth: \"Anchura máxima\",\n cropMaxHeight: \"Altura máxima\",\n cropOutputSize: \"Tamaño de salida\",\n cropPixels: \"px\",\n cropOptional: \"(opcional)\",\n close: \"Cerrar\",\n searchAriaLabel: \"Buscar archivos\",\n filterAriaLabel: \"Filtrar por tipo\",\n filesListAriaLabel: \"Archivos\",\n loading: \"Cargando\",\n expandFolder: \"Expandir carpeta\",\n collapseFolder: \"Contraer carpeta\",\n deleteFolder: \"Eliminar carpeta\",\n deleteFolderConfirm: \"Confirmar eliminación\",\n storageTooltip: \"{used} de {total} usados ({remaining} restantes)\",\n },\n};\n\nexport default es;\n"],"mappings":";AAAA,IAAMA,IAAK,EACT,cAAc;CACZ,OAAO;CACP,mBAAmB;CACnB,UAAU;CACV,WAAW;CACX,cAAc;CACd,iBAAiB;CACjB,cAAc;CACd,aAAa;CACb,WAAW;CACX,YAAY;CACZ,SAAS;CACT,iBAAiB;CACjB,aAAa;CACb,iBAAiB;CACjB,wBAAwB;CACxB,gBAAgB;CAChB,WAAW;CACX,mBAAmB;CACnB,aAAa;CACb,YAAY;CACZ,gBAAgB;CAChB,SAAS;CACT,QAAQ;CACR,aAAa;CACb,cAAc;CACd,cAAc;CACd,eAAe;CACf,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,cAAc;CACd,aAAa;CACb,cAAc;CACd,cAAc;CACd,YAAY;CACZ,eAAe;CACf,eAAe;CACf,YAAY;CACZ,UAAU;CACV,UAAU;CACV,SAAS;CACT,oBAAoB;CACpB,aAAa;CACb,QAAQ;CACR,gBAAgB;CAChB,oBAAoB;CACpB,sBAAsB;CACtB,wBACE;CACF,cAAc;CACd,iBAAiB;CACjB,UAAU;CACV,UAAU;CACV,aAAa;CACb,aAAa;CACb,eAAe;CACf,sBAAsB;CACtB,QAAQ;CACR,WAAW;CACX,aAAa;CACb,aAAa;CACb,qBAAqB;CACrB,uBACE;CACF,yBACE;CACF,mBAAmB;CACnB,SAAS;CACT,WAAW;CACX,cAAc;CACd,QAAQ;CACR,eAAe;CACf,eACE;CACF,iBAAiB;CACjB,UAAU;CACV,YAAY;CACZ,iBAAiB;CACjB,kBAAkB;CAClB,cAAc;CACd,cAAc;CACd,eAAe;CACf,gBAAgB;CAChB,YAAY;CACZ,cAAc;CACd,OAAO;CACP,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,SAAS;CACT,cAAc;CACd,gBAAgB;CAChB,cAAc;CACd,qBAAqB;CACrB,gBAAgB;AAClB,EACF"}
@@ -1,9 +1,6 @@
1
1
  //#region src/i18n/locales/es.ts
2
2
  var e = {
3
- footer: {
4
- poweredBy: "Con la tecnología de",
5
- openSource: "Código abierto"
6
- },
3
+ footer: { poweredBy: "Con la tecnología de" },
7
4
  header: {
8
5
  save: "Guardar",
9
6
  saving: "Guardando...",
@@ -636,4 +633,4 @@ var e = {
636
633
  //#endregion
637
634
  export { e as default };
638
635
 
639
- //# sourceMappingURL=es-BIh8BxV9.js.map
636
+ //# sourceMappingURL=es-oQI-dfx2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"es-oQI-dfx2.js","names":["es"],"sources":["../../../src/i18n/locales/es.ts"],"sourcesContent":["import type en from \"./en\";\n\nconst es: typeof en = {\n // Footer (OSS only)\n footer: {\n poweredBy: \"Con la tecnología de\",\n },\n\n // Header — nombre de la plantilla + estado de guardado. Solo se muestra cuando\n // hay un proveedor de plantillas configurado.\n header: {\n save: \"Guardar\",\n saving: \"Guardando...\",\n saved: \"Guardado\",\n unsaved: \"Sin guardar\",\n saveFailed: \"Error al guardar\",\n saveNoTemplate: \"Carga o crea una plantilla primero\",\n templateName: \"Nombre de la plantilla\",\n rename: \"Cambiar el nombre de la plantilla\",\n untitled: \"Sin título\",\n updatedAt: \"Actualizado {time}\",\n createdAt: \"Creado {time}\",\n updatedJustNow: \"Actualizado ahora mismo\",\n createdJustNow: \"Creado ahora mismo\",\n },\n\n // Relative timestamps, shared by every surface that shows one: the header's\n // updated line, saved blocks, comments and version history.\n time: {\n justNow: \"Ahora mismo\",\n minutesAgo: \"hace {minutes} min\",\n hoursAgo: \"hace {hours} h\",\n daysAgo: \"hace {days} d\",\n },\n\n // History (undo/redo)\n history: {\n collabWarning:\n \"Deshacer puede afectar a los cambios recientes de los colaboradores\",\n undo: \"Deshacer\",\n redo: \"Rehacer\",\n },\n\n // Viewport toggle\n viewport: {\n label: \"Área de visualización\",\n desktop: \"Escritorio\",\n mobile: \"Móvil\",\n },\n\n // Dark mode preview\n darkMode: {\n enable: \"Vista previa en modo oscuro\",\n disable: \"Vista previa en modo claro\",\n },\n\n // Preview mode\n previewMode: {\n enable: \"Modo vista previa\",\n disable: \"Salir de la vista previa\",\n },\n\n // Sidebar - Block types\n blocks: {\n section: \"Sección\",\n image: \"Imagen\",\n title: \"Título\",\n paragraph: \"Párrafo\",\n button: \"Botón\",\n divider: \"Divisor\",\n video: \"Vídeo\",\n social: \"Redes sociales\",\n spacer: \"Espaciador\",\n html: \"HTML\",\n menu: \"Menú\",\n table: \"Tabla\",\n countdown: \"Cuenta atrás\",\n },\n\n // Placeholder text a newly inserted block starts with. Author-facing\n // prompts, meant to be overwritten — so they follow the editor UI locale.\n // Recipient-facing defaults live in `i18n/contentDefaults.ts` and follow\n // the template's own `settings.locale` instead.\n blockDefaults: {\n title: \"Escribe tu título\",\n paragraph: \"Escribe tu texto aquí\",\n button: \"Haz clic aquí\",\n },\n\n // Right sidebar\n sidebar: {\n content: \"Contenido\",\n settings: \"Configuración\",\n noSelection: \"Ningún elemento seleccionado\",\n noSelectionHint: \"Selecciona un bloque en el lienzo para editarlo\",\n },\n\n // Toolbar - Common\n toolbar: {\n duplicate: \"Duplicar\",\n delete: \"Eliminar\",\n },\n\n // Title editor toolbar\n titleEditor: {\n toolbar: \"Formato del título\",\n bold: \"Negrita (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n addLink: \"Añadir enlace\",\n },\n\n // Paragraph editor toolbar\n paragraphEditor: {\n toolbar: \"Formato del texto\",\n bold: \"Negrita (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n underline: \"Subrayado (Ctrl+U)\",\n strikethrough: \"Tachado\",\n subscript: \"Subíndice\",\n superscript: \"Superíndice\",\n addLink: \"Añadir enlace\",\n bulletList: \"Lista con viñetas\",\n numberedList: \"Lista numerada\",\n alignLeft: \"Alinear a la izquierda\",\n alignCenter: \"Alinear al centro\",\n alignRight: \"Alinear a la derecha\",\n alignJustify: \"Justificar\",\n clearFormatting: \"Borrar formato\",\n insertEmoji: \"Insertar emoji\",\n fontFamily: \"Familia tipográfica\",\n defaultFont: \"Por defecto\",\n fontSize: \"Tamaño de fuente\",\n defaultSize: \"Por defecto\",\n textColor: \"Color del texto\",\n highlightColor: \"Color de resaltado\",\n lineHeight: \"Interlineado\",\n letterSpacing: \"Espaciado entre letras\",\n emojiItemLabel: \"Insertar emoji {emoji}\",\n },\n\n // Block actions (BlockWrapper)\n blockActions: {\n drag: \"Arrastra para reordenar o pulsa Espacio para mover con el teclado\",\n dragLifted:\n \"Moviendo {block}. Usa las flechas arriba y abajo para reposicionar, Espacio o Intro para soltar, Esc para cancelar.\",\n duplicate: \"Duplicar bloque\",\n delete: \"Eliminar bloque\",\n hiddenOnViewport: \"Oculto en {viewport}\",\n saveAsBlock: \"Guardar como bloque\",\n conditionToggle: \"Alternar condición de visualización\",\n comments: \"Comentarios ({count})\",\n lifted: \"{block} levantado. Posición {position} de {total}.\",\n moved: \"{block} movido a la posición {position} de {total}.\",\n dropped: \"{block} soltado en la posición {position} de {total}.\",\n cancelled: \"Movimiento cancelado. {block} volvió a la posición {position}.\",\n },\n\n // Toolbar - Section\n section: {\n dropHere: \"Suelta los bloques aquí\",\n columns: \"Columnas\",\n column1: \"1 columna\",\n column2: \"2 columnas\",\n column3: \"3 columnas\",\n ratio12: \"Proporción 1:2\",\n ratio21: \"Proporción 2:1\",\n borderRadius: \"Radio del borde\",\n wrapperEnable: \"Añadir contenedor\",\n wrapperLayoutConflict:\n \"Este editor ya enmarca el correo. Un marco extra en esta sección no es compatible — la vista previa y la exportación fallarán.\",\n stackOnMobile: \"Apilar en móvil\",\n },\n\n // Text editor link dialog\n linkDialog: {\n editLink: \"Editar enlace\",\n insertLink: \"Insertar enlace\",\n updateLink: \"Actualizar enlace\",\n removeLink: \"Eliminar enlace\",\n cancel: \"Cancelar\",\n urlPlaceholder: \"https://example.com\",\n urlLabel: \"URL\",\n colorLabel: \"Color del enlace\",\n },\n\n // Toolbar - Title\n title: {\n level: \"Nivel de encabezado\",\n heading1: \"Encabezado 1 (36px)\",\n heading2: \"Encabezado 2 (28px)\",\n heading3: \"Encabezado 3 (22px)\",\n heading4: \"Encabezado 4 (18px)\",\n fontFamily: \"Familia tipográfica\",\n inheritFont: \"Usar fuente de la plantilla\",\n color: \"Color\",\n align: \"Alineación\",\n alignLeft: \"Izquierda\",\n alignCenter: \"Centro\",\n alignRight: \"Derecha\",\n },\n\n // Emoji picker\n emoji: {\n smileys: \"Emoticonos\",\n gestures: \"Gestos\",\n objects: \"Objetos\",\n },\n\n // Toolbar - Image\n image: {\n imageUrl: \"URL de la imagen\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Texto alternativo\",\n altTextPlaceholder: \"Descripción de la imagen\",\n width: \"Ancho\",\n fullWidth: \"Ancho completo\",\n widthCustom: \"Personalizado\",\n height: \"Altura\",\n heightAuto: \"Automática\",\n heightCustom: \"Personalizada\",\n borderRadius: \"Radio del borde\",\n linkUrl: \"URL del enlace\",\n openInNewTab: \"Abrir en una nueva pestaña\",\n placeholderUrl: \"Imagen de muestra\",\n optional: \"(opcional)\",\n placeholderUrlPlaceholder: \"https://... (solo para diseño)\",\n placeholderUrlTooltip:\n \"Dado que la URL de la imagen utiliza una etiqueta de combinación, puedes proporcionar una imagen real aquí para previsualizar el diseño mientras diseñas. Esto no se incluirá en el resultado final.\",\n clickToAdd: \"Haz clic para añadir la URL de la imagen\",\n browseMedia: \"Explorar archivos multimedia\",\n dropToUpload: \"Suelta la imagen para subirla\",\n uploading: \"Subiendo...\",\n decorative: \"Imagen decorativa\",\n decorativeHint:\n \"Oculto para los lectores de pantalla. Utilízalo solo para espaciadores y adornos visuales.\",\n },\n\n // Toolbar - Video\n video: {\n videoUrl: \"URL del vídeo\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n openInNewTab: \"Abrir en una nueva pestaña\",\n customThumbnail: \"Miniatura personalizada\",\n optional: \"(opcional)\",\n thumbnailPlaceholder: \"Generada automáticamente desde la URL del vídeo\",\n altText: \"Texto alternativo\",\n altTextPlaceholder: \"Descripción del vídeo\",\n width: \"Ancho\",\n fullWidth: \"Ancho completo\",\n height: \"Altura\",\n heightAuto: \"Automática\",\n heightCustom: \"Personalizada\",\n placeholderUrl: \"Miniatura de muestra\",\n placeholderUrlPlaceholder: \"https://... (solo para diseño)\",\n placeholderUrlTooltip:\n \"Dado que la URL del vídeo utiliza una etiqueta de combinación, puedes proporcionar una miniatura real aquí para previsualizar el diseño mientras diseñas. Esto no se incluirá en el resultado final.\",\n addVideo: \"Añadir una URL de vídeo\",\n },\n\n // Toolbar - Button\n button: {\n fontFamily: \"Familia tipográfica\",\n inheritFont: \"Usar fuente de la plantilla\",\n text: \"Texto\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Abrir en una nueva pestaña\",\n background: \"Fondo\",\n textColor: \"Color del texto\",\n borderRadius: \"Radio del borde\",\n fontSize: \"Tamaño de fuente\",\n width: \"Ancho\",\n widthAuto: \"Ajustar al contenido\",\n fullWidth: \"Ancho completo\",\n widthCustom: \"Personalizado\",\n },\n\n // Toolbar - Divider\n divider: {\n style: \"Estilo\",\n solid: \"Sólido\",\n dashed: \"Discontinuo\",\n dotted: \"Punteado\",\n color: \"Color\",\n thickness: \"Grosor\",\n },\n\n // Toolbar - Social Icons\n social: {\n icons: \"Iconos\",\n addIcon: \"Añadir icono\",\n addIcons: \"Añadir iconos de redes sociales\",\n removeIcon: \"Eliminar\",\n urlPlaceholder: \"https://...\",\n style: \"Estilo\",\n styleSolid: \"Sólido\",\n styleOutlined: \"Contorneado\",\n styleRounded: \"Redondeado\",\n styleSquare: \"Cuadrado\",\n styleCircle: \"Círculo\",\n size: \"Tamaño\",\n sizeSmall: \"S\",\n sizeMedium: \"M\",\n sizeLarge: \"L\",\n spacing: \"Espaciado\",\n align: \"Alineación\",\n platforms: {\n facebook: \"Facebook\",\n twitter: \"X (Twitter)\",\n instagram: \"Instagram\",\n linkedin: \"LinkedIn\",\n youtube: \"YouTube\",\n tiktok: \"TikTok\",\n pinterest: \"Pinterest\",\n email: \"Correo electrónico\",\n website: \"Sitio web\",\n whatsapp: \"WhatsApp\",\n telegram: \"Telegram\",\n discord: \"Discord\",\n snapchat: \"Snapchat\",\n reddit: \"Reddit\",\n github: \"GitHub\",\n dribbble: \"Dribbble\",\n behance: \"Behance\",\n },\n },\n\n // Toolbar - Menu\n menu: {\n items: \"Elementos del menú\",\n addItem: \"Añadir elemento\",\n removeItem: \"Eliminar\",\n text: \"Texto\",\n urlPlaceholder: \"https://example.com\",\n openInNewTab: \"Abrir en una nueva pestaña\",\n bold: \"Negrita\",\n underline: \"Subrayado\",\n color: \"Color\",\n linkColor: \"Color del enlace\",\n fontSize: \"Tamaño de fuente\",\n fontFamily: \"Familia tipográfica\",\n separator: \"Separador\",\n separatorColor: \"Color del separador\",\n spacing: \"Espaciado\",\n textAlign: \"Alineación\",\n addLinks: \"Añadir enlaces al menú\",\n },\n\n // Toolbar - Table\n table: {\n dimensions: \"Dimensiones\",\n rows: \"Filas\",\n columns: \"Columnas\",\n hasHeaderRow: \"Fila de encabezado\",\n headerBackgroundColor: \"Fondo del encabezado\",\n noHeaderBg: \"Sin fondo\",\n borderColor: \"Color del borde\",\n borderWidth: \"Grosor del borde\",\n cellPadding: \"Relleno de la celda\",\n fontFamily: \"Familia tipográfica\",\n fontSize: \"Tamaño de fuente\",\n color: \"Color del texto\",\n textAlign: \"Alineación\",\n cellPlaceholder: \"Introduce el texto...\",\n empty: \"Añadir una tabla\",\n },\n\n // Toolbar - Paragraph\n paragraph: {\n paragraphSpacing: \"Espaciado entre párrafos\",\n },\n\n // Toolbar - Spacer\n spacer: {\n height: \"Altura\",\n },\n\n // Toolbar - Countdown\n countdown: {\n targetDate: \"Fecha límite\",\n timezone: \"Zona horaria\",\n display: \"Visualización\",\n days: \"Días\",\n hours: \"Horas\",\n minutes: \"Minutos\",\n seconds: \"Segundos\",\n separator: \"Separador\",\n fontFamily: \"Familia tipográfica\",\n inheritFont: \"Por defecto\",\n digitFontSize: \"Tamaño de los dígitos\",\n digitColor: \"Color de los dígitos\",\n labelColor: \"Color de la etiqueta\",\n labelFontSize: \"Tamaño de la etiqueta\",\n background: \"Fondo\",\n labels: \"Etiquetas\",\n expiry: \"Mensaje de expiración\",\n expiredMessagePlaceholder: \"Esta oferta ha expirado\",\n expiredImageUrl: \"URL de la imagen caducada\",\n hideOnExpiry: \"Ocultar al expirar\",\n setDate: \"Establece una fecha límite en el panel de configuración\",\n hidden: \"Oculto (expirado)\",\n },\n\n // Custom Blocks\n customBlocks: {\n definitionNotFound:\n \"Tipo de bloque desconocido: este bloque no está registrado\",\n renderError:\n \"No se pudo renderizar este bloque. Comprueba si hay errores en la plantilla del bloque.\",\n fields: {\n required: \"Obligatorio\",\n addItem: \"Añadir elemento\",\n removeItem: \"Eliminar\",\n maxItemsReached: \"Se ha alcanzado el máximo de elementos\",\n minItemsRequired: \"Se requiere un mínimo de {count} elementos\",\n },\n toolbar: {\n noDefinition:\n \"Registra este tipo de bloque en la configuración de tu SDK para editar sus propiedades\",\n },\n dataSource: {\n fetchButton: \"Cargar contenido\",\n changeButton: \"Cambiar\",\n fetching: \"Cargando...\",\n readOnlyTooltip: \"Este valor se carga desde tu fuente de datos\",\n fetchError: \"Error al cargar el contenido\",\n },\n },\n\n // Toolbar - HTML\n html: {\n content: \"Contenido HTML\",\n preview: \"Bloque HTML personalizado\",\n empty: \"Añadir contenido HTML en el panel\",\n sanitizationHint:\n \"El HTML se exporta tal cual y no se sanea — asegúrate de que el contenido sea seguro.\",\n },\n\n // Toolbar - Common block settings\n blockSettings: {\n spacing: \"Espaciado\",\n padding: \"Relleno\",\n background: \"Fondo\",\n borderWidth: \"Ancho del borde\",\n borderStyle: \"Estilo del borde\",\n borderColor: \"Color del borde\",\n border: \"Borde\",\n borderSide: \"Lado\",\n linkSides: \"Mismo borde en todos los lados\",\n unlinkSides: \"Definir cada lado por separado\",\n linkCorners: \"Mismo radio en todas las esquinas\",\n unlinkCorners: \"Definir cada esquina por separado\",\n cornerTopLeft: \"Arriba a la izquierda\",\n cornerTopRight: \"Arriba a la derecha\",\n cornerBottomRight: \"Abajo a la derecha\",\n cornerBottomLeft: \"Abajo a la izquierda\",\n color: \"Color\",\n display: \"Visualización\",\n showOnDesktop: \"Mostrar en escritorio\",\n showOnMobile: \"Mostrar en móvil\",\n hiddenOnDevice: \"Oculto en {device}\",\n displayCondition: \"Condición de visualización\",\n selectCondition: \"Seleccionar condición\",\n removeCondition: \"Eliminar condición\",\n noCondition: \"Siempre visible\",\n conditionApplied: \"Condición aplicada\",\n customCondition: \"Condición personalizada\",\n customConditionLabel: \"Nombre de la condición\",\n customConditionBefore: \"Antes (lógica de apertura)\",\n customConditionAfter: \"Después (lógica de cierre)\",\n applyCondition: \"Aplicar\",\n cancelCondition: \"Cancelar\",\n customBadge: \"Personalizado\",\n restoreHiddenBlocks: \"Mostrar todos los bloques ocultos\",\n },\n\n // Template settings\n templateSettings: {\n layout: \"Diseño\",\n widthPreset: \"Ancho predefinido\",\n customWidth: \"Ancho personalizado\",\n appearance: \"Apariencia\",\n backgroundColor: \"Color de fondo\",\n textColor: \"Color del texto\",\n linkColor: \"Color del enlace\",\n linkUnderline: \"Subrayar enlaces\",\n fontFamily: \"Familia tipográfica\",\n preheaderText: \"Texto del preencabezado\",\n preheaderTextPlaceholder:\n \"Texto de vista previa que se muestra después del asunto en la bandeja de entrada...\",\n preheaderTextHint:\n \"Este texto aparece después del asunto en las vistas previas de los clientes de correo electrónico. Admite etiquetas de combinación.\",\n language: \"Idioma\",\n contentLocale: \"Idioma del contenido\",\n contentLocaleHint:\n \"Código BCP 47 (por ejemplo, es, en, de). Establece el atributo lang del correo electrónico renderizado para que los lectores de pantalla pronuncien el contenido correctamente.\",\n contentDirection: \"De derecha a izquierda\",\n contentDirectionHint:\n \"Dirección de escritura del correo, independiente del idioma del editor. Si no se indica, sigue el idioma del contenido (árabe, hebreo, …).\",\n tips: \"Consejos\",\n tip1: \"600px es el ancho estándar para las plantillas de correo electrónico\",\n tip2: \"Usa fuentes seguras para la web para una mejor compatibilidad\",\n tip3: \"Los fondos claros funcionan mejor para la legibilidad\",\n },\n\n // Spacing control\n spacingControl: {\n lockAll: \"Bloquear todos los lados\",\n unlock: \"Desbloquear lados\",\n top: \"Superior\",\n right: \"Derecho\",\n bottom: \"Inferior\",\n left: \"Izquierdo\",\n decreaseTop: \"Disminuir superior\",\n increaseTop: \"Aumentar superior\",\n decreaseLeft: \"Disminuir izquierdo\",\n increaseLeft: \"Aumentar izquierdo\",\n decreaseRight: \"Disminuir derecho\",\n increaseRight: \"Aumentar derecho\",\n decreaseBottom: \"Disminuir inferior\",\n increaseBottom: \"Aumentar inferior\",\n },\n\n // Color Picker\n colorPicker: {\n pickColor: \"Elegir un color\",\n hexValue: \"Valor de color Hex\",\n notSet: \"No establecido\",\n clear: \"Limpiar color\",\n transparent: \"Transparente\",\n presetColors: \"Colores predefinidos\",\n },\n\n // Merge Tag\n mergeTag: {\n editAsText: \"Editar como texto\",\n fieldGroup: \"Valor con etiquetas de combinación\",\n remove: \"Eliminar etiqueta de combinación\",\n insert: \"Insertar etiqueta de combinación\",\n insertShort: \"Etiqueta de combinación\",\n editValue: \"Editar valor de la etiqueta de combinación\",\n changeTag: \"Cambiar etiqueta de combinación: {label}\",\n unknownLabel: \"Etiqueta de combinación\",\n deleteMergeTag: \"Eliminar etiqueta de combinación\",\n suggestionEmpty: \"No hay etiquetas de combinación coincidentes\",\n picker: {\n title: \"Insertar etiqueta de combinación\",\n changeTitle: \"Cambiar etiqueta de combinación\",\n searchPlaceholder: \"Buscar etiquetas de combinación\",\n searchAriaLabel: \"Buscar etiquetas de combinación\",\n noResults: \"No hay etiquetas de combinación coincidentes\",\n empty: \"No hay etiquetas de combinación configuradas\",\n otherGroup: \"Otro\",\n close: \"Cerrar\",\n groupCount: \"{count}\",\n sample: \"Vista previa: {sample}\",\n },\n },\n\n // Logic tags (standalone, separate from merge tags)\n logicTag: {\n insert: \"Insertar lógica\",\n insertShort: \"Lógica\",\n picker: {\n title: \"Insertar lógica\",\n searchPlaceholder: \"Buscar etiquetas de lógica\",\n searchAriaLabel: \"Buscar etiquetas de lógica\",\n noResults: \"No hay etiquetas de lógica coincidentes\",\n empty: \"No hay etiquetas de lógica configuradas\",\n otherGroup: \"Otro\",\n close: \"Cerrar\",\n },\n },\n\n // Canvas\n canvas: {\n noBlocks: \"Aún no hay bloques\",\n dragHint: \"Comienza desde cero arrastrando bloques desde la barra lateral\",\n dropHere: \"Suelta aquí\",\n aiHintChat: \"o deja que\",\n aiHintChatSuffix: \"genere una plantilla completa para ti en segundos\",\n aiHintDesign:\n \"¿Tienes un diseño existente? Sube una captura de pantalla, imagen o PDF y\",\n aiHintDesignSuffix: \"lo recreará instantáneamente\",\n },\n\n // Sidebar\n savedBlocks: {\n title: \"Bloques guardados\",\n saveAsBlock: \"Guardar como bloque\",\n name: \"Nombre\",\n namePlaceholder: \"p. ej. Encabezado, Pie de página, CTA...\",\n save: \"Guardar bloque\",\n saving: \"Guardando...\",\n cancel: \"Cancelar\",\n empty: \"Aún no hay bloques guardados\",\n emptyHint: \"Guarda bloques de tus plantillas para reutilizarlos más tarde.\",\n noResults: \"Ningún bloque guardado coincide con tu búsqueda\",\n search: \"Buscar bloques guardados...\",\n loading: \"Cargando bloques guardados…\",\n category: \"Categoría\",\n categoryPlaceholder: \"p. ej. Cabeceras, Promociones... (opcional)\",\n allCategories: \"Todas las categorías\",\n filterByCategory: \"Filtrar por categoría\",\n insert: \"Insertar\",\n rename: \"Cambiar nombre\",\n delete: \"Eliminar\",\n deleteConfirm: \"¿Eliminar este bloque guardado?\",\n blockCount: \"{count} bloque(s)\",\n browse: \"Explorar bloques guardados\",\n selectToPreview: \"Selecciona un bloque guardado para previsualizarlo\",\n insertAtBeginning: \"Al principio\",\n insertAfterBlock: \"Después de {block}\",\n insertAtEnd: \"Al final\",\n insertPosition: \"Posición de inserción\",\n close: \"Cerrar\",\n pickToolbar: \"Selección de bloque guardado\",\n pickCount: \"{count} bloque(s) seleccionado(s)\",\n pickHint: \"Haz clic en los bloques para añadirlos o quitarlos\",\n savingCount: \"Guardando {count} bloque(s):\",\n reorderHint:\n \"Arrastra para reordenar: los bloques se guardan en este orden\",\n reorderHandle: \"Reordenar {block}, posición {position} de {total}\",\n reorderAnnouncement: \"{block} movido a la posición {position} de {total}\",\n expand: \"Expandir\",\n collapse: \"Contraer\",\n expandPreview: \"Expandir la vista previa de {block}\",\n collapsePreview: \"Contraer la vista previa de {block}\",\n },\n comments: {\n button: \"Comentarios\",\n title: \"Comentarios\",\n close: \"Cerrar\",\n placeholder: \"Escribe un comentario...\",\n replyPlaceholder: \"Escribe una respuesta...\",\n reply: \"Responder\",\n resolve: \"Resolver\",\n unresolve: \"Reabrir\",\n delete: \"Eliminar\",\n edit: \"Editar\",\n cancel: \"Cancelar\",\n save: \"Guardar\",\n noComments: \"Aún no hay comentarios\",\n noCommentsHint:\n \"Inicia una conversación añadiendo un comentario a la plantilla o a un bloque concreto.\",\n deleteConfirm: \"¿Eliminar este comentario?\",\n filterAll: \"Todos\",\n filterUnresolved: \"Sin resolver\",\n filterBlock: \"Este bloque\",\n jumpToBlock: \"Bloque\",\n ownedByYou: \"Tú\",\n edited: \"editado\",\n resolvedBy: \"Resuelto por {name}\",\n replyOne: \"{count} respuesta\",\n replyMany: \"{count} respuestas\",\n missingBlock: \"Bloque no encontrado\",\n saveTemplateFirst: \"Guarda la plantilla antes de comentar en este bloque.\",\n },\n versionHistory: {\n tooltip: \"Historial de versiones\",\n dropdownTitle: \"Historial de versiones\",\n empty: \"Todavía no hay versiones\",\n auto: \"auto\",\n olderVersion: \"Versión anterior\",\n newerVersion: \"Versión más reciente\",\n },\n versionPreview: {\n message: \"Estás viendo una versión anterior de esta plantilla.\",\n cancel: \"Cancelar\",\n restore: \"Restaurar esta versión\",\n restoreConfirm: {\n title: \"¿Restaurar esta versión?\",\n unsavedWithSave:\n \"Tienes cambios sin guardar. Guárdalos primero y se conservarán en el historial; si restauras sin guardar, se perderán.\",\n unsavedNoSave:\n \"Tienes cambios sin guardar y no hay dónde guardarlos. Al restaurar esta versión se perderán.\",\n saveAndRestore: \"Guardar y restaurar\",\n restoreAnyway: \"Restaurar de todos modos\",\n cancel: \"Cancelar\",\n },\n },\n previewResolution: {\n resolving: \"Resolviendo la vista previa…\",\n failed:\n \"No se pudo resolver la vista previa; se muestra la plantilla sin resolver.\",\n hint: \"La vista previa usa datos resueltos por tu backend.\",\n },\n mergeTagPreview: {\n label: \"Vista de etiquetas de combinación\",\n sample: \"Ejemplo\",\n labelView: \"Etiqueta\",\n },\n testEmail: {\n title: \"Enviar correo de prueba\",\n recipientLabel: \"Destinatario\",\n recipientPlaceholder: \"tu@ejemplo.com\",\n invalidAddress: \"Introduce una dirección de correo válida.\",\n preview: \"Vista previa\",\n previewHint:\n \"Las etiquetas de combinación se muestran sin resolver — tu backend las rellena.\",\n previewHintSample:\n \"Las etiquetas de combinación muestran valores de ejemplo; tu backend inserta los datos reales.\",\n recipientNotAllowed:\n \"Esa dirección no está en la lista de destinatarios permitidos.\",\n send: \"Enviar\",\n sending: \"Enviando...\",\n cancel: \"Cancelar\",\n success: \"Correo de prueba enviado correctamente\",\n button: \"Prueba\",\n },\n sidebarNav: {\n browseSavedBlocks: \"Explorar bloques guardados\",\n palette: \"Paleta de bloques\",\n insertBlock: \"Insertar bloque {block}\",\n },\n\n // Landmark region labels for assistive technology\n landmarks: {\n canvas: \"Lienzo del correo electrónico\",\n blockToolbar: \"Propiedades del bloque\",\n rightSidebar: \"Propiedades del bloque y configuración de la plantilla\",\n reorderAnnouncements: \"Anuncios de reordenación de bloques\",\n },\n\n // Design Reference (cloud)\n errors: {\n editorLoading: \"Cargando el editor...\",\n editorLoadFailed: \"Error al cargar el editor.\",\n retry: \"Reintentar\",\n },\n\n issues: {\n panelTitle: \"Problemes\",\n panelTabLabel: \"Problemes\",\n groupErrors: \"Errores\",\n groupWarnings: \"Advertencias\",\n groupInfo: \"Información\",\n jump: \"Saltar al bloque\",\n fix: \"Corregir\",\n emptyState: \"Sin problemas, todo se ve bien.\",\n badgeError: \"Tiene errores\",\n badgeWarning: \"Tiene advertencias\",\n issueCountTooltip: \"{count} problema(s)\",\n },\n\n smallScreen: {\n title: \"Se requiere una pantalla más grande\",\n message:\n \"El editor necesita más espacio del que ofrece esta pantalla. Ábrelo en una tableta o computadora de escritorio para comenzar a editar.\",\n },\n};\n\nexport default es;\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import { H as e, N as t, Q as n, S as r, U as i, Y as a, at as o, c as s, ct as
2
2
  import { a as y, d as b, o as x, r as S } from "./src-BR3I3gOW.js";
3
3
  import { t as C } from "./useI18n-CyO_Nmca.js";
4
4
  import { t as w } from "./useMergeTag-C5MyHYxJ.js";
5
- import { _ as T, g as E, h as D, n as O, r as k, t as A, v as j, y as M } from "./tiptap-D1u8JIUW.js";
6
- import { a as N, o as P, s as F } from "./LogicTagInsertButton-CItDS_U1.js";
5
+ import { _ as T, g as E, h as D, n as O, r as k, t as A, v as j, y as M } from "./tiptap-02G9FGkl.js";
6
+ import { a as N, o as P, s as F } from "./LogicTagInsertButton-FNMVxm_V.js";
7
7
  //#region src/extensions/FontSize.ts
8
8
  var I = D.create({
9
9
  name: "fontSize",
@@ -527,4 +527,4 @@ var $ = D.create({
527
527
  //#endregion
528
528
  export { I as FontSize, L as LetterSpacing, R as LineBoundaryKeys, z as LineHeight, K as LogicMergeTagNode, Z as MergeTagNode, $ as MergeTagSuggestion, P as filterMergeTags, F as handleSuggestionKeyDown };
529
529
 
530
- //# sourceMappingURL=extensions-Dy251lPd.js.map
530
+ //# sourceMappingURL=extensions-rPP7qRRm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extensions-Dy251lPd.js","names":["_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","_unref","_normalizeClass","_normalizeStyle","_withCtx","_withDirectives","_createElementBlock","_vModelText","_withModifiers","_withKeys","_toDisplayString","_createElementVNode","_createCommentVNode","_sfc_main","LogicMergeTagNodeView","_openBlock","_createBlock","_unref","_withCtx","_withDirectives","_createElementBlock","_normalizeClass","_vModelText","_withModifiers","_withKeys","_toDisplayString","_createElementVNode","_sfc_main","MergeTagNodeView","Suggestion","withCtx","openBlock","ref","defineComponent","withDirectives","onScopeDispose","unref","withKeys","normalizeClass","computed","createElementBlock","createCommentVNode","nextTick","withModifiers","normalizeStyle","createBlock","vModelText","createBaseVNode","toDisplayString","getLogicMergeTagKeyword","isLogicMergeTagValue","getMergeTagLabel","SYNTAX_PRESETS","useI18n","useMergeTag","Node","InputRule","Extension","NodeViewWrapper","VueNodeViewRenderer","src_default","PasteRule","mergeAttributes","createMergeTagPopup","filterMergeTags","handleSuggestionKeyDown","FontSize","element","attributes","chain","size","LetterSpacing","spacing","LineBoundaryKeys","moveToLineBoundary","view","editor","selection","direction","LineHeight","commands","type","_hoisted_1$1","_hoisted_2$1","_hoisted_3$1","LogicMergeTagNodeView_default","props","__props","isValid","syntax","displayKeyword","isEditing","editValue","inputRef","handled","startEditing","finishEditing","newValue","handleKeydown","event","_cache","$event","t","args","isNodeSelected","$from","$to","nodeTypeName","renderVueNodeView","component","LogicMergeTagNode","node","keyword","HTMLAttributes","inputRegex","fullValue","match","range","pasteRegex","_hoisted_1","_hoisted_2","_hoisted_3","MergeTagNodeView_default","displayLabel","getMergeTagDisplayLabel","canRepick","canRepickMergeTag","disposed","isEditValueValid","isMergeTagValue","repick","replacement","requestMergeTag","findMergeTag","activate","format","showRawValue","MergeTagNode","label","attrs","createMergeTagSuggestionRenderer","popup","emptyText","popoverRootRef","item","MergeTagSuggestion","tags","config","query"],"sources":["../../../src/extensions/FontSize.ts","../../../src/extensions/LetterSpacing.ts","../../../src/extensions/LineBoundaryKeys.ts","../../../src/extensions/LineHeight.ts","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/isNodeSelected.ts","../../../src/extensions/renderVueNodeView.ts","../../../src/extensions/LogicMergeTagNode.ts","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNode.ts","../../../src/extensions/MergeTagSuggestion.ts"],"sourcesContent":["import { Extension } from \"@tiptap/core\";\n\nexport interface FontSizeOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n fontSize: {\n setFontSize: (size: string) => ReturnType;\n unsetFontSize: () => ReturnType;\n };\n }\n}\n\nexport const FontSize = Extension.create<FontSizeOptions>({\n name: \"fontSize\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n fontSize: {\n default: null,\n parseHTML: (element) =>\n element.style.fontSize?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.fontSize) {\n return {};\n }\n return {\n style: `font-size: ${attributes.fontSize}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setFontSize:\n (size: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { fontSize: size }).run();\n },\n unsetFontSize:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { fontSize: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LetterSpacingOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n letterSpacing: {\n setLetterSpacing: (spacing: string) => ReturnType;\n unsetLetterSpacing: () => ReturnType;\n };\n }\n}\n\nexport const LetterSpacing = Extension.create<LetterSpacingOptions>({\n name: \"letterSpacing\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n letterSpacing: {\n default: null,\n parseHTML: (element) =>\n element.style.letterSpacing?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.letterSpacing) {\n return {};\n }\n return {\n style: `letter-spacing: ${attributes.letterSpacing}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLetterSpacing:\n (spacing: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { letterSpacing: spacing }).run();\n },\n unsetLetterSpacing:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { letterSpacing: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\n/**\n * Routes plain End/Home through `Selection.modify` instead of the browser's\n * native caret movement.\n *\n * Chromium (verified 140–151, including stable Chrome) has a bug where a\n * native End or Home pressed while a triple-click selection is active puts\n * Blink's selection state into a mode where the next typed character\n * natively smooth-scrolls the nearest scrollable ancestor (the editor's\n * canvas, `.tpl-body`) to its very bottom (End) or top (Home), dragging the\n * caret out of view. The state cannot be repaired after the fact — clearing\n * or re-setting the DOM selection, blur/refocus, and preventDefault on the\n * clicks all fail — but a programmatic caret move never arms it.\n *\n * `Selection.modify(\"move\", direction, \"lineboundary\")` is the exact\n * equivalent of the native key (visual line boundary, bidi-aware via the\n * logical forward/backward directions), so behavior is unchanged for users.\n * Shift+End / Shift+Home extend selections natively and do not trigger the\n * bug, so only the plain keys are bound — a keymap entry without modifiers\n * never matches the shifted keys.\n */\nexport const LineBoundaryKeys = Extension.create({\n name: \"lineBoundaryKeys\",\n\n addKeyboardShortcuts() {\n const moveToLineBoundary =\n (direction: \"forward\" | \"backward\") =>\n ({ editor }: { editor: { view: unknown } }): boolean => {\n const view = editor.view as {\n composing: boolean;\n root: DocumentOrShadowRoot & {\n getSelection?: () => Selection | null;\n };\n dom: HTMLElement;\n };\n // During IME composition End/Home commit the composition; leave\n // that to the browser.\n if (!view || view.composing) return false;\n // `view.root` is the editable's Document or ShadowRoot, so the\n // selection resolves correctly in both mount modes. ShadowRoot's\n // getSelection is Chromium-only; elsewhere fall back to the owner\n // document (Chromium is also the only engine with the bug).\n const selection =\n typeof view.root?.getSelection === \"function\"\n ? view.root.getSelection()\n : (view.dom?.ownerDocument?.getSelection?.() ?? null);\n // `modify` is non-standard; without it, or with a selection that\n // is not ours, native handling is the safer default.\n if (!selection || typeof selection.modify !== \"function\") return false;\n if (!selection.anchorNode || !view.dom.contains(selection.anchorNode)) {\n return false;\n }\n selection.modify(\"move\", direction, \"lineboundary\");\n return true;\n };\n\n return {\n End: moveToLineBoundary(\"forward\"),\n Home: moveToLineBoundary(\"backward\"),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LineHeightOptions {\n types: string[];\n defaultLineHeight: string;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n lineHeight: {\n setLineHeight: (lineHeight: string) => ReturnType;\n unsetLineHeight: () => ReturnType;\n };\n }\n}\n\nexport const LineHeight = Extension.create<LineHeightOptions>({\n name: \"lineHeight\",\n\n addOptions() {\n return {\n types: [\"paragraph\"],\n defaultLineHeight: \"1.5\",\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n lineHeight: {\n default: null,\n parseHTML: (element) => element.style.lineHeight || null,\n renderHTML: (attributes) => {\n if (!attributes.lineHeight) {\n return {};\n }\n return {\n style: `line-height: ${attributes.lineHeight}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLineHeight:\n (lineHeight: string) =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.updateAttributes(type, { lineHeight }),\n );\n },\n unsetLineHeight:\n () =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.resetAttributes(type, \"lineHeight\"),\n );\n },\n };\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import type { Editor } from \"@tiptap/core\";\n\n/**\n * Decides whether MergeTagNode / LogicMergeTagNode should swallow a\n * Backspace/Delete keystroke (returning `true` from a TipTap keymap\n * suppresses the default behavior).\n *\n * Only suppresses in the cursor-adjacent case so the \"first press\n * selects the atom, second press deletes it\" UX works. Range selections\n * (Cmd+A, drag-select, double-click-word) explicitly fall through —\n * default `replaceSelection` then deletes the entire range including\n * any merge-tag atoms inside it.\n *\n * Earlier revisions also returned `true` whenever `nodesBetween($from,\n * $to)` found a merge tag anywhere in the selection range — which\n * silently broke Cmd+A + Backspace for any paragraph containing a\n * merge tag (entire deletion was cancelled, nothing happened). The\n * cursor-adjacent protection is the only piece that should survive.\n */\nexport function isNodeSelected(editor: Editor, nodeTypeName: string): boolean {\n const { $from, $to } = editor.state.selection;\n\n // Range selection: let TipTap's default range-delete run. Even when\n // the range contains atom nodes, ProseMirror's replaceSelection\n // handles atomic deletion correctly.\n if ($from.pos !== $to.pos) return false;\n\n // Cursor-adjacent atom: protect from single-keystroke deletion.\n // (`$from.pos > 0` guards against reading `nodeBefore` at doc start\n // where ProseMirror's index would be undefined for nodeBefore.)\n if ($from.pos > 0 && $from.nodeBefore?.type.name === nodeTypeName) {\n return true;\n }\n if ($from.nodeAfter?.type.name === nodeTypeName) {\n return true;\n }\n\n return false;\n}\n","import type { Component } from \"vue\";\nimport { VueNodeViewRenderer } from \"@tiptap/vue-3\";\n\n/**\n * Typed wrapper for VueNodeViewRenderer that handles the known type mismatch\n * between Vue SFC default exports and TipTap's expected component type.\n */\nexport function renderVueNodeView(component: Component) {\n return VueNodeViewRenderer(\n component as Parameters<typeof VueNodeViewRenderer>[0],\n );\n}\n","import LogicMergeTagNodeView from \"./LogicMergeTagNodeView.vue\";\nimport type { SyntaxPreset } from \"@templatical/types\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n SYNTAX_PRESETS,\n} from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface LogicMergeTagNodeOptions {\n syntax: SyntaxPreset;\n}\n\nexport const LogicMergeTagNode = Node.create<LogicMergeTagNodeOptions>({\n name: \"logicMergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n // Both attributes are state, not markup, same as `MergeTagNode`:\n // `renderHTML()` emits the canonical `data-logic-merge-tag` /\n // `data-keyword` pair and `parseHTML` reads only those. `rendered: false`\n // keeps TipTap from also serializing each attribute under its own name,\n // which would put a write-only duplicate of the same data on every tag.\n value: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-logic-merge-tag\") || \"\",\n },\n keyword: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-keyword\") || element.textContent || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-logic-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n if (!isLogicMergeTagValue(node.attrs.value, this.options.syntax)) {\n return [\"span\", {}, node.attrs.value];\n }\n\n const keyword = getLogicMergeTagKeyword(\n node.attrs.value,\n this.options.syntax,\n );\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-logic-merge-tag\": node.attrs.value,\n \"data-keyword\": keyword,\n }),\n keyword,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(LogicMergeTagNodeView);\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.logic.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.logic.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, onScopeDispose, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst {\n getMergeTagLabel,\n getMergeTagDisplayLabel,\n findMergeTag,\n canRepickMergeTag,\n showRawValue,\n isMergeTagValue,\n requestMergeTag,\n} = useMergeTag();\nconst { t, format } = useI18n();\n\n// `node.attrs.label` is the label captured when the tag was inserted. It is\n// only ever a fallback — the configured tags win, which is what keeps a\n// relabelled tag from rendering its stale stored copy.\nconst displayLabel = computed(() =>\n getMergeTagDisplayLabel(props.node.attrs.value, props.node.attrs.label),\n);\nconst canRepick = computed(() => canRepickMergeTag(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// The chooser mounts in the popover root, outside this node view, so the node\n// can be torn down while it is open — a collaborator edit, a block change, the\n// editor unmounting. Writing attributes onto a destroyed node view throws.\nlet disposed = false;\nonScopeDispose(() => {\n disposed = true;\n});\n\n/** Whether the text currently in the raw input is a usable merge tag. */\nconst isEditValueValid = computed(\n () =>\n editValue.value.trim() === \"\" || isMergeTagValue(editValue.value.trim()),\n);\n\n/**\n * Swap this tag for another one, chosen through the same chooser insertion\n * uses. `requestMergeTag` sets the editor-wide requesting flag for the whole\n * await, which is what stops `useRichTextEditor`'s click-outside handler\n * finishing the block when the user clicks a row in the chooser — that would\n * dispose this node view and silently drop the update.\n */\nasync function repick(): Promise<void> {\n const replacement = await requestMergeTag({\n reason: \"edit\",\n current: findMergeTag(props.node.attrs.value),\n });\n if (disposed || !replacement) return;\n props.updateAttributes({\n value: replacement.value,\n label: replacement.label,\n });\n}\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\n/**\n * Activating the chip opens the chooser when one can resolve this token, and\n * the raw input only when none can. Editing a token as free text is how a\n * chip ends up holding something that is not a merge tag at all, which the\n * renderer then emits verbatim into the sent email.\n */\nfunction activate(): void {\n if (canRepick.value) {\n void repick();\n return;\n }\n startEditing();\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n // Never commit something that isn't a merge tag: `renderHTML` writes the\n // value straight into `data-merge-tag`, and the renderer drops that into the\n // MJML unchanged, so a typo would ship as literal text in the email.\n if (\n newValue &&\n newValue !== props.node.attrs.value &&\n isMergeTagValue(newValue)\n ) {\n props.updateAttributes({\n // The honest resolution, not the display chain: this is written into the\n // document as `data-label`, so a placeholder would be persisted as if it\n // were the tag's name.\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n // Enter on an invalid value keeps the input open rather than discarding\n // what was typed — the border and `aria-invalid` already say why.\n if (!isEditValueValid.value) return;\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n :aria-invalid=\"isEditValueValid ? undefined : 'true'\"\n class=\"tpl:w-32 tpl:rounded tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n :class=\"\n isEditValueValid\n ? 'tpl:border-none'\n : 'tpl:border tpl:border-[var(--tpl-danger)]'\n \"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"\n canRepick\n ? format(t.mergeTag.changeTag, { label: displayLabel })\n : t.mergeTag.editValue\n \"\n class=\"tpl:cursor-pointer\"\n :class=\"showRawValue ? 'tpl-tooltip' : ''\"\n :data-tooltip=\"showRawValue ? node.attrs.value : undefined\"\n @click.stop=\"activate\"\n @keydown.enter.stop=\"activate\"\n @keydown.space.prevent.stop=\"activate\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, onScopeDispose, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst {\n getMergeTagLabel,\n getMergeTagDisplayLabel,\n findMergeTag,\n canRepickMergeTag,\n showRawValue,\n isMergeTagValue,\n requestMergeTag,\n} = useMergeTag();\nconst { t, format } = useI18n();\n\n// `node.attrs.label` is the label captured when the tag was inserted. It is\n// only ever a fallback — the configured tags win, which is what keeps a\n// relabelled tag from rendering its stale stored copy.\nconst displayLabel = computed(() =>\n getMergeTagDisplayLabel(props.node.attrs.value, props.node.attrs.label),\n);\nconst canRepick = computed(() => canRepickMergeTag(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// The chooser mounts in the popover root, outside this node view, so the node\n// can be torn down while it is open — a collaborator edit, a block change, the\n// editor unmounting. Writing attributes onto a destroyed node view throws.\nlet disposed = false;\nonScopeDispose(() => {\n disposed = true;\n});\n\n/** Whether the text currently in the raw input is a usable merge tag. */\nconst isEditValueValid = computed(\n () =>\n editValue.value.trim() === \"\" || isMergeTagValue(editValue.value.trim()),\n);\n\n/**\n * Swap this tag for another one, chosen through the same chooser insertion\n * uses. `requestMergeTag` sets the editor-wide requesting flag for the whole\n * await, which is what stops `useRichTextEditor`'s click-outside handler\n * finishing the block when the user clicks a row in the chooser — that would\n * dispose this node view and silently drop the update.\n */\nasync function repick(): Promise<void> {\n const replacement = await requestMergeTag({\n reason: \"edit\",\n current: findMergeTag(props.node.attrs.value),\n });\n if (disposed || !replacement) return;\n props.updateAttributes({\n value: replacement.value,\n label: replacement.label,\n });\n}\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\n/**\n * Activating the chip opens the chooser when one can resolve this token, and\n * the raw input only when none can. Editing a token as free text is how a\n * chip ends up holding something that is not a merge tag at all, which the\n * renderer then emits verbatim into the sent email.\n */\nfunction activate(): void {\n if (canRepick.value) {\n void repick();\n return;\n }\n startEditing();\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n // Never commit something that isn't a merge tag: `renderHTML` writes the\n // value straight into `data-merge-tag`, and the renderer drops that into the\n // MJML unchanged, so a typo would ship as literal text in the email.\n if (\n newValue &&\n newValue !== props.node.attrs.value &&\n isMergeTagValue(newValue)\n ) {\n props.updateAttributes({\n // The honest resolution, not the display chain: this is written into the\n // document as `data-label`, so a placeholder would be persisted as if it\n // were the tag's name.\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n // Enter on an invalid value keeps the input open rather than discarding\n // what was typed — the border and `aria-invalid` already say why.\n if (!isEditValueValid.value) return;\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n :aria-invalid=\"isEditValueValid ? undefined : 'true'\"\n class=\"tpl:w-32 tpl:rounded tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n :class=\"\n isEditValueValid\n ? 'tpl:border-none'\n : 'tpl:border tpl:border-[var(--tpl-danger)]'\n \"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"\n canRepick\n ? format(t.mergeTag.changeTag, { label: displayLabel })\n : t.mergeTag.editValue\n \"\n class=\"tpl:cursor-pointer\"\n :class=\"showRawValue ? 'tpl-tooltip' : ''\"\n :data-tooltip=\"showRawValue ? node.attrs.value : undefined\"\n @click.stop=\"activate\"\n @keydown.enter.stop=\"activate\"\n @keydown.space.prevent.stop=\"activate\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import MergeTagNodeView from \"./MergeTagNodeView.vue\";\nimport type { MergeTag, SyntaxPreset } from \"@templatical/types\";\nimport { getMergeTagLabel, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface MergeTagNodeOptions {\n /**\n * Read live, not captured. `renderHTML` and the input/paste rules resolve a\n * label every time they run, and `editor.setMergeTags` can replace the list\n * while a block is open for editing.\n */\n mergeTags: () => MergeTag[];\n syntax: SyntaxPreset;\n}\n\n/**\n * Inserted-node attrs are intentionally limited to `label` + `value`. The\n * `MergeTag` interface also carries optional `group` / `description` for\n * the built-in picker, but those are display-only and must NOT leak into\n * the document JSON — they would bloat templates and pollute serialized\n * state. `Pick<>` makes the constraint explicit at the type level.\n */\nexport type InsertMergeTagAttrs = Pick<MergeTag, \"label\" | \"value\">;\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n mergeTagNode: {\n insertMergeTag: (attrs: InsertMergeTagAttrs) => ReturnType;\n };\n }\n}\n\nexport const MergeTagNode = Node.create<MergeTagNodeOptions>({\n name: \"mergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n mergeTags: () => [],\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n // Both attributes are state, not markup: `renderHTML()` below emits the\n // canonical `data-merge-tag` / `data-label` pair and `parseHTML` reads\n // only those. `rendered: false` keeps TipTap from also serializing each\n // attribute under its own name, which would put a write-only duplicate\n // of the same data on every tag — invalid on a `<span>`, and paid again\n // through storage, autosave, snapshots and version history.\n label: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-label\") || element.textContent || \"\",\n },\n value: {\n default: \"\",\n rendered: false,\n parseHTML: (element) => element.getAttribute(\"data-merge-tag\") || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = getMergeTagLabel(node.attrs.value, this.options.mergeTags());\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-merge-tag\": node.attrs.value,\n \"data-label\": label,\n }),\n label,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(MergeTagNodeView);\n },\n\n addCommands() {\n return {\n insertMergeTag:\n (attrs: InsertMergeTagAttrs) =>\n ({ commands }) => {\n // Whitelist explicitly — even if a caller smuggles extra keys via\n // a wider type assertion, only label/value reach the node attrs.\n return commands.insertContent({\n type: this.name,\n attrs: { label: attrs.label, value: attrs.value },\n });\n },\n };\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.value.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags());\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.value.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags());\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","import type { MergeTag } from \"@templatical/types\";\nimport { Extension } from \"@tiptap/core\";\nimport Suggestion, {\n type SuggestionOptions,\n type SuggestionProps,\n type SuggestionKeyDownProps,\n} from \"@tiptap/suggestion\";\nimport type { Ref } from \"vue\";\nimport { createMergeTagPopup, filterMergeTags } from \"../utils/mergeTagPopup\";\n\nexport interface MergeTagSuggestionOptions {\n /** Available merge tags */\n /**\n * Read live, not captured: the list can be replaced while a block is being\n * edited (`editor.setMergeTags`), and a snapshot taken when the extension\n * was configured would keep suggesting the old tags.\n */\n mergeTags: () => MergeTag[];\n /** Trigger string (e.g. \"{{\", \"*|\", \"%%=\") */\n char: string;\n /** Localized empty-state label */\n emptyText: string;\n /**\n * Mount target for the suggestion popup. When provided with a non-null\n * `.value`, the popup attaches into that element instead of\n * `document.body` — keeping it inside the editor's effective DOM root\n * (shadow-aware). Pass the ref returned by `usePopoverRoot()`.\n *\n * Falls back to `document.body` when omitted or when the ref's value is\n * null at popup-open time (e.g. headless/test editors without an editor\n * shell). Preserves pre-Phase-3 behavior for callers that don't migrate.\n */\n popoverRoot?: Ref<HTMLElement | null> | null;\n}\n\n/**\n * Builds the `@tiptap/suggestion` `render` factory for the merge-tag popup.\n * A thin adapter over {@link createMergeTagPopup}: it maps ProseMirror's\n * `SuggestionProps` (items, `clientRect`, `view.dom`, `command`) onto the\n * controller. Exported as a standalone function so that mapping can be\n * unit-tested with mocked `SuggestionProps` instead of a full editor.\n */\nexport function createMergeTagSuggestionRenderer(\n emptyText: string,\n popoverRootRef?: Ref<HTMLElement | null> | null,\n): NonNullable<SuggestionOptions<MergeTag>[\"render\"]> {\n return () => {\n const popup = createMergeTagPopup(emptyText, popoverRootRef);\n\n return {\n onStart: (props: SuggestionProps<MergeTag>) => {\n // Use view.dom (ProseMirror contenteditable, actually\n // attached to the DOM) rather than options.element, which may\n // be a detached div when no `element` is passed to the editor\n // constructor (as is the case with @tiptap/vue-3 EditorContent).\n const viewDom = props.editor.view?.dom as HTMLElement | undefined;\n popup.open({\n items: props.items,\n getRect: props.clientRect ?? null,\n anchorEl: viewDom ?? null,\n onCommand: (item) => props.command(item),\n });\n },\n onUpdate: (props: SuggestionProps<MergeTag>) => {\n popup.update({\n items: props.items,\n getRect: props.clientRect ?? null,\n onCommand: (item) => props.command(item),\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps): boolean => {\n // Escape closes via TipTap's suggestion plugin (→ onExit); report it\n // handled without selecting.\n if (props.event.key === \"Escape\") {\n return true;\n }\n return popup.handleKeyDown(props.event);\n },\n onExit: () => popup.close(),\n };\n };\n}\n\nexport const MergeTagSuggestion = Extension.create<MergeTagSuggestionOptions>({\n name: \"mergeTagSuggestion\",\n\n addOptions() {\n return {\n mergeTags: () => [],\n char: \"{{\",\n emptyText: \"No matching merge tags\",\n popoverRoot: null,\n };\n },\n\n addProseMirrorPlugins() {\n const tags = this.options.mergeTags();\n const emptyText = this.options.emptyText;\n const popoverRootRef = this.options.popoverRoot;\n\n const config: Omit<SuggestionOptions<MergeTag>, \"editor\"> = {\n char: this.options.char,\n allowSpaces: false,\n startOfLine: false,\n // Default is [\" \"] which requires whitespace/line-start before the\n // trigger char — so `.{{` would not fire. Allow any preceding char.\n allowedPrefixes: null,\n items: ({ query }: { query: string }) => filterMergeTags(tags, query),\n command: ({\n editor,\n range,\n props,\n }: {\n editor: SuggestionProps<MergeTag>[\"editor\"];\n range: { from: number; to: number };\n props: MergeTag;\n }) => {\n // Use insertContentAt for atomic replace (matches the canonical\n // @tiptap/suggestion + Mention pattern). Avoids edge cases where\n // chained deleteRange + insertMergeTag fails to insert when the\n // selection state shifts mid-chain.\n editor\n .chain()\n .focus()\n .insertContentAt(range, {\n type: \"mergeTagNode\",\n attrs: { label: props.label, value: props.value },\n })\n .run();\n },\n render: createMergeTagSuggestionRenderer(emptyText, popoverRootRef),\n };\n\n return [\n Suggestion({\n editor: this.editor,\n ...config,\n }),\n ];\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"extensions-rPP7qRRm.js","names":["_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","_unref","_normalizeClass","_normalizeStyle","_withCtx","_withDirectives","_createElementBlock","_vModelText","_withModifiers","_withKeys","_toDisplayString","_createElementVNode","_createCommentVNode","_sfc_main","LogicMergeTagNodeView","_openBlock","_createBlock","_unref","_withCtx","_withDirectives","_createElementBlock","_normalizeClass","_vModelText","_withModifiers","_withKeys","_toDisplayString","_createElementVNode","_sfc_main","MergeTagNodeView","Suggestion","withCtx","openBlock","ref","defineComponent","withDirectives","onScopeDispose","unref","withKeys","normalizeClass","computed","createElementBlock","createCommentVNode","nextTick","withModifiers","normalizeStyle","createBlock","vModelText","createBaseVNode","toDisplayString","getLogicMergeTagKeyword","isLogicMergeTagValue","getMergeTagLabel","SYNTAX_PRESETS","useI18n","useMergeTag","Node","InputRule","Extension","NodeViewWrapper","VueNodeViewRenderer","src_default","PasteRule","mergeAttributes","createMergeTagPopup","filterMergeTags","handleSuggestionKeyDown","FontSize","element","attributes","chain","size","LetterSpacing","spacing","LineBoundaryKeys","moveToLineBoundary","view","editor","selection","direction","LineHeight","commands","type","_hoisted_1$1","_hoisted_2$1","_hoisted_3$1","LogicMergeTagNodeView_default","props","__props","isValid","syntax","displayKeyword","isEditing","editValue","inputRef","handled","startEditing","finishEditing","newValue","handleKeydown","event","_cache","$event","t","args","isNodeSelected","$from","$to","nodeTypeName","renderVueNodeView","component","LogicMergeTagNode","node","keyword","HTMLAttributes","inputRegex","fullValue","match","range","pasteRegex","_hoisted_1","_hoisted_2","_hoisted_3","MergeTagNodeView_default","displayLabel","getMergeTagDisplayLabel","canRepick","canRepickMergeTag","disposed","isEditValueValid","isMergeTagValue","repick","replacement","requestMergeTag","findMergeTag","activate","format","showRawValue","MergeTagNode","label","attrs","createMergeTagSuggestionRenderer","popup","emptyText","popoverRootRef","item","MergeTagSuggestion","tags","config","query"],"sources":["../../../src/extensions/FontSize.ts","../../../src/extensions/LetterSpacing.ts","../../../src/extensions/LineBoundaryKeys.ts","../../../src/extensions/LineHeight.ts","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/isNodeSelected.ts","../../../src/extensions/renderVueNodeView.ts","../../../src/extensions/LogicMergeTagNode.ts","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNode.ts","../../../src/extensions/MergeTagSuggestion.ts"],"sourcesContent":["import { Extension } from \"@tiptap/core\";\n\nexport interface FontSizeOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n fontSize: {\n setFontSize: (size: string) => ReturnType;\n unsetFontSize: () => ReturnType;\n };\n }\n}\n\nexport const FontSize = Extension.create<FontSizeOptions>({\n name: \"fontSize\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n fontSize: {\n default: null,\n parseHTML: (element) =>\n element.style.fontSize?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.fontSize) {\n return {};\n }\n return {\n style: `font-size: ${attributes.fontSize}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setFontSize:\n (size: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { fontSize: size }).run();\n },\n unsetFontSize:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { fontSize: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LetterSpacingOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n letterSpacing: {\n setLetterSpacing: (spacing: string) => ReturnType;\n unsetLetterSpacing: () => ReturnType;\n };\n }\n}\n\nexport const LetterSpacing = Extension.create<LetterSpacingOptions>({\n name: \"letterSpacing\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n letterSpacing: {\n default: null,\n parseHTML: (element) =>\n element.style.letterSpacing?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.letterSpacing) {\n return {};\n }\n return {\n style: `letter-spacing: ${attributes.letterSpacing}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLetterSpacing:\n (spacing: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { letterSpacing: spacing }).run();\n },\n unsetLetterSpacing:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { letterSpacing: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\n/**\n * Routes plain End/Home through `Selection.modify` instead of the browser's\n * native caret movement.\n *\n * Chromium (verified 140–151, including stable Chrome) has a bug where a\n * native End or Home pressed while a triple-click selection is active puts\n * Blink's selection state into a mode where the next typed character\n * natively smooth-scrolls the nearest scrollable ancestor (the editor's\n * canvas, `.tpl-body`) to its very bottom (End) or top (Home), dragging the\n * caret out of view. The state cannot be repaired after the fact — clearing\n * or re-setting the DOM selection, blur/refocus, and preventDefault on the\n * clicks all fail — but a programmatic caret move never arms it.\n *\n * `Selection.modify(\"move\", direction, \"lineboundary\")` is the exact\n * equivalent of the native key (visual line boundary, bidi-aware via the\n * logical forward/backward directions), so behavior is unchanged for users.\n * Shift+End / Shift+Home extend selections natively and do not trigger the\n * bug, so only the plain keys are bound — a keymap entry without modifiers\n * never matches the shifted keys.\n */\nexport const LineBoundaryKeys = Extension.create({\n name: \"lineBoundaryKeys\",\n\n addKeyboardShortcuts() {\n const moveToLineBoundary =\n (direction: \"forward\" | \"backward\") =>\n ({ editor }: { editor: { view: unknown } }): boolean => {\n const view = editor.view as {\n composing: boolean;\n root: DocumentOrShadowRoot & {\n getSelection?: () => Selection | null;\n };\n dom: HTMLElement;\n };\n // During IME composition End/Home commit the composition; leave\n // that to the browser.\n if (!view || view.composing) return false;\n // `view.root` is the editable's Document or ShadowRoot, so the\n // selection resolves correctly in both mount modes. ShadowRoot's\n // getSelection is Chromium-only; elsewhere fall back to the owner\n // document (Chromium is also the only engine with the bug).\n const selection =\n typeof view.root?.getSelection === \"function\"\n ? view.root.getSelection()\n : (view.dom?.ownerDocument?.getSelection?.() ?? null);\n // `modify` is non-standard; without it, or with a selection that\n // is not ours, native handling is the safer default.\n if (!selection || typeof selection.modify !== \"function\") return false;\n if (!selection.anchorNode || !view.dom.contains(selection.anchorNode)) {\n return false;\n }\n selection.modify(\"move\", direction, \"lineboundary\");\n return true;\n };\n\n return {\n End: moveToLineBoundary(\"forward\"),\n Home: moveToLineBoundary(\"backward\"),\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LineHeightOptions {\n types: string[];\n defaultLineHeight: string;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n lineHeight: {\n setLineHeight: (lineHeight: string) => ReturnType;\n unsetLineHeight: () => ReturnType;\n };\n }\n}\n\nexport const LineHeight = Extension.create<LineHeightOptions>({\n name: \"lineHeight\",\n\n addOptions() {\n return {\n types: [\"paragraph\"],\n defaultLineHeight: \"1.5\",\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n lineHeight: {\n default: null,\n parseHTML: (element) => element.style.lineHeight || null,\n renderHTML: (attributes) => {\n if (!attributes.lineHeight) {\n return {};\n }\n return {\n style: `line-height: ${attributes.lineHeight}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLineHeight:\n (lineHeight: string) =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.updateAttributes(type, { lineHeight }),\n );\n },\n unsetLineHeight:\n () =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.resetAttributes(type, \"lineHeight\"),\n );\n },\n };\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import type { Editor } from \"@tiptap/core\";\n\n/**\n * Decides whether MergeTagNode / LogicMergeTagNode should swallow a\n * Backspace/Delete keystroke (returning `true` from a TipTap keymap\n * suppresses the default behavior).\n *\n * Only suppresses in the cursor-adjacent case so the \"first press\n * selects the atom, second press deletes it\" UX works. Range selections\n * (Cmd+A, drag-select, double-click-word) explicitly fall through —\n * default `replaceSelection` then deletes the entire range including\n * any merge-tag atoms inside it.\n *\n * Earlier revisions also returned `true` whenever `nodesBetween($from,\n * $to)` found a merge tag anywhere in the selection range — which\n * silently broke Cmd+A + Backspace for any paragraph containing a\n * merge tag (entire deletion was cancelled, nothing happened). The\n * cursor-adjacent protection is the only piece that should survive.\n */\nexport function isNodeSelected(editor: Editor, nodeTypeName: string): boolean {\n const { $from, $to } = editor.state.selection;\n\n // Range selection: let TipTap's default range-delete run. Even when\n // the range contains atom nodes, ProseMirror's replaceSelection\n // handles atomic deletion correctly.\n if ($from.pos !== $to.pos) return false;\n\n // Cursor-adjacent atom: protect from single-keystroke deletion.\n // (`$from.pos > 0` guards against reading `nodeBefore` at doc start\n // where ProseMirror's index would be undefined for nodeBefore.)\n if ($from.pos > 0 && $from.nodeBefore?.type.name === nodeTypeName) {\n return true;\n }\n if ($from.nodeAfter?.type.name === nodeTypeName) {\n return true;\n }\n\n return false;\n}\n","import type { Component } from \"vue\";\nimport { VueNodeViewRenderer } from \"@tiptap/vue-3\";\n\n/**\n * Typed wrapper for VueNodeViewRenderer that handles the known type mismatch\n * between Vue SFC default exports and TipTap's expected component type.\n */\nexport function renderVueNodeView(component: Component) {\n return VueNodeViewRenderer(\n component as Parameters<typeof VueNodeViewRenderer>[0],\n );\n}\n","import LogicMergeTagNodeView from \"./LogicMergeTagNodeView.vue\";\nimport type { SyntaxPreset } from \"@templatical/types\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n SYNTAX_PRESETS,\n} from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface LogicMergeTagNodeOptions {\n syntax: SyntaxPreset;\n}\n\nexport const LogicMergeTagNode = Node.create<LogicMergeTagNodeOptions>({\n name: \"logicMergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n // Both attributes are state, not markup, same as `MergeTagNode`:\n // `renderHTML()` emits the canonical `data-logic-merge-tag` /\n // `data-keyword` pair and `parseHTML` reads only those. `rendered: false`\n // keeps TipTap from also serializing each attribute under its own name,\n // which would put a write-only duplicate of the same data on every tag.\n value: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-logic-merge-tag\") || \"\",\n },\n keyword: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-keyword\") || element.textContent || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-logic-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n if (!isLogicMergeTagValue(node.attrs.value, this.options.syntax)) {\n return [\"span\", {}, node.attrs.value];\n }\n\n const keyword = getLogicMergeTagKeyword(\n node.attrs.value,\n this.options.syntax,\n );\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-logic-merge-tag\": node.attrs.value,\n \"data-keyword\": keyword,\n }),\n keyword,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(LogicMergeTagNodeView);\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.logic.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.logic.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, onScopeDispose, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst {\n getMergeTagLabel,\n getMergeTagDisplayLabel,\n findMergeTag,\n canRepickMergeTag,\n showRawValue,\n isMergeTagValue,\n requestMergeTag,\n} = useMergeTag();\nconst { t, format } = useI18n();\n\n// `node.attrs.label` is the label captured when the tag was inserted. It is\n// only ever a fallback — the configured tags win, which is what keeps a\n// relabelled tag from rendering its stale stored copy.\nconst displayLabel = computed(() =>\n getMergeTagDisplayLabel(props.node.attrs.value, props.node.attrs.label),\n);\nconst canRepick = computed(() => canRepickMergeTag(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// The chooser mounts in the popover root, outside this node view, so the node\n// can be torn down while it is open — a collaborator edit, a block change, the\n// editor unmounting. Writing attributes onto a destroyed node view throws.\nlet disposed = false;\nonScopeDispose(() => {\n disposed = true;\n});\n\n/** Whether the text currently in the raw input is a usable merge tag. */\nconst isEditValueValid = computed(\n () =>\n editValue.value.trim() === \"\" || isMergeTagValue(editValue.value.trim()),\n);\n\n/**\n * Swap this tag for another one, chosen through the same chooser insertion\n * uses. `requestMergeTag` sets the editor-wide requesting flag for the whole\n * await, which is what stops `useRichTextEditor`'s click-outside handler\n * finishing the block when the user clicks a row in the chooser — that would\n * dispose this node view and silently drop the update.\n */\nasync function repick(): Promise<void> {\n const replacement = await requestMergeTag({\n reason: \"edit\",\n current: findMergeTag(props.node.attrs.value),\n });\n if (disposed || !replacement) return;\n props.updateAttributes({\n value: replacement.value,\n label: replacement.label,\n });\n}\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\n/**\n * Activating the chip opens the chooser when one can resolve this token, and\n * the raw input only when none can. Editing a token as free text is how a\n * chip ends up holding something that is not a merge tag at all, which the\n * renderer then emits verbatim into the sent email.\n */\nfunction activate(): void {\n if (canRepick.value) {\n void repick();\n return;\n }\n startEditing();\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n // Never commit something that isn't a merge tag: `renderHTML` writes the\n // value straight into `data-merge-tag`, and the renderer drops that into the\n // MJML unchanged, so a typo would ship as literal text in the email.\n if (\n newValue &&\n newValue !== props.node.attrs.value &&\n isMergeTagValue(newValue)\n ) {\n props.updateAttributes({\n // The honest resolution, not the display chain: this is written into the\n // document as `data-label`, so a placeholder would be persisted as if it\n // were the tag's name.\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n // Enter on an invalid value keeps the input open rather than discarding\n // what was typed — the border and `aria-invalid` already say why.\n if (!isEditValueValid.value) return;\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n :aria-invalid=\"isEditValueValid ? undefined : 'true'\"\n class=\"tpl:w-32 tpl:rounded tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n :class=\"\n isEditValueValid\n ? 'tpl:border-none'\n : 'tpl:border tpl:border-[var(--tpl-danger)]'\n \"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"\n canRepick\n ? format(t.mergeTag.changeTag, { label: displayLabel })\n : t.mergeTag.editValue\n \"\n class=\"tpl:cursor-pointer\"\n :class=\"showRawValue ? 'tpl-tooltip' : ''\"\n :data-tooltip=\"showRawValue ? node.attrs.value : undefined\"\n @click.stop=\"activate\"\n @keydown.enter.stop=\"activate\"\n @keydown.space.prevent.stop=\"activate\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, onScopeDispose, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst {\n getMergeTagLabel,\n getMergeTagDisplayLabel,\n findMergeTag,\n canRepickMergeTag,\n showRawValue,\n isMergeTagValue,\n requestMergeTag,\n} = useMergeTag();\nconst { t, format } = useI18n();\n\n// `node.attrs.label` is the label captured when the tag was inserted. It is\n// only ever a fallback — the configured tags win, which is what keeps a\n// relabelled tag from rendering its stale stored copy.\nconst displayLabel = computed(() =>\n getMergeTagDisplayLabel(props.node.attrs.value, props.node.attrs.label),\n);\nconst canRepick = computed(() => canRepickMergeTag(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// The chooser mounts in the popover root, outside this node view, so the node\n// can be torn down while it is open — a collaborator edit, a block change, the\n// editor unmounting. Writing attributes onto a destroyed node view throws.\nlet disposed = false;\nonScopeDispose(() => {\n disposed = true;\n});\n\n/** Whether the text currently in the raw input is a usable merge tag. */\nconst isEditValueValid = computed(\n () =>\n editValue.value.trim() === \"\" || isMergeTagValue(editValue.value.trim()),\n);\n\n/**\n * Swap this tag for another one, chosen through the same chooser insertion\n * uses. `requestMergeTag` sets the editor-wide requesting flag for the whole\n * await, which is what stops `useRichTextEditor`'s click-outside handler\n * finishing the block when the user clicks a row in the chooser — that would\n * dispose this node view and silently drop the update.\n */\nasync function repick(): Promise<void> {\n const replacement = await requestMergeTag({\n reason: \"edit\",\n current: findMergeTag(props.node.attrs.value),\n });\n if (disposed || !replacement) return;\n props.updateAttributes({\n value: replacement.value,\n label: replacement.label,\n });\n}\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\n/**\n * Activating the chip opens the chooser when one can resolve this token, and\n * the raw input only when none can. Editing a token as free text is how a\n * chip ends up holding something that is not a merge tag at all, which the\n * renderer then emits verbatim into the sent email.\n */\nfunction activate(): void {\n if (canRepick.value) {\n void repick();\n return;\n }\n startEditing();\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n // Never commit something that isn't a merge tag: `renderHTML` writes the\n // value straight into `data-merge-tag`, and the renderer drops that into the\n // MJML unchanged, so a typo would ship as literal text in the email.\n if (\n newValue &&\n newValue !== props.node.attrs.value &&\n isMergeTagValue(newValue)\n ) {\n props.updateAttributes({\n // The honest resolution, not the display chain: this is written into the\n // document as `data-label`, so a placeholder would be persisted as if it\n // were the tag's name.\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n // Enter on an invalid value keeps the input open rather than discarding\n // what was typed — the border and `aria-invalid` already say why.\n if (!isEditValueValid.value) return;\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n :aria-invalid=\"isEditValueValid ? undefined : 'true'\"\n class=\"tpl:w-32 tpl:rounded tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n :class=\"\n isEditValueValid\n ? 'tpl:border-none'\n : 'tpl:border tpl:border-[var(--tpl-danger)]'\n \"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"\n canRepick\n ? format(t.mergeTag.changeTag, { label: displayLabel })\n : t.mergeTag.editValue\n \"\n class=\"tpl:cursor-pointer\"\n :class=\"showRawValue ? 'tpl-tooltip' : ''\"\n :data-tooltip=\"showRawValue ? node.attrs.value : undefined\"\n @click.stop=\"activate\"\n @keydown.enter.stop=\"activate\"\n @keydown.space.prevent.stop=\"activate\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all tpl:hover:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import MergeTagNodeView from \"./MergeTagNodeView.vue\";\nimport type { MergeTag, SyntaxPreset } from \"@templatical/types\";\nimport { getMergeTagLabel, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface MergeTagNodeOptions {\n /**\n * Read live, not captured. `renderHTML` and the input/paste rules resolve a\n * label every time they run, and `editor.setMergeTags` can replace the list\n * while a block is open for editing.\n */\n mergeTags: () => MergeTag[];\n syntax: SyntaxPreset;\n}\n\n/**\n * Inserted-node attrs are intentionally limited to `label` + `value`. The\n * `MergeTag` interface also carries optional `group` / `description` for\n * the built-in picker, but those are display-only and must NOT leak into\n * the document JSON — they would bloat templates and pollute serialized\n * state. `Pick<>` makes the constraint explicit at the type level.\n */\nexport type InsertMergeTagAttrs = Pick<MergeTag, \"label\" | \"value\">;\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n mergeTagNode: {\n insertMergeTag: (attrs: InsertMergeTagAttrs) => ReturnType;\n };\n }\n}\n\nexport const MergeTagNode = Node.create<MergeTagNodeOptions>({\n name: \"mergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n mergeTags: () => [],\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n // Both attributes are state, not markup: `renderHTML()` below emits the\n // canonical `data-merge-tag` / `data-label` pair and `parseHTML` reads\n // only those. `rendered: false` keeps TipTap from also serializing each\n // attribute under its own name, which would put a write-only duplicate\n // of the same data on every tag — invalid on a `<span>`, and paid again\n // through storage, autosave, snapshots and version history.\n label: {\n default: \"\",\n rendered: false,\n parseHTML: (element) =>\n element.getAttribute(\"data-label\") || element.textContent || \"\",\n },\n value: {\n default: \"\",\n rendered: false,\n parseHTML: (element) => element.getAttribute(\"data-merge-tag\") || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = getMergeTagLabel(node.attrs.value, this.options.mergeTags());\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-merge-tag\": node.attrs.value,\n \"data-label\": label,\n }),\n label,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(MergeTagNodeView);\n },\n\n addCommands() {\n return {\n insertMergeTag:\n (attrs: InsertMergeTagAttrs) =>\n ({ commands }) => {\n // Whitelist explicitly — even if a caller smuggles extra keys via\n // a wider type assertion, only label/value reach the node attrs.\n return commands.insertContent({\n type: this.name,\n attrs: { label: attrs.label, value: attrs.value },\n });\n },\n };\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.value.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags());\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.value.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags());\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","import type { MergeTag } from \"@templatical/types\";\nimport { Extension } from \"@tiptap/core\";\nimport Suggestion, {\n type SuggestionOptions,\n type SuggestionProps,\n type SuggestionKeyDownProps,\n} from \"@tiptap/suggestion\";\nimport type { Ref } from \"vue\";\nimport { createMergeTagPopup, filterMergeTags } from \"../utils/mergeTagPopup\";\n\nexport interface MergeTagSuggestionOptions {\n /** Available merge tags */\n /**\n * Read live, not captured: the list can be replaced while a block is being\n * edited (`editor.setMergeTags`), and a snapshot taken when the extension\n * was configured would keep suggesting the old tags.\n */\n mergeTags: () => MergeTag[];\n /** Trigger string (e.g. \"{{\", \"*|\", \"%%=\") */\n char: string;\n /** Localized empty-state label */\n emptyText: string;\n /**\n * Mount target for the suggestion popup. When provided with a non-null\n * `.value`, the popup attaches into that element instead of\n * `document.body` — keeping it inside the editor's effective DOM root\n * (shadow-aware). Pass the ref returned by `usePopoverRoot()`.\n *\n * Falls back to `document.body` when omitted or when the ref's value is\n * null at popup-open time (e.g. headless/test editors without an editor\n * shell). Preserves pre-Phase-3 behavior for callers that don't migrate.\n */\n popoverRoot?: Ref<HTMLElement | null> | null;\n}\n\n/**\n * Builds the `@tiptap/suggestion` `render` factory for the merge-tag popup.\n * A thin adapter over {@link createMergeTagPopup}: it maps ProseMirror's\n * `SuggestionProps` (items, `clientRect`, `view.dom`, `command`) onto the\n * controller. Exported as a standalone function so that mapping can be\n * unit-tested with mocked `SuggestionProps` instead of a full editor.\n */\nexport function createMergeTagSuggestionRenderer(\n emptyText: string,\n popoverRootRef?: Ref<HTMLElement | null> | null,\n): NonNullable<SuggestionOptions<MergeTag>[\"render\"]> {\n return () => {\n const popup = createMergeTagPopup(emptyText, popoverRootRef);\n\n return {\n onStart: (props: SuggestionProps<MergeTag>) => {\n // Use view.dom (ProseMirror contenteditable, actually\n // attached to the DOM) rather than options.element, which may\n // be a detached div when no `element` is passed to the editor\n // constructor (as is the case with @tiptap/vue-3 EditorContent).\n const viewDom = props.editor.view?.dom as HTMLElement | undefined;\n popup.open({\n items: props.items,\n getRect: props.clientRect ?? null,\n anchorEl: viewDom ?? null,\n onCommand: (item) => props.command(item),\n });\n },\n onUpdate: (props: SuggestionProps<MergeTag>) => {\n popup.update({\n items: props.items,\n getRect: props.clientRect ?? null,\n onCommand: (item) => props.command(item),\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps): boolean => {\n // Escape closes via TipTap's suggestion plugin (→ onExit); report it\n // handled without selecting.\n if (props.event.key === \"Escape\") {\n return true;\n }\n return popup.handleKeyDown(props.event);\n },\n onExit: () => popup.close(),\n };\n };\n}\n\nexport const MergeTagSuggestion = Extension.create<MergeTagSuggestionOptions>({\n name: \"mergeTagSuggestion\",\n\n addOptions() {\n return {\n mergeTags: () => [],\n char: \"{{\",\n emptyText: \"No matching merge tags\",\n popoverRoot: null,\n };\n },\n\n addProseMirrorPlugins() {\n const tags = this.options.mergeTags();\n const emptyText = this.options.emptyText;\n const popoverRootRef = this.options.popoverRoot;\n\n const config: Omit<SuggestionOptions<MergeTag>, \"editor\"> = {\n char: this.options.char,\n allowSpaces: false,\n startOfLine: false,\n // Default is [\" \"] which requires whitespace/line-start before the\n // trigger char — so `.{{` would not fire. Allow any preceding char.\n allowedPrefixes: null,\n items: ({ query }: { query: string }) => filterMergeTags(tags, query),\n command: ({\n editor,\n range,\n props,\n }: {\n editor: SuggestionProps<MergeTag>[\"editor\"];\n range: { from: number; to: number };\n props: MergeTag;\n }) => {\n // Use insertContentAt for atomic replace (matches the canonical\n // @tiptap/suggestion + Mention pattern). Avoids edge cases where\n // chained deleteRange + insertMergeTag fails to insert when the\n // selection state shifts mid-chain.\n editor\n .chain()\n .focus()\n .insertContentAt(range, {\n type: \"mergeTagNode\",\n attrs: { label: props.label, value: props.value },\n })\n .run();\n },\n render: createMergeTagSuggestionRenderer(emptyText, popoverRootRef),\n };\n\n return [\n Suggestion({\n editor: this.editor,\n ...config,\n }),\n ];\n },\n});\n"],"mappings":"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"}