@templatical/editor 0.39.4 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/{AiChatSidebar-5qqrS6uy.js → AiChatSidebar-a8TPBAl2.js} +3 -3
  2. package/dist/{AiFeatureMenu-BBO_Ojlp.js → AiFeatureMenu-CbgDjLqk.js} +3 -3
  3. package/dist/{BlockIssueBadge-D5oGxOQP.js → BlockIssueBadge-D8KsGWY6.js} +2 -2
  4. package/dist/BlockPreviewCanvas-XNV83YGg.js +133 -0
  5. package/dist/{CloudHeaderExtras-Cg6uF-yQ.js → CloudHeaderExtras-C-tbLyfE.js} +2 -2
  6. package/dist/{CloudPanels-D1Gg4qrG.js → CloudPanels-DnWDrgnQ.js} +4 -4
  7. package/dist/{CollaboratorBar-Cmn0LccS.js → CollaboratorBar-DUmeCkyD.js} +2 -2
  8. package/dist/{CommentsPanel-twNhYUqZ.js → CommentsPanel-9k1q-79y.js} +1 -1
  9. package/dist/{CommentsSidebar-DDO0Qx8U.js → CommentsSidebar-Bc7nORmD.js} +2 -2
  10. package/dist/{CountdownBlock-DSvbf3qU.js → CountdownBlock-C6R-hn7D.js} +1 -1
  11. package/dist/{CountdownToolbar-nvHxu8Gu.js → CountdownToolbar-DkC-qSgq.js} +4 -4
  12. package/dist/{DesignReferenceSidebar-C8ohNhOd.js → DesignReferenceSidebar-OoiYJAKS.js} +3 -3
  13. package/dist/{IssuesPanel-Ck0EGoV5.js → IssuesPanel-Dnns3O9z.js} +3 -3
  14. package/dist/{LogicTagInsertButton-D8IwH-9Z.js → LogicTagInsertButton-BimKobaG.js} +67 -67
  15. package/dist/{MergeTagInput-QI-5KG2R.js → MergeTagInput-DqpVzhy7.js} +1 -1
  16. package/dist/{MergeTagModeToggle-D793Bj_E.js → MergeTagModeToggle-BkIiAgpQ.js} +4 -4
  17. package/dist/{ParagraphEditor-D6FRxr8Y.js → ParagraphEditor-D67Cqh-T.js} +19 -19
  18. package/dist/{RestoreVersionDialog--b0qfNaK.js → RestoreVersionDialog-BaKDuPCX.js} +2 -2
  19. package/dist/{RichTextEditorContent-BC5UTVnb.js → RichTextEditorContent-AEtfKvLz.js} +5 -5
  20. package/dist/{SaveBlockDialog-C0BdEGeL.js → SaveBlockDialog-BxkI52fN.js} +8 -8
  21. package/dist/{SavedBlocksBrowserModal-BzDgXtr4.js → SavedBlocksBrowserModal-C4_25Kvn.js} +6 -6
  22. package/dist/{SavedBlocksPanels-DN-bDZzy.js → SavedBlocksPanels-DQ0w9G1O.js} +1 -1
  23. package/dist/{SavedBlocksPickBar-pEi9Et8Q.js → SavedBlocksPickBar-CtH-sfxh.js} +2 -2
  24. package/dist/{TemplateScoringPanel-B1wcfE0w.js → TemplateScoringPanel-Cs0TGtRF.js} +3 -3
  25. package/dist/{TemplateSettings-C_5leDdF.js → TemplateSettings-BD1WNP4C.js} +7 -7
  26. package/dist/{TestEmailModal-maM8SLBV.js → TestEmailModal-DZi58FU9.js} +70 -65
  27. package/dist/{TestEmailPanel-8iAZYpCK.js → TestEmailPanel-D__9Wvgz.js} +1 -1
  28. package/dist/{TitleEditor-L26QXwna.js → TitleEditor-v5p89Djs.js} +9 -9
  29. package/dist/{ToggleSwitch-JvQ_CmI1.js → ToggleSwitch-CJfQ8u57.js} +1 -1
  30. package/dist/{Toolbar-B2kDLFhn.js → Toolbar-C7SjilzH.js} +540 -525
  31. package/dist/{TplModal-DVMS_nAn.js → TplModal-B8uLyoXE.js} +3 -3
  32. package/dist/{VersionHistoryMenu-BVQityes.js → VersionHistoryMenu-b0A8mS2_.js} +2 -2
  33. package/dist/{VersionHistoryPanels-D8f8KvKX.js → VersionHistoryPanels-DfNrRT6y.js} +1 -1
  34. package/dist/{VersionPreviewBanner-BaMSgA1O.js → VersionPreviewBanner-xB9r83r0.js} +1 -1
  35. package/dist/WrapperBlock-yGmKw0T-.js +34 -0
  36. package/dist/{blockTypeIcons-CdXugKyV.js → blockTypeIcons-B0Bq1WT1.js} +1 -1
  37. package/dist/bundle-stats.json +6 -6
  38. package/dist/{ca-Bqeygp1_.js → ca-Ckgw9yd0.js} +1 -0
  39. package/dist/cdn/chunks/{AiChatSidebar-ctfIU4i0.js → AiChatSidebar-mKImUXi6.js} +7 -7
  40. package/dist/cdn/chunks/{AiChatSidebar-ctfIU4i0.js.map → AiChatSidebar-mKImUXi6.js.map} +1 -1
  41. package/dist/cdn/chunks/{AiFeatureMenu-ByygdH_9.js → AiFeatureMenu-23HCL517.js} +4 -4
  42. package/dist/cdn/chunks/{AiFeatureMenu-ByygdH_9.js.map → AiFeatureMenu-23HCL517.js.map} +1 -1
  43. package/dist/cdn/chunks/{BlockIssueBadge-9hHpaXb9.js → BlockIssueBadge-DS1pUf5g.js} +3 -3
  44. package/dist/cdn/chunks/{BlockIssueBadge-9hHpaXb9.js.map → BlockIssueBadge-DS1pUf5g.js.map} +1 -1
  45. package/dist/cdn/chunks/BlockPreviewCanvas-DObWyQfh.js +135 -0
  46. package/dist/cdn/chunks/BlockPreviewCanvas-DObWyQfh.js.map +1 -0
  47. package/dist/cdn/chunks/{CloudHeaderExtras-BxsMpvVr.js → CloudHeaderExtras-DOB_uWpt.js} +3 -3
  48. package/dist/cdn/chunks/{CloudHeaderExtras-BxsMpvVr.js.map → CloudHeaderExtras-DOB_uWpt.js.map} +1 -1
  49. package/dist/cdn/chunks/{CloudPanels-DmxmqAS_.js → CloudPanels-DcAn5QW7.js} +5 -5
  50. package/dist/cdn/chunks/{CloudPanels-DmxmqAS_.js.map → CloudPanels-DcAn5QW7.js.map} +1 -1
  51. package/dist/cdn/chunks/{CollaboratorBar-B8ttqAqX.js → CollaboratorBar-C0M8c2Vv.js} +3 -3
  52. package/dist/cdn/chunks/{CollaboratorBar-B8ttqAqX.js.map → CollaboratorBar-C0M8c2Vv.js.map} +1 -1
  53. package/dist/cdn/chunks/{CommentsPanel-C8ibhaiK.js → CommentsPanel-BtAGZfF6.js} +2 -2
  54. package/dist/cdn/chunks/{CommentsPanel-C8ibhaiK.js.map → CommentsPanel-BtAGZfF6.js.map} +1 -1
  55. package/dist/cdn/chunks/{CommentsSidebar-CybWfZ3V.js → CommentsSidebar-Ws07MXYj.js} +3 -3
  56. package/dist/cdn/chunks/{CommentsSidebar-CybWfZ3V.js.map → CommentsSidebar-Ws07MXYj.js.map} +1 -1
  57. package/dist/cdn/chunks/{CountdownBlock-DdC8Lg5F.js → CountdownBlock-sVDjKkRw.js} +2 -2
  58. package/dist/cdn/chunks/{CountdownBlock-DdC8Lg5F.js.map → CountdownBlock-sVDjKkRw.js.map} +1 -1
  59. package/dist/cdn/chunks/{CountdownToolbar-DRESXiVD.js → CountdownToolbar-Bu93WiS8.js} +5 -5
  60. package/dist/cdn/chunks/{CountdownToolbar-DRESXiVD.js.map → CountdownToolbar-Bu93WiS8.js.map} +1 -1
  61. package/dist/cdn/chunks/{DesignReferenceSidebar-CtaoY-hl.js → DesignReferenceSidebar-BlSdh5B7.js} +4 -4
  62. package/dist/cdn/chunks/{DesignReferenceSidebar-CtaoY-hl.js.map → DesignReferenceSidebar-BlSdh5B7.js.map} +1 -1
  63. package/dist/cdn/chunks/{IssuesPanel-C6RlIxoi.js → IssuesPanel-CKkv9z9g.js} +4 -4
  64. package/dist/cdn/chunks/{IssuesPanel-C6RlIxoi.js.map → IssuesPanel-CKkv9z9g.js.map} +1 -1
  65. package/dist/cdn/chunks/{LogicTagInsertButton-DpyPozop.js → LogicTagInsertButton-CHc1yZ9v.js} +165 -165
  66. package/dist/cdn/chunks/{LogicTagInsertButton-DpyPozop.js.map → LogicTagInsertButton-CHc1yZ9v.js.map} +1 -1
  67. package/dist/cdn/chunks/{MergeTagInput-ERJr7bP_.js → MergeTagInput--qTYfCWk.js} +2 -2
  68. package/dist/cdn/chunks/{MergeTagInput-ERJr7bP_.js.map → MergeTagInput--qTYfCWk.js.map} +1 -1
  69. package/dist/cdn/chunks/{MergeTagModeToggle-n3nvSNeQ.js → MergeTagModeToggle-CdWIGo3Q.js} +5 -5
  70. package/dist/cdn/chunks/{MergeTagModeToggle-n3nvSNeQ.js.map → MergeTagModeToggle-CdWIGo3Q.js.map} +1 -1
  71. package/dist/cdn/chunks/{ParagraphEditor-R2ph_UOo.js → ParagraphEditor-BilbVtrG.js} +17 -17
  72. package/dist/cdn/chunks/{ParagraphEditor-R2ph_UOo.js.map → ParagraphEditor-BilbVtrG.js.map} +1 -1
  73. package/dist/cdn/chunks/{RestoreVersionDialog-C-dwbb7T.js → RestoreVersionDialog-Q-a44Vjv.js} +3 -3
  74. package/dist/cdn/chunks/{RestoreVersionDialog-C-dwbb7T.js.map → RestoreVersionDialog-Q-a44Vjv.js.map} +1 -1
  75. package/dist/cdn/chunks/{RichTextEditorContent-Ch5zeyZ7.js → RichTextEditorContent-Fzuu9pLP.js} +6 -6
  76. package/dist/cdn/chunks/{RichTextEditorContent-Ch5zeyZ7.js.map → RichTextEditorContent-Fzuu9pLP.js.map} +1 -1
  77. package/dist/cdn/chunks/{SaveBlockDialog-C_BPjVv6.js → SaveBlockDialog-DeW_M6kh.js} +9 -9
  78. package/dist/cdn/chunks/{SaveBlockDialog-C_BPjVv6.js.map → SaveBlockDialog-DeW_M6kh.js.map} +1 -1
  79. package/dist/cdn/chunks/{SavedBlocksBrowserModal-CrrGEfFb.js → SavedBlocksBrowserModal-BzQWibKG.js} +6 -6
  80. package/dist/cdn/chunks/{SavedBlocksBrowserModal-CrrGEfFb.js.map → SavedBlocksBrowserModal-BzQWibKG.js.map} +1 -1
  81. package/dist/cdn/chunks/{SavedBlocksPanels-DfTaMPAr.js → SavedBlocksPanels-Ce1RePjk.js} +2 -2
  82. package/dist/cdn/chunks/{SavedBlocksPanels-DfTaMPAr.js.map → SavedBlocksPanels-Ce1RePjk.js.map} +1 -1
  83. package/dist/cdn/chunks/{SavedBlocksPickBar-DziwHYSu.js → SavedBlocksPickBar-DIDl6xfy.js} +3 -3
  84. package/dist/cdn/chunks/{SavedBlocksPickBar-DziwHYSu.js.map → SavedBlocksPickBar-DIDl6xfy.js.map} +1 -1
  85. package/dist/cdn/chunks/{TemplateScoringPanel-CR6RG6dZ.js → TemplateScoringPanel-d_Ppowsq.js} +49 -49
  86. package/dist/cdn/chunks/{TemplateScoringPanel-CR6RG6dZ.js.map → TemplateScoringPanel-d_Ppowsq.js.map} +1 -1
  87. package/dist/cdn/chunks/{TemplateSettings-DjXiiiNr.js → TemplateSettings-Dr5OJ0Gi.js} +8 -8
  88. package/dist/cdn/chunks/{TemplateSettings-DjXiiiNr.js.map → TemplateSettings-Dr5OJ0Gi.js.map} +1 -1
  89. package/dist/cdn/chunks/{TestEmailModal-DId9Og-N.js → TestEmailModal-CWTmaJuT.js} +56 -51
  90. package/dist/cdn/chunks/TestEmailModal-CWTmaJuT.js.map +1 -0
  91. package/dist/cdn/chunks/{TestEmailPanel-B8LwXmeX.js → TestEmailPanel-BdYsVHXA.js} +2 -2
  92. package/dist/cdn/chunks/{TestEmailPanel-B8LwXmeX.js.map → TestEmailPanel-BdYsVHXA.js.map} +1 -1
  93. package/dist/cdn/chunks/{TitleEditor-C7KMzX1e.js → TitleEditor-pJeeZ-PX.js} +9 -9
  94. package/dist/cdn/chunks/{TitleEditor-C7KMzX1e.js.map → TitleEditor-pJeeZ-PX.js.map} +1 -1
  95. package/dist/cdn/chunks/{ToggleSwitch-CDy8OfcB.js → ToggleSwitch-0dGCYnRY.js} +2 -2
  96. package/dist/cdn/chunks/{ToggleSwitch-CDy8OfcB.js.map → ToggleSwitch-0dGCYnRY.js.map} +1 -1
  97. package/dist/cdn/chunks/{Toolbar-DqFhWxgW.js → Toolbar-BsiHW9gY.js} +493 -478
  98. package/dist/cdn/chunks/{Toolbar-DqFhWxgW.js.map → Toolbar-BsiHW9gY.js.map} +1 -1
  99. package/dist/cdn/chunks/{TplModal-BKqDFLq3.js → TplModal-CEuGD7pt.js} +4 -4
  100. package/dist/cdn/chunks/{TplModal-BKqDFLq3.js.map → TplModal-CEuGD7pt.js.map} +1 -1
  101. package/dist/cdn/chunks/{VersionHistoryMenu-Dij1pTEb.js → VersionHistoryMenu-B3-hNu9c.js} +3 -3
  102. package/dist/cdn/chunks/{VersionHistoryMenu-Dij1pTEb.js.map → VersionHistoryMenu-B3-hNu9c.js.map} +1 -1
  103. package/dist/cdn/chunks/{VersionHistoryPanels-DcCFFDK6.js → VersionHistoryPanels-C1yck5gT.js} +2 -2
  104. package/dist/cdn/chunks/{VersionHistoryPanels-DcCFFDK6.js.map → VersionHistoryPanels-C1yck5gT.js.map} +1 -1
  105. package/dist/cdn/chunks/{VersionPreviewBanner-Df3FVlig.js → VersionPreviewBanner-PzVQSBJE.js} +2 -2
  106. package/dist/cdn/chunks/{VersionPreviewBanner-Df3FVlig.js.map → VersionPreviewBanner-PzVQSBJE.js.map} +1 -1
  107. package/dist/cdn/chunks/WrapperBlock-DGkbmj06.js +36 -0
  108. package/dist/cdn/chunks/WrapperBlock-DGkbmj06.js.map +1 -0
  109. package/dist/cdn/chunks/{ca-Bqeygp1_.js → ca-Ckgw9yd0.js} +2 -1
  110. package/dist/cdn/chunks/ca-Ckgw9yd0.js.map +1 -0
  111. package/dist/cdn/chunks/{cloud-eaXyxZ8l.js → cloud-CIT4yPp4.js} +2 -2
  112. package/dist/cdn/chunks/{cloud-eaXyxZ8l.js.map → cloud-CIT4yPp4.js.map} +1 -1
  113. package/dist/cdn/chunks/{createCloudRuntime-D5bXzcTe.js → createCloudRuntime-COUxbPvd.js} +49 -40
  114. package/dist/cdn/chunks/createCloudRuntime-COUxbPvd.js.map +1 -0
  115. package/dist/cdn/chunks/{de-CY6SlqBx.js → de-D4dweQw8.js} +2 -1
  116. package/dist/cdn/chunks/de-D4dweQw8.js.map +1 -0
  117. package/dist/{en-8V-2kDcp.js → cdn/chunks/en-VE7R7NyL.js} +3 -0
  118. package/dist/cdn/chunks/en-VE7R7NyL.js.map +1 -0
  119. package/dist/{es-Cmn1rT3l.js → cdn/chunks/es-B9OhP8H3.js} +3 -0
  120. package/dist/cdn/chunks/es-B9OhP8H3.js.map +1 -0
  121. package/dist/cdn/chunks/{extensions-Bhu8UEE5.js → extensions-CafoGF65.js} +5 -5
  122. package/dist/cdn/chunks/{extensions-Bhu8UEE5.js.map → extensions-CafoGF65.js.map} +1 -1
  123. package/dist/cdn/chunks/{fr-2yDa4zXM.js → fr-wrXhIi00.js} +2 -1
  124. package/dist/cdn/chunks/fr-wrXhIi00.js.map +1 -0
  125. package/dist/cdn/chunks/keys-DCWeHkSF.js +12 -0
  126. package/dist/cdn/chunks/keys-DCWeHkSF.js.map +1 -0
  127. package/dist/{nl-Bzk2zRI8.js → cdn/chunks/nl-DeFatKJe.js} +3 -0
  128. package/dist/cdn/chunks/nl-DeFatKJe.js.map +1 -0
  129. package/dist/cdn/chunks/preRenderCustomBlocks-Dpj-_cOj.js +21 -0
  130. package/dist/cdn/chunks/preRenderCustomBlocks-Dpj-_cOj.js.map +1 -0
  131. package/dist/cdn/chunks/{pt-BR-DqtSCTUi.js → pt-BR-Bh1J8ey6.js} +2 -1
  132. package/dist/cdn/chunks/pt-BR-Bh1J8ey6.js.map +1 -0
  133. package/dist/cdn/chunks/{socialIcons-9VrG8X3m.js → socialIcons-BvYNMn1G.js} +264 -258
  134. package/dist/cdn/chunks/socialIcons-BvYNMn1G.js.map +1 -0
  135. package/dist/cdn/chunks/{src-CqYYKcCI.js → src-CbGCES0u.js} +175 -168
  136. package/dist/cdn/chunks/src-CbGCES0u.js.map +1 -0
  137. package/dist/cdn/chunks/{src-CaNsA_WX.js → src-Cnc5E71L.js} +258 -247
  138. package/dist/cdn/chunks/src-Cnc5E71L.js.map +1 -0
  139. package/dist/cdn/chunks/{src-Dwgq7hf4.js → src-DveVYoWS.js} +281 -181
  140. package/dist/cdn/chunks/src-DveVYoWS.js.map +1 -0
  141. package/dist/cdn/chunks/{useCloudI18n-C09JcFS0.js → useCloudI18n-CrjWbpbu.js} +2 -2
  142. package/dist/cdn/chunks/{useCloudI18n-C09JcFS0.js.map → useCloudI18n-CrjWbpbu.js.map} +1 -1
  143. package/dist/cdn/chunks/{useEditorCore-CXYI14ux.js → useEditorCore-CQraU2s_.js} +421 -408
  144. package/dist/cdn/chunks/useEditorCore-CQraU2s_.js.map +1 -0
  145. package/dist/cdn/chunks/{useEditorRoot-DFx2fXJP.js → useEditorRoot-CshefZLP.js} +2 -2
  146. package/dist/cdn/chunks/{useEditorRoot-DFx2fXJP.js.map → useEditorRoot-CshefZLP.js.map} +1 -1
  147. package/dist/cdn/chunks/{useI18n-DTZ9sIcv.js → useI18n-CyO_Nmca.js} +2 -2
  148. package/dist/cdn/chunks/{useI18n-DTZ9sIcv.js.map → useI18n-CyO_Nmca.js.map} +1 -1
  149. package/dist/cdn/chunks/{useLogicTag-E2V0Xdcb.js → useLogicTag-DF7-l66G.js} +4 -4
  150. package/dist/cdn/chunks/{useLogicTag-E2V0Xdcb.js.map → useLogicTag-DF7-l66G.js.map} +1 -1
  151. package/dist/cdn/chunks/{useMergeTag-BE8oDoha.js → useMergeTag-DbIHw4NG.js} +4 -4
  152. package/dist/cdn/chunks/{useMergeTag-BE8oDoha.js.map → useMergeTag-DbIHw4NG.js.map} +1 -1
  153. package/dist/cdn/chunks/{usePopoverPosition-gB9q-ql7.js → usePopoverPosition-BJRvAb-h.js} +2 -2
  154. package/dist/cdn/chunks/{usePopoverPosition-gB9q-ql7.js.map → usePopoverPosition-BJRvAb-h.js.map} +1 -1
  155. package/dist/cdn/chunks/{useScrollToBlock-CtTXwmiU.js → useScrollToBlock-DgHZJvID.js} +2 -2
  156. package/dist/cdn/chunks/{useScrollToBlock-CtTXwmiU.js.map → useScrollToBlock-DgHZJvID.js.map} +1 -1
  157. package/dist/cdn/editor.css +1 -1
  158. package/dist/cdn/editor.js +484 -460
  159. package/dist/cdn/editor.js.map +1 -1
  160. package/dist/{cloud-C9xWZXhg.js → cloud-DsqqUfzh.js} +1 -1
  161. package/dist/{createCloudRuntime-BGd5--Bq.js → createCloudRuntime-iLflkYzP.js} +47 -38
  162. package/dist/{de-CY6SlqBx.js → de-D4dweQw8.js} +1 -0
  163. package/dist/{dist-tGkjkX-v.js → dist-DbUZvK04.js} +277 -176
  164. package/dist/{cdn/chunks/en-8V-2kDcp.js → en-VE7R7NyL.js} +1 -2
  165. package/dist/{cdn/chunks/es-Cmn1rT3l.js → es-B9OhP8H3.js} +1 -2
  166. package/dist/{extensions-DRN-elXT.js → extensions-DDhjuVKy.js} +4 -4
  167. package/dist/{fr-2yDa4zXM.js → fr-wrXhIi00.js} +1 -0
  168. package/dist/index.d.ts +65 -0
  169. package/dist/keys-CDqfZIRt.js +10 -0
  170. package/dist/{cdn/chunks/nl-Bzk2zRI8.js → nl-DeFatKJe.js} +1 -2
  171. package/dist/preRenderCustomBlocks-BBSqQgke.js +19 -0
  172. package/dist/{pt-BR-DqtSCTUi.js → pt-BR-Bh1J8ey6.js} +1 -0
  173. package/dist/{socialIcons-DkGcpEwC.js → socialIcons-O7PZCW2P.js} +245 -239
  174. package/dist/style.css +1 -1
  175. package/dist/templatical-editor.js +520 -496
  176. package/dist/{useCloudI18n-D1iedlP3.js → useCloudI18n-DpEsiLeR.js} +1 -1
  177. package/dist/{useEditorCore-BUVbQgwb.js → useEditorCore-Dt_VWNqX.js} +817 -804
  178. package/dist/{useEditorRoot-BOxn5EfP.js → useEditorRoot-CkB_5d0h.js} +1 -1
  179. package/dist/{useI18n-Ds_-druD.js → useI18n-B_CdcBwG.js} +1 -1
  180. package/dist/{useLogicTag-DoHfc08B.js → useLogicTag-D9pvgxQR.js} +3 -3
  181. package/dist/{useMergeTag-DXyum3kG.js → useMergeTag-BaVFPdlq.js} +5 -5
  182. package/dist/{usePopoverPosition-Du-Ah8E9.js → usePopoverPosition-DH7z3Ghm.js} +1 -1
  183. package/dist/{useScrollToBlock-DXQYPCnJ.js → useScrollToBlock-Dj4v3rxr.js} +1 -1
  184. package/package.json +7 -7
  185. package/dist/BlockPreviewCanvas-BMC16_QU.js +0 -131
  186. package/dist/cdn/chunks/BlockPreviewCanvas-DeS7Sa--.js +0 -133
  187. package/dist/cdn/chunks/BlockPreviewCanvas-DeS7Sa--.js.map +0 -1
  188. package/dist/cdn/chunks/TestEmailModal-DId9Og-N.js.map +0 -1
  189. package/dist/cdn/chunks/ca-Bqeygp1_.js.map +0 -1
  190. package/dist/cdn/chunks/createCloudRuntime-D5bXzcTe.js.map +0 -1
  191. package/dist/cdn/chunks/de-CY6SlqBx.js.map +0 -1
  192. package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js +0 -8
  193. package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js.map +0 -1
  194. package/dist/cdn/chunks/en-8V-2kDcp.js.map +0 -1
  195. package/dist/cdn/chunks/es-Cmn1rT3l.js.map +0 -1
  196. package/dist/cdn/chunks/fr-2yDa4zXM.js.map +0 -1
  197. package/dist/cdn/chunks/keys-dJbD1QPs.js +0 -12
  198. package/dist/cdn/chunks/keys-dJbD1QPs.js.map +0 -1
  199. package/dist/cdn/chunks/nl-Bzk2zRI8.js.map +0 -1
  200. package/dist/cdn/chunks/preRenderCustomBlocks-Bdpiiw4A.js +0 -23
  201. package/dist/cdn/chunks/preRenderCustomBlocks-Bdpiiw4A.js.map +0 -1
  202. package/dist/cdn/chunks/pt-BR-DqtSCTUi.js.map +0 -1
  203. package/dist/cdn/chunks/socialIcons-9VrG8X3m.js.map +0 -1
  204. package/dist/cdn/chunks/src-CaNsA_WX.js.map +0 -1
  205. package/dist/cdn/chunks/src-CqYYKcCI.js.map +0 -1
  206. package/dist/cdn/chunks/src-Dwgq7hf4.js.map +0 -1
  207. package/dist/cdn/chunks/useEditorCore-CXYI14ux.js.map +0 -1
  208. package/dist/emailFrameWidth-BmFCN2dp.js +0 -6
  209. package/dist/keys-BdoSgsz9.js +0 -10
  210. package/dist/preRenderCustomBlocks-DFXH54Zr.js +0 -21
@@ -1 +0,0 @@
1
- {"version":3,"file":"keys-dJbD1QPs.js","names":[],"sources":["../../../src/keys.ts"],"sourcesContent":["import {\n inject,\n shallowRef,\n type InjectionKey,\n type ComputedRef,\n type Ref,\n type ShallowRef,\n} from \"vue\";\nimport type {\n UseHistoryReturn,\n UseBlockActionsReturn,\n UseConditionPreviewReturn,\n} from \"@templatical/core\";\nimport type {\n BlockDefaults,\n CustomBlockDefinition,\n DisplayCondition,\n LogicPair,\n LogicTag,\n MergeTag,\n MergeTagRequestContext,\n ResolvePreview,\n SyntaxPreset,\n} from \"@templatical/types\";\nimport type { Translations, CloudTranslations } from \"./i18n\";\nimport type {\n AuthManager,\n UseAiConfigReturn,\n UseTemplateScoringReturn,\n} from \"@templatical/core/cloud\";\nimport type { UseSavedBlocksReturn } from \"@templatical/core\";\nimport type { BaseEditorReturn } from \"./composables/useEditorCore\";\nimport type { ImageUrlResolver } from \"./composables/useImageUrlResolver\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\nimport type { UseBlockRegistryReturn } from \"./composables/useBlockRegistry\";\nimport type { UseKeyboardReorderReturn } from \"./composables/useKeyboardReorder\";\nimport type { EditorCapabilities } from \"./types/editor-capabilities\";\nimport type { OnRequestMedia } from \"./index\";\n\n// ---------------------------------------------------------------------------\n// Shared keys (provided by useEditorCore, consumed by all components)\n// ---------------------------------------------------------------------------\n\nexport const TRANSLATIONS_KEY: InjectionKey<Translations> =\n Symbol(\"translations\");\n\nexport const EDITOR_KEY: InjectionKey<BaseEditorReturn> = Symbol(\"editor\");\n\nexport const HISTORY_KEY: InjectionKey<UseHistoryReturn> = Symbol(\"history\");\n\nexport const BLOCK_ACTIONS_KEY: InjectionKey<UseBlockActionsReturn> =\n Symbol(\"blockActions\");\n\nexport const CONDITION_PREVIEW_KEY: InjectionKey<UseConditionPreviewReturn> =\n Symbol(\"conditionPreview\");\n\n/**\n * Whether the hand-toggled display-condition filter applies on this surface —\n * provided per-surface, exactly like `USE_MERGE_TAG_SAMPLES_KEY`, and for the\n * same reason: the rule folds in both `previewMode` and whether a resolver owns\n * the preview, so a component that re-derived it from `CONDITION_PREVIEW_KEY`\n * plus the editor's `previewMode` could get one half wrong on its own.\n *\n * Absent means \"applies\" — a surface with no provider filters as it always has.\n */\nexport const APPLIES_CONDITION_FILTER_KEY: InjectionKey<ComputedRef<boolean>> =\n Symbol(\"appliesConditionFilter\");\n\nexport const FONTS_MANAGER_KEY: InjectionKey<UseFontsReturn> =\n Symbol(\"fontsManager\");\n\nexport const THEME_STYLES_KEY: InjectionKey<\n ComputedRef<Record<string, string>>\n> = Symbol(\"themeStyles\");\n\nexport const UI_THEME_KEY: InjectionKey<ComputedRef<string>> =\n Symbol(\"tplUiTheme\");\n\n/**\n * The editor's UI locale, exactly as the consumer passed it to\n * `init({ locale })` — unresolved, so it may be unsupported or malformed.\n *\n * For `Intl` formatting only. Translated *strings* come from\n * `TRANSLATIONS_KEY`, which is already resolved to a supported bundle; this is\n * for the labels `Intl` builds itself (dates), where the browser's own locale\n * would otherwise win and disagree with the surrounding chrome. Always format\n * through `formatAbsoluteDateTime`, which guards the malformed case.\n *\n * Not the template's `settings.locale` — that is the *email's* language and\n * belongs to content, not chrome.\n */\nexport const UI_LOCALE_KEY: InjectionKey<string | undefined> =\n Symbol(\"tplUiLocale\");\n\n// A ref, not a plain object: the recipient-facing half of these defaults\n// (video alt, countdown labels) tracks the template's own `settings.locale`,\n// which the author can change while the editor is open. Read it at insert\n// time — a value destructured at setup goes stale.\nexport const BLOCK_DEFAULTS_KEY: InjectionKey<ComputedRef<BlockDefaults>> =\n Symbol(\"blockDefaults\");\n\nexport const BLOCK_REGISTRY_KEY: InjectionKey<UseBlockRegistryReturn> =\n Symbol(\"blockRegistry\");\n\nexport const CUSTOM_BLOCK_DEFINITIONS_KEY: InjectionKey<\n CustomBlockDefinition[]\n> = Symbol(\"customBlockDefinitions\");\n\n/**\n * Consumer-supplied block-palette allowlist + order (`config.paletteBlocks`).\n * `undefined` means the default full palette. Consumed by `Sidebar.vue`.\n */\nexport const PALETTE_BLOCKS_KEY: InjectionKey<string[] | undefined> =\n Symbol(\"paletteBlocks\");\n\n/**\n * Whether HTML blocks render a live sandboxed-iframe preview in the canvas\n * (`config.htmlBlockPreview`), normalized to a boolean. `false` (the default)\n * keeps the static placeholder card. Consumed by `HtmlBlock.vue`.\n */\nexport const HTML_BLOCK_PREVIEW_KEY: InjectionKey<boolean> =\n Symbol(\"htmlBlockPreview\");\n\n/**\n * Consumer-supplied color-picker palette (`config.colors`), normalized to\n * `{ presets, allowCustom, allowCustomIgnored }` by `resolveColorsConfig`.\n * Provided by `useEditorCore`, consumed by every `ColorPicker`. The inject\n * default (`DEFAULT_RESOLVED_COLORS` = `{ presets: [], allowCustom: true,\n * allowCustomIgnored: false }`) makes an unconfigured picker render exactly\n * as before.\n */\nexport const COLORS_KEY: InjectionKey<\n import(\"./utils/resolveColorsConfig\").ResolvedColors\n> = Symbol(\"colors\");\n\n/**\n * The template settings the panel may render (`config.templateSettings.fields`),\n * resolved to a set of `TemplateSettings` member names by\n * `resolveTemplateSettingsFields`. Provided by `useEditorCore`, consumed by\n * `TemplateSettings.vue` for its cards and by `RightSidebar.vue` for the\n * Settings tab.\n *\n * Consumers inject with `ALL_TEMPLATE_SETTINGS_FIELDS` as the default, never an\n * empty set: the key is absent for anyone who configured nothing, and this\n * config only ever narrows.\n */\nexport const TEMPLATE_SETTINGS_FIELDS_KEY: InjectionKey<\n ReadonlySet<import(\"./utils/templateSettingsFields\").TemplateSettingsField>\n> = Symbol(\"templateSettingsFields\");\n\nexport const CUSTOM_BLOCK_STYLESHEETS_KEY: InjectionKey<ComputedRef<string[]>> =\n Symbol(\"customBlockStylesheets\");\n\n/**\n * The configured merge tags, as a ref so `editor.setMergeTags()` repaints\n * everything that renders one. `useEditorCore` is the only writer.\n *\n * `shallowRef`, not `ref`: the list is replaced wholesale, and a deep proxy\n * would make an in-place `push` by a consumer holding the original array\n * behave differently from the same push today. Consumers must go through\n * `setMergeTags`.\n */\nexport const MERGE_TAGS_KEY: InjectionKey<ShallowRef<MergeTag[]>> =\n Symbol(\"mergeTags\");\n\n/**\n * Inject default for {@link MERGE_TAGS_KEY} — headless callers and tests mount\n * components with no editor above them. Shared rather than allocated per call,\n * and never written: an editor always provides its own ref.\n */\nexport const NO_MERGE_TAGS: ShallowRef<MergeTag[]> = shallowRef([]);\n\nexport const MERGE_TAG_SYNTAX_KEY: InjectionKey<SyntaxPreset> =\n Symbol(\"mergeTagSyntax\");\n\nexport const ON_REQUEST_MERGE_TAG_KEY: InjectionKey<\n ((context?: MergeTagRequestContext) => Promise<MergeTag | null>) | null\n> = Symbol(\"onRequestMergeTag\");\n\nexport const MERGE_TAG_AUTOCOMPLETE_KEY: InjectionKey<boolean> = Symbol(\n \"mergeTagAutocomplete\",\n);\n\n/**\n * Whether a merge tag's tooltip reveals the raw token behind its label.\n * Resolved once from `mergeTags.showRawValue` and read by every surface that\n * renders a tag, so a chip in a sidebar field and a chip on the canvas cannot\n * disagree about whether the token is visible.\n */\nexport const MERGE_TAG_SHOW_RAW_VALUE_KEY: InjectionKey<boolean> = Symbol(\n \"mergeTagShowRawValue\",\n);\n\n/**\n * The user's choice between the previews' two merge-tag views: `true` for\n * Sample (tags replaced by their `MergeTag.sample`, rendered as ordinary text)\n * and `false` for Label (tags shown as their label with the usual cue).\n *\n * Session state owned by `useEditorCore` and driven by a toggle that exists\n * only on preview surfaces. Defaults to Sample.\n */\nexport const MERGE_TAG_SAMPLE_MODE_KEY: InjectionKey<Ref<boolean>> =\n Symbol(\"mergeTagSampleMode\");\n\n/**\n * Whether **the current render surface** substitutes samples.\n *\n * Provided per surface rather than globally, and that is the whole point:\n * substitution must never happen while editing, so `Canvas.vue` folds in its\n * `previewMode` flag and provides `false` whenever the canvas is editable, no\n * matter what the mode ref says. Preview surfaces provide the mode directly.\n *\n * Block components inject **this** key. They must never read\n * `MERGE_TAG_SAMPLE_MODE_KEY`, which would bypass the editing guard.\n */\nexport const USE_MERGE_TAG_SAMPLES_KEY: InjectionKey<ComputedRef<boolean>> =\n Symbol(\"useMergeTagSamples\");\n\n/**\n * The `resolvePreview` seam — resolved content plus its loading and failure\n * state. Provided by `useEditorCore` so every preview surface shares one\n * lifecycle; `null` when the consumer configured no hook.\n *\n * Surfaces read `content` from here instead of `editor.content`, and gate the\n * Sample/Label toggle on `supersedesSamples`.\n */\n/**\n * The raw `config.resolvePreview` hook.\n *\n * Provided alongside PREVIEW_RESOLUTION_KEY because the test-email dialog needs\n * its **own** resolution instance — it resolves for the selected recipient,\n * which the editor's canvas has no notion of — and so cannot reuse the shared\n * one. `undefined` when the consumer configured no hook.\n */\nexport const RESOLVE_PREVIEW_KEY: InjectionKey<ResolvePreview | undefined> =\n Symbol(\"resolvePreview\");\n\nexport const PREVIEW_RESOLUTION_KEY: InjectionKey<\n import(\"./composables/usePreviewResolution\").UsePreviewResolutionReturn | null\n> = Symbol(\"previewResolution\");\n\n/**\n * Singleton state for the built-in merge tag picker modal. Provided by\n * `useEditorCore`, consumed by `useMergeTag.requestMergeTag()` (to open\n * the modal as a fall-through when only `mergeTags.tags` is configured)\n * and by `MergeTagPickerModal.vue` (to render and resolve).\n */\nexport const MERGE_TAG_PICKER_KEY: InjectionKey<\n import(\"./composables/useMergeTagPicker\").UseMergeTagPickerReturn\n> = Symbol(\"mergeTagPicker\");\n\n/**\n * One \"a merge tag request is in flight\" flag per editor, provided by\n * `useEditorCore` and injected by every `useMergeTag()` call.\n *\n * Shared, not per-call: `useRichTextEditor.handleClickOutside` suppresses\n * editor teardown while this is set, because the picker modal mounts in the\n * popover root — outside `.tpl-text-editor-wrapper`. A ref private to one\n * `useMergeTag()` instance leaves that guard blind to a picker opened from\n * any other host (the link dialog's URL field), so the click on a picker row\n * tears the block out of edit mode and the pending insert then no-ops on its\n * own disposal guard. Falls back to a private ref for headless callers.\n */\nexport const MERGE_TAG_REQUESTING_KEY: InjectionKey<Ref<boolean>> =\n Symbol(\"mergeTagRequesting\");\n\n/** Logic-tag counterpart of {@link MERGE_TAG_REQUESTING_KEY}. */\nexport const LOGIC_TAG_REQUESTING_KEY: InjectionKey<Ref<boolean>> =\n Symbol(\"logicTagRequesting\");\n\n// ---------------------------------------------------------------------------\n// Logic tags — a standalone feature, separate from merge tags. Native\n// highlighting (LogicMergeTagNode) is always on; these drive the dedicated\n// logic picker + insertion.\n// ---------------------------------------------------------------------------\n\nexport const LOGIC_TAGS_KEY: InjectionKey<LogicTag[]> = Symbol(\"logicTags\");\n\nexport const LOGIC_PAIRS_KEY: InjectionKey<LogicPair[]> = Symbol(\"logicPairs\");\n\n/**\n * Singleton state for the built-in logic picker modal. Provided by\n * `useEditorCore`, consumed by `useLogicTag.requestLogicTag()` (to open the modal)\n * and by `LogicTagPickerModal.vue` (to render and resolve).\n */\nexport const LOGIC_TAG_PICKER_KEY: InjectionKey<\n import(\"./composables/useLogicTagPicker\").UseLogicTagPickerReturn\n> = Symbol(\"logicPicker\");\n\nexport const ON_REQUEST_LOGIC_TAG_KEY: InjectionKey<\n (() => Promise<LogicTag | LogicPair | null>) | null\n> = Symbol(\"onRequestLogic\");\n\nexport const ON_REQUEST_MEDIA_KEY: InjectionKey<OnRequestMedia | null> =\n Symbol(\"onRequestMedia\");\n\n/**\n * Whether a dropped image file may be uploaded. Distinct from Browse: a\n * read-only provider (`create: false`) still opens the library, but the\n * drop zone must not highlight. True when a host `onRequestMedia` is set\n * (the callback receives `files`) or when `provider.create` is a function.\n *\n * `null` when neither a provider nor a callback is configured — image\n * fields stay URL-only and drop is off. Injected as a computed; consumed\n * by ImageBlock / ImageToolbar / ImageField with a `null` default.\n */\nexport const CAN_DROP_MEDIA_KEY: InjectionKey<ComputedRef<boolean> | null> =\n Symbol(\"canDropMedia\");\n\n/**\n * Per-editor display-only image URL resolver (`config.resolveImageUrl`),\n * wrapped with a per-src cache by `createImageUrlResolver`. `null` when the\n * host doesn't resolve — the canvas then displays canonical src values\n * verbatim. Consumed via `useResolvedImageSrc` in image-displaying blocks.\n */\nexport const IMAGE_URL_RESOLVER_KEY: InjectionKey<ImageUrlResolver | null> =\n Symbol(\"imageUrlResolver\");\n\nexport const DISPLAY_CONDITIONS_KEY: InjectionKey<DisplayCondition[]> =\n Symbol(\"displayConditions\");\n\nexport const ALLOW_CUSTOM_CONDITIONS_KEY: InjectionKey<boolean> = Symbol(\n \"allowCustomConditions\",\n);\n\nexport const CAPABILITIES_KEY: InjectionKey<EditorCapabilities> =\n Symbol(\"capabilities\");\n\nexport const KEYBOARD_REORDER_KEY: InjectionKey<UseKeyboardReorderReturn> =\n Symbol(\"keyboardReorder\");\n\nexport const TEMPLATE_LINT_KEY: InjectionKey<\n import(\"./composables/useTemplateLint\").UseTemplateLintReturn | null\n> = Symbol(\"templateLint\");\n\n/**\n * The editor's effective DOM root — `Document` in light-DOM mode, `ShadowRoot`\n * when `shadowDom: true`. Composables that read `document.activeElement`,\n * popup mount targets, etc. should inject this and use the root-aware API\n * (both `Document` and `ShadowRoot` expose `activeElement` etc.).\n */\nexport const EDITOR_ROOT_KEY: InjectionKey<Document | ShadowRoot> =\n Symbol(\"editorRoot\");\n\n/**\n * Mount target for popovers, toolbars, and modal dialogs — never\n * `document.body`. Provided by `useEditorCore` as a ref bound\n * to a `<div class=\"tpl-popover-root\" />` rendered at the top level of the\n * editor template. Teleports use `:to=\"popoverRoot\"` so popups land inside\n * the editor's effective root — `document` in light-DOM mode, `ShadowRoot`\n * when `shadowDom: true` — instead of escaping the shadow boundary.\n *\n * Null until the editor's template mounts; consumers must guard with\n * `v-if=\"popoverRoot\"` before passing to `<Teleport>`.\n */\nexport const POPOVER_ROOT_KEY: InjectionKey<Ref<HTMLElement | null>> =\n Symbol(\"popoverRoot\");\n\n/**\n * Reactive saved-blocks state, provided by `useSavedBlocksFeature` when a\n * `SavedBlocksProvider` is configured. Shared by OSS and Cloud — the storage\n * transport lives behind the provider, so this key is not cloud-specific.\n */\nexport const SAVED_BLOCKS_KEY: InjectionKey<UseSavedBlocksReturn> =\n Symbol(\"savedBlocks\");\n\n// ---------------------------------------------------------------------------\n// Cloud-only keys (provided by createCloudRuntime, consumed by cloud components)\n// ---------------------------------------------------------------------------\n\nexport const AUTH_MANAGER_KEY: InjectionKey<AuthManager> =\n Symbol(\"authManager\");\n\nexport const AI_CONFIG_KEY: InjectionKey<UseAiConfigReturn> =\n Symbol(\"aiConfig\");\n\n// Comments are deliberately not injected: the panel takes `useCommentsFeature`'s\n// return as a **prop** the way `TestEmailPanel` and `SavedBlocksPanels` do, and the\n// per-block indicators read the comments entry on `CAPABILITIES_KEY`. Nothing\n// injects the composable itself.\n\nexport const SCORING_KEY: InjectionKey<UseTemplateScoringReturn> =\n Symbol(\"scoring\");\n\nexport const CLOUD_TRANSLATIONS_KEY: InjectionKey<CloudTranslations> =\n Symbol(\"cloudTranslations\");\n\n// ---------------------------------------------------------------------------\n// Helper for required injections with explicit null default + throw\n// ---------------------------------------------------------------------------\n\nexport function requireInject<T>(\n key: InjectionKey<T>,\n componentName: string,\n): T {\n const value = inject(key, null) as T | null;\n if (value === null || value === undefined) {\n throw new Error(\n `${componentName} requires a provider for ${key.description ?? \"unknown key\"}. Ensure it renders inside the editor, below Editor.vue.`,\n );\n }\n return value;\n}\n"],"mappings":";;AA2CA,IAAa,IACX,OAAO,cAAc,GAEV,IAA6C,OAAO,QAAQ,GAE5D,IAA8C,OAAO,SAAS,GAE9D,IACX,OAAO,cAAc,GAEV,IACX,OAAO,kBAAkB,GAWd,IACX,OAAO,wBAAwB,GAEpB,IACX,OAAO,cAAc,GAEV,IAET,OAAO,aAAa,GAEX,IACX,OAAO,YAAY,GAeR,IACX,OAAO,aAAa,GAMT,IACX,OAAO,eAAe,GAEX,IACX,OAAO,eAAe,GAEX,IAET,OAAO,wBAAwB,GAMtB,IACX,OAAO,eAAe,GAOX,IACX,OAAO,kBAAkB,GAUd,IAET,OAAO,QAAQ,GAaN,IAET,OAAO,wBAAwB,GAEtB,IACX,OAAO,wBAAwB,GAWpB,IACX,OAAO,WAAW,GAOP,IAAwC,EAAW,CAAC,CAAC,GAErD,IACX,OAAO,gBAAgB,GAEZ,IAET,OAAO,mBAAmB,GAEjB,IAAoD,OAC/D,sBACF,GAQa,IAAsD,OACjE,sBACF,GAUa,IACX,OAAO,oBAAoB,GAahB,IACX,OAAO,oBAAoB,GAkBhB,IACX,OAAO,gBAAgB,GAEZ,IAET,OAAO,mBAAmB,GAQjB,IAET,OAAO,gBAAgB,GAcd,IACX,OAAO,oBAAoB,GAGhB,IACX,OAAO,oBAAoB,GAQhB,IAA2C,OAAO,WAAW,GAE7D,IAA6C,OAAO,YAAY,GAOhE,IAET,OAAO,aAAa,GAEX,IAET,OAAO,gBAAgB,GAEd,IACX,OAAO,gBAAgB,GAYZ,IACX,OAAO,cAAc,GAQV,IACX,OAAO,kBAAkB,GAEd,IACX,OAAO,mBAAmB,GAEf,IAAqD,OAChE,uBACF,GAEa,IACX,OAAO,cAAc,GAEV,IACX,OAAO,iBAAiB,GAEb,IAET,OAAO,cAAc,GAQZ,IACX,OAAO,YAAY,GAaR,IACX,OAAO,aAAa,GAOT,IACX,OAAO,aAAa,GAMT,IACX,OAAO,aAAa,GAET,IACX,OAAO,UAAU,GAON,IACX,OAAO,SAAS,GAEL,IACX,OAAO,mBAAmB;AAM5B,SAAgB,EACd,GACA,GACG;CACH,IAAM,IAAQ,EAAO,GAAK,IAAI;CAC9B,IAAI,KAAU,MACZ,MAAU,MACR,GAAG,EAAc,2BAA2B,EAAI,eAAe,cAAc,yDAC/E;CAEF,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"nl-Bzk2zRI8.js","names":[],"sources":["../../../src/i18n/locales/nl.ts"],"sourcesContent":["import type en from \"./en\";\n\nconst nl: typeof en = {\n footer: {\n poweredBy: \"Mogelijk gemaakt door\",\n openSource: \"Open Source\",\n },\n\n header: {\n save: \"Opslaan\",\n saving: \"Opslaan...\",\n saved: \"Opgeslagen\",\n unsaved: \"Niet opgeslagen\",\n saveFailed: \"Opslaan mislukt\",\n saveNoTemplate: \"Laad of maak eerst een template\",\n templateName: \"Templatenaam\",\n rename: \"Template hernoemen\",\n untitled: \"Naamloos\",\n updatedAt: \"Bijgewerkt {time}\",\n createdAt: \"Gemaakt {time}\",\n updatedJustNow: \"Zojuist bijgewerkt\",\n createdJustNow: \"Zojuist gemaakt\",\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: \"Zojuist\",\n minutesAgo: \"{minutes} min geleden\",\n hoursAgo: \"{hours} u geleden\",\n daysAgo: \"{days} d geleden\",\n },\n\n // Toolbar - Paragraph\n paragraph: {\n paragraphSpacing: \"Alinea-afstand\",\n },\n\n history: {\n collabWarning:\n \"Ongedaan maken kan recente wijzigingen van medewerkers beïnvloeden\",\n undo: \"Ongedaan maken\",\n redo: \"Opnieuw\",\n },\n viewport: {\n label: \"Weergave\",\n desktop: \"Desktop\",\n mobile: \"Mobiel\",\n },\n darkMode: {\n enable: \"Voorbeeld in donkere modus\",\n disable: \"Voorbeeld in lichte modus\",\n },\n previewMode: {\n enable: \"Voorbeeldmodus\",\n disable: \"Voorbeeld sluiten\",\n },\n blocks: {\n section: \"Sectie\",\n image: \"Afbeelding\",\n title: \"Titel\",\n paragraph: \"Alinea\",\n button: \"Knop\",\n divider: \"Scheidingslijn\",\n video: \"Video\",\n social: \"Sociale media\",\n spacer: \"Tussenruimte\",\n html: \"HTML\",\n menu: \"Menu\",\n table: \"Tabel\",\n countdown: \"Aftelklok\",\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: \"Voer uw titel in\",\n paragraph: \"Voer hier uw tekst in\",\n button: \"Klik hier\",\n },\n sidebar: {\n content: \"Inhoud\",\n settings: \"Instellingen\",\n noSelection: \"Geen element geselecteerd\",\n noSelectionHint: \"Selecteer een blok op het canvas om het te bewerken\",\n },\n toolbar: {\n duplicate: \"Dupliceren\",\n delete: \"Verwijderen\",\n },\n titleEditor: {\n toolbar: \"Titelopmaak\",\n bold: \"Vet (Ctrl+B)\",\n italic: \"Cursief (Ctrl+I)\",\n addLink: \"Link toevoegen\",\n },\n paragraphEditor: {\n toolbar: \"Tekstopmaak\",\n bold: \"Vet (Ctrl+B)\",\n italic: \"Cursief (Ctrl+I)\",\n underline: \"Onderstreept (Ctrl+U)\",\n strikethrough: \"Doorgehaald\",\n subscript: \"Subscript\",\n superscript: \"Superscript\",\n addLink: \"Link toevoegen\",\n bulletList: \"Opsommingslijst\",\n numberedList: \"Genummerde lijst\",\n alignLeft: \"Links uitlijnen\",\n alignCenter: \"Centreren\",\n alignRight: \"Rechts uitlijnen\",\n alignJustify: \"Uitvullen\",\n clearFormatting: \"Opmaak wissen\",\n insertEmoji: \"Emoji invoegen\",\n fontFamily: \"Lettertype\",\n defaultFont: \"Standaard\",\n fontSize: \"Tekengrootte\",\n defaultSize: \"Standaard\",\n textColor: \"Tekstkleur\",\n highlightColor: \"Markeerkleur\",\n lineHeight: \"Regelafstand\",\n letterSpacing: \"Letterafstand\",\n emojiItemLabel: \"Emoji {emoji} invoegen\",\n },\n blockActions: {\n drag: \"Sleep om te herschikken, of druk op de spatiebalk om met het toetsenbord te verplaatsen\",\n dragLifted:\n \"{block} wordt verplaatst. Gebruik de pijltjestoetsen omhoog en omlaag om te herpositioneren, spatiebalk of Enter om neer te zetten, Escape om te annuleren.\",\n duplicate: \"Blok dupliceren\",\n delete: \"Blok verwijderen\",\n hiddenOnViewport: \"Verborgen op {viewport}\",\n saveAsBlock: \"Opslaan als blok\",\n conditionToggle: \"Weergavevoorwaarde in- of uitschakelen\",\n comments: \"Opmerkingen ({count})\",\n lifted: \"{block} opgetild. Positie {position} van {total}.\",\n moved: \"{block} verplaatst naar positie {position} van {total}.\",\n dropped: \"{block} neergezet op positie {position} van {total}.\",\n cancelled:\n \"Verplaatsing geannuleerd. {block} is terug op positie {position}.\",\n },\n section: {\n dropHere: \"Sleep blokken hierheen\",\n columns: \"Kolommen\",\n column1: \"1 kolom\",\n column2: \"2 kolommen\",\n column3: \"3 kolommen\",\n ratio12: \"Verhouding 1:2\",\n ratio21: \"Verhouding 2:1\",\n borderRadius: \"Hoekafronding\",\n wrapperEnable: \"Omhulsel toevoegen\",\n stackOnMobile: \"Stapelen op mobiel\",\n },\n linkDialog: {\n editLink: \"Link bewerken\",\n insertLink: \"Link invoegen\",\n updateLink: \"Link bijwerken\",\n removeLink: \"Link verwijderen\",\n cancel: \"Annuleren\",\n urlPlaceholder: \"https://example.com\",\n urlLabel: \"URL\",\n colorLabel: \"Linkkleur\",\n },\n title: {\n level: \"Titelniveau\",\n heading1: \"Titel 1 (36px)\",\n heading2: \"Titel 2 (28px)\",\n heading3: \"Titel 3 (22px)\",\n heading4: \"Titel 4 (18px)\",\n fontFamily: \"Lettertype\",\n inheritFont: \"Lettertype van het sjabloon gebruiken\",\n color: \"Kleur\",\n align: \"Uitlijnen\",\n alignLeft: \"Links\",\n alignCenter: \"Midden\",\n alignRight: \"Rechts\",\n },\n emoji: {\n smileys: \"Smileys\",\n gestures: \"Gebaren\",\n objects: \"Objecten\",\n },\n image: {\n imageUrl: \"URL van de afbeelding\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Alt-tekst\",\n altTextPlaceholder: \"Beschrijving van de afbeelding\",\n width: \"Breedte\",\n fullWidth: \"Volledige breedte\",\n widthCustom: \"Aangepast\",\n height: \"Hoogte\",\n heightAuto: \"Automatisch\",\n heightCustom: \"Aangepast\",\n borderRadius: \"Hoekafronding\",\n linkUrl: \"Link-URL\",\n openInNewTab: \"Openen in nieuw tabblad\",\n placeholderUrl: \"Tijdelijke afbeelding\",\n optional: \"(optioneel)\",\n placeholderUrlPlaceholder: \"https://... (alleen tijdens ontwerp)\",\n placeholderUrlTooltip:\n \"Omdat de afbeeldings-URL een samenvoegveld gebruikt, kunt u hier een echte afbeelding opgeven om de lay-out tijdens het ontwerpen te bekijken. Deze wordt niet opgenomen in het eindresultaat.\",\n clickToAdd: \"Klik om een afbeeldings-URL toe te voegen\",\n browseMedia: \"Mediabibliotheek openen\",\n dropToUpload: \"Sleep een afbeelding hierheen om te uploaden\",\n uploading: \"Uploaden…\",\n decorative: \"Decoratieve afbeelding\",\n decorativeHint:\n \"Verborgen voor schermlezers. Alleen gebruiken voor tussenruimtes en visuele versieringen.\",\n },\n video: {\n videoUrl: \"URL van de video\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n openInNewTab: \"Openen in nieuw tabblad\",\n customThumbnail: \"Aangepaste miniatuur\",\n optional: \"(optioneel)\",\n thumbnailPlaceholder: \"Automatisch gegenereerd uit de video-URL\",\n altText: \"Alt-tekst\",\n altTextPlaceholder: \"Beschrijving van de video\",\n width: \"Breedte\",\n fullWidth: \"Volledige breedte\",\n height: \"Hoogte\",\n heightAuto: \"Automatisch\",\n heightCustom: \"Aangepast\",\n placeholderUrl: \"Tijdelijke miniatuur\",\n placeholderUrlPlaceholder: \"https://... (alleen tijdens ontwerp)\",\n placeholderUrlTooltip:\n \"Omdat de video-URL een samenvoegveld gebruikt, kunt u hier een echte miniatuur opgeven om de lay-out tijdens het ontwerpen te bekijken. Deze wordt niet opgenomen in het eindresultaat.\",\n addVideo: \"Voeg een video-URL toe\",\n },\n button: {\n fontFamily: \"Lettertype\",\n inheritFont: \"Lettertype van het sjabloon gebruiken\",\n text: \"Tekst\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Openen in nieuw tabblad\",\n background: \"Achtergrond\",\n textColor: \"Tekstkleur\",\n borderRadius: \"Hoekafronding\",\n fontSize: \"Tekengrootte\",\n width: \"Breedte\",\n widthAuto: \"Aanpassen aan inhoud\",\n fullWidth: \"Volledige breedte\",\n widthCustom: \"Aangepast\",\n },\n divider: {\n style: \"Stijl\",\n solid: \"Doorlopend\",\n dashed: \"Streepjes\",\n dotted: \"Stippellijn\",\n color: \"Kleur\",\n thickness: \"Dikte\",\n },\n social: {\n icons: \"Iconen\",\n addIcon: \"Icoon toevoegen\",\n addIcons: \"Voeg sociale-media-iconen toe\",\n removeIcon: \"Verwijderen\",\n urlPlaceholder: \"https://...\",\n style: \"Stijl\",\n styleSolid: \"Gevuld\",\n styleOutlined: \"Omlijnd\",\n styleRounded: \"Afgerond\",\n styleSquare: \"Vierkant\",\n styleCircle: \"Cirkel\",\n size: \"Grootte\",\n sizeSmall: \"S\",\n sizeMedium: \"M\",\n sizeLarge: \"L\",\n spacing: \"Afstand\",\n align: \"Uitlijning\",\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: \"E-mail\",\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 menu: {\n items: \"Menu-items\",\n addItem: \"Item toevoegen\",\n removeItem: \"Verwijderen\",\n text: \"Tekst\",\n urlPlaceholder: \"https://example.com\",\n openInNewTab: \"Openen in nieuw tabblad\",\n bold: \"Vet\",\n underline: \"Onderstreept\",\n color: \"Kleur\",\n linkColor: \"Linkkleur\",\n fontSize: \"Tekengrootte\",\n fontFamily: \"Lettertype\",\n separator: \"Scheidingsteken\",\n separatorColor: \"Kleur van het scheidingsteken\",\n spacing: \"Afstand\",\n textAlign: \"Uitlijning\",\n addLinks: \"Voeg menulinks toe\",\n },\n table: {\n dimensions: \"Afmetingen\",\n rows: \"Rijen\",\n columns: \"Kolommen\",\n hasHeaderRow: \"Koprij\",\n headerBackgroundColor: \"Achtergrond van de koprij\",\n noHeaderBg: \"Geen achtergrond\",\n borderColor: \"Randkleur\",\n borderWidth: \"Randdikte\",\n cellPadding: \"Binnenmarge van cellen\",\n fontFamily: \"Lettertype\",\n fontSize: \"Tekengrootte\",\n color: \"Tekstkleur\",\n textAlign: \"Uitlijning\",\n cellPlaceholder: \"Voer tekst in...\",\n empty: \"Voeg een tabel toe\",\n },\n spacer: {\n height: \"Hoogte\",\n },\n countdown: {\n targetDate: \"Doeldatum\",\n timezone: \"Tijdzone\",\n display: \"Weergave\",\n days: \"Dagen\",\n hours: \"Uren\",\n minutes: \"Minuten\",\n seconds: \"Seconden\",\n separator: \"Scheidingsteken\",\n fontFamily: \"Lettertype\",\n inheritFont: \"Standaard\",\n digitFontSize: \"Cijfergrootte\",\n digitColor: \"Cijferkleur\",\n labelColor: \"Labelkleur\",\n labelFontSize: \"Labelgrootte\",\n background: \"Achtergrond\",\n labels: \"Labels\",\n expiry: \"Bericht na afloop\",\n expiredMessagePlaceholder: \"Deze aanbieding is verlopen\",\n expiredImageUrl: \"URL van de afbeelding na afloop\",\n hideOnExpiry: \"Verbergen na afloop\",\n setDate: \"Stel een doeldatum in via het instellingenpaneel\",\n hidden: \"Verborgen (verlopen)\",\n },\n customBlocks: {\n definitionNotFound: \"Onbekend bloktype — dit blok is niet geregistreerd\",\n renderError:\n \"Dit blok kon niet worden weergegeven. Controleer het bloksjabloon op fouten.\",\n fields: {\n required: \"Verplicht\",\n addItem: \"Item toevoegen\",\n removeItem: \"Verwijderen\",\n maxItemsReached: \"Maximaal aantal items bereikt\",\n minItemsRequired: \"Minimaal {count} items vereist\",\n },\n toolbar: {\n noDefinition:\n \"Registreer dit bloktype in uw SDK-configuratie om de eigenschappen te bewerken\",\n },\n dataSource: {\n fetchButton: \"Inhoud laden\",\n changeButton: \"Wijzigen\",\n fetching: \"Laden...\",\n readOnlyTooltip: \"Deze waarde wordt geladen uit uw gegevensbron\",\n fetchError: \"Inhoud laden mislukt\",\n },\n },\n html: {\n content: \"HTML-inhoud\",\n preview: \"Aangepast HTML-blok\",\n empty: \"Voeg HTML-inhoud toe in het paneel\",\n sanitizationHint:\n \"HTML wordt ongewijzigd geëxporteerd en niet opgeschoond — zorg dat de inhoud veilig is.\",\n },\n blockSettings: {\n spacing: \"Afstand\",\n padding: \"Binnenmarge\",\n background: \"Achtergrond\",\n color: \"Kleur\",\n display: \"Weergave\",\n showOnDesktop: \"Tonen op desktop\",\n showOnMobile: \"Tonen op mobiel\",\n hiddenOnDevice: \"Verborgen op {device}\",\n displayCondition: \"Weergavevoorwaarde\",\n selectCondition: \"Voorwaarde selecteren\",\n removeCondition: \"Voorwaarde verwijderen\",\n noCondition: \"Altijd zichtbaar\",\n conditionApplied: \"Voorwaarde toegepast\",\n customCondition: \"Aangepaste voorwaarde\",\n customConditionLabel: \"Naam van de voorwaarde\",\n customConditionBefore: \"Voor (openingslogica)\",\n customConditionAfter: \"Na (sluitingslogica)\",\n applyCondition: \"Toepassen\",\n cancelCondition: \"Annuleren\",\n customBadge: \"Aangepast\",\n restoreHiddenBlocks: \"Alle verborgen blokken tonen\",\n },\n templateSettings: {\n layout: \"Lay-out\",\n widthPreset: \"Vooraf ingestelde breedte\",\n customWidth: \"Aangepaste breedte\",\n appearance: \"Uiterlijk\",\n backgroundColor: \"Achtergrondkleur\",\n textColor: \"Tekstkleur\",\n linkColor: \"Linkkleur\",\n linkUnderline: \"Links onderstrepen\",\n fontFamily: \"Lettertype\",\n preheaderText: \"Voorbeeldtekst (preheader)\",\n preheaderTextPlaceholder:\n \"Voorbeeldtekst die na de onderwerpregel in de inbox wordt getoond...\",\n preheaderTextHint:\n \"Deze tekst verschijnt na de onderwerpregel in de voorbeelden van e-mailprogramma's. Samenvoegvelden worden ondersteund.\",\n language: \"Taal\",\n contentLocale: \"Taal van de inhoud\",\n contentLocaleHint:\n \"BCP 47-code (bv. nl, de, pt-BR). Bepaalt het lang-attribuut van de gerenderde e-mail zodat schermlezers de inhoud correct uitspreken.\",\n contentDirection: \"Rechts-naar-links\",\n contentDirectionHint:\n \"Schrijfrichting van de e-mail, onafhankelijk van de taal van de editor. Zonder waarde volgt dit de inhoudstaal (Arabisch, Hebreeuws, …).\",\n tips: \"Tips\",\n tip1: \"600px is de standaardbreedte voor e-mailsjablonen\",\n tip2: \"Gebruik webveilige lettertypen voor de beste compatibiliteit\",\n tip3: \"Lichte achtergronden lezen het prettigst\",\n },\n spacingControl: {\n lockAll: \"Alle zijden vergrendelen\",\n unlock: \"Zijden ontgrendelen\",\n top: \"Boven\",\n right: \"Rechts\",\n bottom: \"Onder\",\n left: \"Links\",\n decreaseTop: \"Boven verkleinen\",\n increaseTop: \"Boven vergroten\",\n decreaseLeft: \"Links verkleinen\",\n increaseLeft: \"Links vergroten\",\n decreaseRight: \"Rechts verkleinen\",\n increaseRight: \"Rechts vergroten\",\n decreaseBottom: \"Onder verkleinen\",\n increaseBottom: \"Onder vergroten\",\n },\n colorPicker: {\n pickColor: \"Kies een kleur\",\n hexValue: \"Hexadecimale kleurwaarde\",\n notSet: \"Niet ingesteld\",\n clear: \"Kleur wissen\",\n presetColors: \"Vooraf ingestelde kleuren\",\n },\n mergeTag: {\n editAsText: \"Als tekst bewerken\",\n fieldGroup: \"Waarde met samenvoegvelden\",\n remove: \"Samenvoegveld verwijderen\",\n insert: \"Samenvoegveld invoegen\",\n insertShort: \"Samenvoegveld\",\n editValue: \"Waarde van het samenvoegveld bewerken\",\n changeTag: \"Samenvoegveld wijzigen: {label}\",\n unknownLabel: \"Samenvoegveld\",\n deleteMergeTag: \"Samenvoegveld verwijderen\",\n suggestionEmpty: \"Geen overeenkomende samenvoegvelden\",\n picker: {\n title: \"Samenvoegveld invoegen\",\n changeTitle: \"Samenvoegveld wijzigen\",\n searchPlaceholder: \"Samenvoegvelden zoeken\",\n searchAriaLabel: \"Samenvoegvelden zoeken\",\n noResults: \"Geen overeenkomende samenvoegvelden\",\n empty: \"Geen samenvoegvelden geconfigureerd\",\n otherGroup: \"Overige\",\n close: \"Sluiten\",\n groupCount: \"{count}\",\n sample: \"Voorbeeld: {sample}\",\n },\n },\n logicTag: {\n insert: \"Logica invoegen\",\n insertShort: \"Logica\",\n picker: {\n title: \"Logica invoegen\",\n searchPlaceholder: \"Logicatags zoeken\",\n searchAriaLabel: \"Logicatags zoeken\",\n noResults: \"Geen overeenkomende logicatags\",\n empty: \"Geen logicatags geconfigureerd\",\n otherGroup: \"Overige\",\n close: \"Sluiten\",\n },\n },\n canvas: {\n noBlocks: \"Nog geen blokken\",\n dragHint: \"Begin vanaf nul door blokken vanuit de zijbalk te slepen\",\n dropHere: \"Hier neerzetten\",\n aiHintChat: \"of laat\",\n aiHintChatSuffix:\n \"in enkele seconden een volledig sjabloon voor u genereren\",\n aiHintDesign:\n \"Heeft u al een ontwerp? Upload een schermafbeelding, afbeelding of PDF en\",\n aiHintDesignSuffix: \"maakt het direct na\",\n },\n savedBlocks: {\n title: \"Opgeslagen blokken\",\n saveAsBlock: \"Opslaan als blok\",\n name: \"Naam\",\n namePlaceholder: \"bv. Koptekst, Voettekst, CTA...\",\n save: \"Blok opslaan\",\n saving: \"Opslaan...\",\n cancel: \"Annuleren\",\n empty: \"Nog geen opgeslagen blokken\",\n emptyHint:\n \"Sla blokken uit uw sjablonen op om ze later opnieuw te gebruiken.\",\n noResults: \"Geen opgeslagen blokken gevonden voor uw zoekopdracht\",\n search: \"Opgeslagen blokken zoeken...\",\n loading: \"Opgeslagen blokken laden…\",\n category: \"Categorie\",\n categoryPlaceholder: \"bv. Kopteksten, Promoties... (optioneel)\",\n allCategories: \"Alle categorieën\",\n filterByCategory: \"Filteren op categorie\",\n insert: \"Invoegen\",\n rename: \"Hernoemen\",\n delete: \"Verwijderen\",\n deleteConfirm: \"Dit opgeslagen blok verwijderen?\",\n blockCount: \"{count} blok(ken)\",\n browse: \"Opgeslagen blokken bekijken\",\n selectToPreview: \"Selecteer een opgeslagen blok voor een voorbeeld\",\n insertAtBeginning: \"Aan het begin\",\n insertAfterBlock: \"Na {block}\",\n insertAtEnd: \"Aan het einde\",\n insertPosition: \"Invoegpositie\",\n close: \"Sluiten\",\n pickToolbar: \"Selectie van opgeslagen blokken\",\n pickCount: \"{count} blok(ken) geselecteerd\",\n pickHint: \"Klik op blokken om ze toe te voegen of te verwijderen\",\n savingCount: \"{count} blok(ken) opslaan:\",\n reorderHint:\n \"Sleep om te herschikken — blokken worden in deze volgorde opgeslagen\",\n reorderHandle: \"{block} herschikken, positie {position} van {total}\",\n reorderAnnouncement:\n \"{block} verplaatst naar positie {position} van {total}\",\n expand: \"Uitklappen\",\n collapse: \"Inklappen\",\n expandPreview: \"Voorbeeld van {block} uitklappen\",\n collapsePreview: \"Voorbeeld van {block} inklappen\",\n },\n comments: {\n button: \"Opmerkingen\",\n title: \"Opmerkingen\",\n close: \"Sluiten\",\n placeholder: \"Schrijf een opmerking...\",\n replyPlaceholder: \"Schrijf een antwoord...\",\n reply: \"Antwoorden\",\n resolve: \"Oplossen\",\n unresolve: \"Heropenen\",\n delete: \"Verwijderen\",\n edit: \"Bewerken\",\n cancel: \"Annuleren\",\n save: \"Opslaan\",\n noComments: \"Nog geen opmerkingen\",\n noCommentsHint:\n \"Start een gesprek door een opmerking toe te voegen aan de template of aan een specifiek blok.\",\n deleteConfirm: \"Deze opmerking verwijderen?\",\n filterAll: \"Alle\",\n filterUnresolved: \"Niet opgelost\",\n filterBlock: \"Dit blok\",\n jumpToBlock: \"Blok\",\n ownedByYou: \"Jij\",\n edited: \"bewerkt\",\n resolvedBy: \"Opgelost door {name}\",\n replyOne: \"{count} antwoord\",\n replyMany: \"{count} antwoorden\",\n missingBlock: \"Blok ontbreekt\",\n saveTemplateFirst:\n \"Sla de template op voordat je een opmerking bij dit blok plaatst.\",\n },\n versionHistory: {\n tooltip: \"Versiegeschiedenis\",\n dropdownTitle: \"Versiegeschiedenis\",\n empty: \"Nog geen versies\",\n auto: \"auto\",\n olderVersion: \"Oudere versie\",\n newerVersion: \"Nieuwere versie\",\n },\n versionPreview: {\n message: \"U bekijkt een eerdere versie van dit sjabloon.\",\n cancel: \"Annuleren\",\n restore: \"Deze versie herstellen\",\n restoreConfirm: {\n title: \"Deze versie herstellen?\",\n unsavedWithSave:\n \"U hebt niet-opgeslagen wijzigingen. Sla ze eerst op en ze blijven in uw geschiedenis — herstelt u zonder op te slaan, dan gaan ze verloren.\",\n unsavedNoSave:\n \"U hebt niet-opgeslagen wijzigingen en nergens om ze op te slaan. Bij het herstellen van deze versie gaan ze verloren.\",\n saveAndRestore: \"Opslaan en herstellen\",\n restoreAnyway: \"Toch herstellen\",\n cancel: \"Annuleren\",\n },\n },\n previewResolution: {\n resolving: \"Voorbeeld wordt opgelost…\",\n failed:\n \"Het voorbeeld kon niet worden opgelost — het niet-opgeloste sjabloon wordt weergegeven.\",\n hint: \"Het voorbeeld gebruikt opgeloste gegevens uit uw backend.\",\n },\n mergeTagPreview: {\n label: \"Weergave van samenvoegvelden\",\n sample: \"Voorbeeldwaarde\",\n labelView: \"Naam\",\n },\n testEmail: {\n title: \"Test-e-mail verzenden\",\n recipientLabel: \"Ontvanger\",\n recipientPlaceholder: \"u@voorbeeld.nl\",\n invalidAddress: \"Voer een geldig e-mailadres in.\",\n preview: \"Voorbeeld\",\n previewHint:\n \"Samenvoegvelden worden onopgelost weergegeven — uw backend vult ze in.\",\n previewHintSample:\n \"Samenvoegvelden tonen voorbeeldwaarden — uw backend vult de echte gegevens in.\",\n recipientNotAllowed:\n \"Dit adres staat niet in de lijst met toegestane ontvangers.\",\n send: \"Verzenden\",\n sending: \"Wordt verzonden...\",\n cancel: \"Annuleren\",\n success: \"Test-e-mail succesvol verzonden\",\n button: \"Test\",\n },\n sidebarNav: {\n browseSavedBlocks: \"Opgeslagen blokken bekijken\",\n palette: \"Blokkenpalet\",\n insertBlock: \"Blok {block} invoegen\",\n },\n landmarks: {\n canvas: \"E-mailcanvas\",\n blockToolbar: \"Blokeigenschappen\",\n rightSidebar: \"Blokeigenschappen en sjablooninstellingen\",\n reorderAnnouncements: \"Aankondigingen van blokherschikking\",\n },\n errors: {\n editorLoading: \"Editor laden...\",\n editorLoadFailed: \"Editor laden mislukt.\",\n retry: \"Opnieuw proberen\",\n },\n issues: {\n panelTitle: \"Problemen\",\n panelTabLabel: \"Problemen\",\n groupErrors: \"Fouten\",\n groupWarnings: \"Waarschuwingen\",\n groupInfo: \"Info\",\n jump: \"Naar het blok springen\",\n fix: \"Herstellen\",\n emptyState: \"Geen problemen — ziet er goed uit.\",\n badgeError: \"Bevat fouten\",\n badgeWarning: \"Bevat waarschuwingen\",\n issueCountTooltip: \"{count} probleem(en)\",\n },\n smallScreen: {\n title: \"Groter scherm vereist\",\n message:\n \"De editor heeft meer ruimte nodig dan dit scherm biedt. Open hem op een tablet of desktop om te beginnen met bewerken.\",\n },\n};\n\nexport default nl;\n"],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- import { W as e } from "./src-Dwgq7hf4.js";
2
- //#region src/utils/preRenderCustomBlocks.ts
3
- async function t(t, n) {
4
- let r = async (t) => {
5
- if (e(t)) {
6
- let e = t;
7
- try {
8
- e.renderedHtml = await n.renderCustomBlock(e);
9
- } catch {
10
- e.renderedHtml = `<!-- Custom block render error: ${e.customType} -->`;
11
- }
12
- }
13
- if (t.type === "section" && "children" in t) {
14
- let e = t;
15
- for (let t of e.children) for (let e of t) await r(e);
16
- }
17
- };
18
- for (let e of t.blocks) await r(e);
19
- }
20
- //#endregion
21
- export { t };
22
-
23
- //# sourceMappingURL=preRenderCustomBlocks-Bdpiiw4A.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preRenderCustomBlocks-Bdpiiw4A.js","names":[],"sources":["../../../src/utils/preRenderCustomBlocks.ts"],"sourcesContent":["import type { Block, CustomBlock, TemplateContent } from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\n\n/**\n * The only thing this needs from the block registry. Named as a slice rather than\n * taking `UseBlockRegistryReturn` so the render-payload builder can supply the\n * editor instance's own `renderCustomBlock` without reaching for the registry.\n */\nexport interface CustomBlockResolver {\n renderCustomBlock: (block: CustomBlock) => Promise<string>;\n}\n\n/**\n * Renders every custom block in the content tree to its HTML representation\n * and stores the result on `block.renderedHtml`.\n *\n * **A contract obligation of every render, not a save-path detail.** A renderer\n * handed a custom block with neither a resolver nor `renderedHtml` omits it from\n * the output silently, and a backend has no way to resolve one: the HTML comes\n * from the consumer's liquid template plus the block's field values, and the\n * definition is registered in the browser. So this runs before a\n * `RenderProvider` sees the content, and before Cloud persists it.\n *\n * Failures per-block are swallowed (and replaced with a comment placeholder)\n * so one broken block doesn't block the save of the rest.\n */\nexport async function preRenderCustomBlocks(\n content: TemplateContent,\n registry: CustomBlockResolver,\n): Promise<void> {\n const renderBlock = async (block: Block): Promise<void> => {\n if (isCustomBlock(block)) {\n const customBlock = block as CustomBlock;\n try {\n customBlock.renderedHtml =\n await registry.renderCustomBlock(customBlock);\n } catch {\n customBlock.renderedHtml = `<!-- Custom block render error: ${customBlock.customType} -->`;\n }\n }\n\n if (block.type === \"section\" && \"children\" in block) {\n const sectionBlock = block as { children: Block[][] };\n for (const column of sectionBlock.children) {\n for (const child of column) {\n await renderBlock(child);\n }\n }\n }\n };\n\n for (const block of content.blocks) {\n await renderBlock(block);\n }\n}\n"],"mappings":";;AA0BA,eAAsB,EACpB,GACA,GACe;CACf,IAAM,IAAc,OAAO,MAAgC;EACzD,IAAI,EAAc,CAAK,GAAG;GACxB,IAAM,IAAc;GACpB,IAAI;IACF,EAAY,eACV,MAAM,EAAS,kBAAkB,CAAW;GAChD,QAAQ;IACN,EAAY,eAAe,mCAAmC,EAAY,WAAW;GACvF;EACF;EAEA,IAAI,EAAM,SAAS,aAAa,cAAc,GAAO;GACnD,IAAM,IAAe;GACrB,KAAK,IAAM,KAAU,EAAa,UAChC,KAAK,IAAM,KAAS,GAClB,MAAM,EAAY,CAAK;EAG7B;CACF;CAEA,KAAK,IAAM,KAAS,EAAQ,QAC1B,MAAM,EAAY,CAAK;AAE3B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pt-BR-DqtSCTUi.js","names":[],"sources":["../../../src/i18n/locales/pt-BR.ts"],"sourcesContent":["import type en from \"./en\";\n\nconst ptBR: typeof en = {\n // Footer (OSS only)\n footer: {\n poweredBy: \"Desenvolvido por\",\n openSource: \"Código Aberto\",\n },\n\n // Header — nome do template + estado de salvamento. Exibido apenas quando um\n // provedor de templates está configurado.\n header: {\n save: \"Salvar\",\n saving: \"Salvando...\",\n saved: \"Salvo\",\n unsaved: \"Não salvo\",\n saveFailed: \"Falha ao salvar\",\n saveNoTemplate: \"Carregue ou crie um template primeiro\",\n templateName: \"Nome do template\",\n rename: \"Renomear template\",\n untitled: \"Sem título\",\n updatedAt: \"Atualizado {time}\",\n createdAt: \"Criado {time}\",\n updatedJustNow: \"Atualizado agora mesmo\",\n createdJustNow: \"Criado agora mesmo\",\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: \"Agora mesmo\",\n minutesAgo: \"há {minutes}min\",\n hoursAgo: \"há {hours}h\",\n daysAgo: \"há {days}d\",\n },\n\n // History (undo/redo)\n history: {\n collabWarning: \"Desfazer pode afetar alterações recentes de colaboradores\",\n undo: \"Desfazer\",\n redo: \"Refazer\",\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: \"Visualizar Modo Escuro\",\n disable: \"Visualizar Modo Claro\",\n },\n\n // Preview mode\n previewMode: {\n enable: \"Modo de Visualização\",\n disable: \"Sair da Visualização\",\n },\n\n // Sidebar - Block types\n blocks: {\n section: \"Seção\",\n image: \"Imagem\",\n title: \"Título\",\n paragraph: \"Parágrafo\",\n button: \"Botão\",\n divider: \"Divisor\",\n video: \"Vídeo\",\n social: \"Redes Sociais\",\n spacer: \"Espaçador\",\n html: \"HTML\",\n menu: \"Menu\",\n table: \"Tabela\",\n countdown: \"Contagem Regressiva\",\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: \"Digite seu título\",\n paragraph: \"Digite seu texto aqui\",\n button: \"Clique aqui\",\n },\n\n // Right sidebar\n sidebar: {\n content: \"Conteúdo\",\n settings: \"Configurações\",\n noSelection: \"Nenhum elemento selecionado\",\n noSelectionHint: \"Selecione um bloco na tela para editá-lo\",\n },\n\n // Toolbar - Common\n toolbar: {\n duplicate: \"Duplicar\",\n delete: \"Excluir\",\n },\n\n // Title editor toolbar\n titleEditor: {\n toolbar: \"Formatação de título\",\n bold: \"Negrito (Ctrl+B)\",\n italic: \"Itálico (Ctrl+I)\",\n addLink: \"Adicionar Link\",\n },\n\n // Paragraph editor toolbar\n paragraphEditor: {\n toolbar: \"Formatação de texto\",\n bold: \"Negrito (Ctrl+B)\",\n italic: \"Itálico (Ctrl+I)\",\n underline: \"Sublinhado (Ctrl+U)\",\n strikethrough: \"Tachado\",\n subscript: \"Subscrito\",\n superscript: \"Sobrescrito\",\n addLink: \"Adicionar Link\",\n bulletList: \"Lista com Marcadores\",\n numberedList: \"Lista Numerada\",\n alignLeft: \"Alinhar à Esquerda\",\n alignCenter: \"Centralizar\",\n alignRight: \"Alinhar à Direita\",\n alignJustify: \"Justificar\",\n clearFormatting: \"Limpar Formatação\",\n insertEmoji: \"Inserir Emoji\",\n fontFamily: \"Família da Fonte\",\n defaultFont: \"Padrão\",\n fontSize: \"Tamanho da Fonte\",\n defaultSize: \"Padrão\",\n textColor: \"Cor do Texto\",\n highlightColor: \"Cor de Destaque\",\n lineHeight: \"Altura da Linha\",\n letterSpacing: \"Espaçamento entre Letras\",\n emojiItemLabel: \"Inserir emoji {emoji}\",\n },\n\n // Block actions (BlockWrapper)\n blockActions: {\n drag: \"Arraste para reordenar, ou pressione Espaço para mover com o teclado\",\n dragLifted:\n \"Movendo {block}. Use as setas para cima e para baixo para reposicionar, Espaço ou Enter para soltar, Escape para cancelar.\",\n duplicate: \"Duplicar bloco\",\n delete: \"Excluir bloco\",\n hiddenOnViewport: \"Oculto em {viewport}\",\n saveAsBlock: \"Salvar como Bloco\",\n conditionToggle: \"Alternar condição de exibição\",\n comments: \"Comentários ({count})\",\n lifted: \"{block} levantado. Posição {position} de {total}.\",\n moved: \"{block} movido para a posição {position} de {total}.\",\n dropped: \"{block} solto na posição {position} de {total}.\",\n cancelled: \"Movimento cancelado. {block} retornou à posição {position}.\",\n },\n\n // Toolbar - Section\n section: {\n dropHere: \"Solte blocos aqui\",\n columns: \"Colunas\",\n column1: \"1 Coluna\",\n column2: \"2 Colunas\",\n column3: \"3 Colunas\",\n ratio12: \"Proporção 1:2\",\n ratio21: \"Proporção 2:1\",\n borderRadius: \"Arredondamento das Bordas\",\n wrapperEnable: \"Adicionar wrapper\",\n stackOnMobile: \"Empilhar no celular\",\n },\n\n // Text editor link dialog\n linkDialog: {\n editLink: \"Editar Link\",\n insertLink: \"Inserir Link\",\n updateLink: \"Atualizar Link\",\n removeLink: \"Remover Link\",\n cancel: \"Cancelar\",\n urlPlaceholder: \"https://exemplo.com\",\n urlLabel: \"URL\",\n colorLabel: \"Cor do Link\",\n },\n\n // Toolbar - Title\n title: {\n level: \"Nível do Cabeçalho\",\n heading1: \"Cabeçalho 1 (36px)\",\n heading2: \"Cabeçalho 2 (28px)\",\n heading3: \"Cabeçalho 3 (22px)\",\n heading4: \"Cabeçalho 4 (18px)\",\n fontFamily: \"Família da Fonte\",\n inheritFont: \"Usar fonte do template\",\n color: \"Cor\",\n align: \"Alinhamento\",\n alignLeft: \"Esquerda\",\n alignCenter: \"Centro\",\n alignRight: \"Direita\",\n },\n\n // Emoji picker\n emoji: {\n smileys: \"Sorrisos\",\n gestures: \"Gestos\",\n objects: \"Objetos\",\n },\n\n // Toolbar - Image\n image: {\n imageUrl: \"URL da Imagem\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Texto Alternativo\",\n altTextPlaceholder: \"Descrição da imagem\",\n width: \"Largura\",\n fullWidth: \"Largura Total\",\n widthCustom: \"Personalizada\",\n height: \"Altura\",\n heightAuto: \"Automática\",\n heightCustom: \"Personalizada\",\n borderRadius: \"Arredondamento das Bordas\",\n linkUrl: \"URL do Link\",\n openInNewTab: \"Abrir em nova aba\",\n placeholderUrl: \"Imagem de Espaço Reservado\",\n optional: \"(opcional)\",\n placeholderUrlPlaceholder: \"https://... (somente em design)\",\n placeholderUrlTooltip:\n \"Como a URL da imagem usa uma merge tag, você pode fornecer uma imagem real aqui para visualizar o layout durante o design. Isso não é incluído na saída final.\",\n clickToAdd: \"Clique para adicionar a URL da imagem\",\n browseMedia: \"Explorar Mídia\",\n dropToUpload: \"Solte a imagem para enviar\",\n uploading: \"Enviando…\",\n decorative: \"Imagem decorativa\",\n decorativeHint:\n \"Oculta para leitores de tela. Use apenas para espaçadores e elementos visuais.\",\n },\n\n // Toolbar - Video\n video: {\n videoUrl: \"URL do Vídeo\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n openInNewTab: \"Abrir em nova aba\",\n customThumbnail: \"Miniatura Personalizada\",\n optional: \"(opcional)\",\n thumbnailPlaceholder: \"Gerada automaticamente a partir da URL do vídeo\",\n altText: \"Texto Alternativo\",\n altTextPlaceholder: \"Descrição do vídeo\",\n width: \"Largura\",\n fullWidth: \"Largura Total\",\n height: \"Altura\",\n heightAuto: \"Automática\",\n heightCustom: \"Personalizada\",\n placeholderUrl: \"Miniatura de Espaço Reservado\",\n placeholderUrlPlaceholder: \"https://... (somente em design)\",\n placeholderUrlTooltip:\n \"Como a URL do vídeo usa uma merge tag, você pode fornecer uma miniatura real aqui para visualizar o layout durante o design. Isso não é incluído na saída final.\",\n addVideo: \"Adicionar URL de vídeo\",\n },\n\n // Toolbar - Button\n button: {\n fontFamily: \"Família da Fonte\",\n inheritFont: \"Usar fonte do template\",\n text: \"Texto\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Abrir em nova aba\",\n background: \"Plano de Fundo\",\n textColor: \"Cor do Texto\",\n borderRadius: \"Arredondamento das Bordas\",\n fontSize: \"Tamanho da Fonte\",\n width: \"Largura\",\n widthAuto: \"Ajustar ao conteúdo\",\n fullWidth: \"Largura Total\",\n widthCustom: \"Personalizada\",\n },\n\n // Toolbar - Divider\n divider: {\n style: \"Estilo\",\n solid: \"Sólido\",\n dashed: \"Tracejado\",\n dotted: \"Pontilhado\",\n color: \"Cor\",\n thickness: \"Espessura\",\n },\n\n // Toolbar - Social Icons\n social: {\n icons: \"Ícones\",\n addIcon: \"Adicionar Ícone\",\n addIcons: \"Adicionar ícones de redes sociais\",\n removeIcon: \"Remover\",\n urlPlaceholder: \"https://...\",\n style: \"Estilo\",\n styleSolid: \"Sólido\",\n styleOutlined: \"Contornado\",\n styleRounded: \"Arredondado\",\n styleSquare: \"Quadrado\",\n styleCircle: \"Circular\",\n size: \"Tamanho\",\n sizeSmall: \"P\",\n sizeMedium: \"M\",\n sizeLarge: \"G\",\n spacing: \"Espaçamento\",\n align: \"Alinhamento\",\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: \"E-mail\",\n website: \"Site\",\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: \"Itens do Menu\",\n addItem: \"Adicionar Item\",\n removeItem: \"Remover\",\n text: \"Texto\",\n urlPlaceholder: \"https://exemplo.com\",\n openInNewTab: \"Abrir em nova aba\",\n bold: \"Negrito\",\n underline: \"Sublinhado\",\n color: \"Cor\",\n linkColor: \"Cor do Link\",\n fontSize: \"Tamanho da Fonte\",\n fontFamily: \"Família da Fonte\",\n separator: \"Separador\",\n separatorColor: \"Cor do Separador\",\n spacing: \"Espaçamento\",\n textAlign: \"Alinhamento\",\n addLinks: \"Adicionar links ao menu\",\n },\n\n // Toolbar - Table\n table: {\n dimensions: \"Dimensões\",\n rows: \"Linhas\",\n columns: \"Colunas\",\n hasHeaderRow: \"Linha de cabeçalho\",\n headerBackgroundColor: \"Plano de Fundo do Cabeçalho\",\n noHeaderBg: \"Sem plano de fundo\",\n borderColor: \"Cor da Borda\",\n borderWidth: \"Espessura da Borda\",\n cellPadding: \"Preenchimento da Célula\",\n fontFamily: \"Família da Fonte\",\n fontSize: \"Tamanho da Fonte\",\n color: \"Cor do Texto\",\n textAlign: \"Alinhamento\",\n cellPlaceholder: \"Digite o texto...\",\n empty: \"Adicionar uma tabela\",\n },\n\n // Toolbar - Paragraph\n paragraph: {\n paragraphSpacing: \"Espaçamento de Parágrafo\",\n },\n\n // Toolbar - Spacer\n spacer: {\n height: \"Altura\",\n },\n\n // Toolbar - Countdown\n countdown: {\n targetDate: \"Data Alvo\",\n timezone: \"Fuso Horário\",\n display: \"Exibição\",\n days: \"Dias\",\n hours: \"Horas\",\n minutes: \"Minutos\",\n seconds: \"Segundos\",\n separator: \"Separador\",\n fontFamily: \"Família da Fonte\",\n inheritFont: \"Padrão\",\n digitFontSize: \"Tamanho dos Dígitos\",\n digitColor: \"Cor dos Dígitos\",\n labelColor: \"Cor dos Rótulos\",\n labelFontSize: \"Tamanho dos Rótulos\",\n background: \"Plano de Fundo\",\n labels: \"Rótulos\",\n expiry: \"Mensagem de Expiração\",\n expiredMessagePlaceholder: \"Esta oferta expirou\",\n expiredImageUrl: \"URL da Imagem de Expiração\",\n hideOnExpiry: \"Ocultar ao expirar\",\n setDate: \"Defina uma data alvo no painel de configurações\",\n hidden: \"Oculto (expirado)\",\n },\n\n // Custom Blocks\n customBlocks: {\n definitionNotFound:\n \"Tipo de bloco desconhecido — este bloco não está registrado\",\n renderError:\n \"Este bloco não pôde ser renderizado. Verifique se há erros no template do bloco.\",\n fields: {\n required: \"Obrigatório\",\n addItem: \"Adicionar item\",\n removeItem: \"Remover\",\n maxItemsReached: \"Número máximo de itens atingido\",\n minItemsRequired: \"Mínimo de {count} itens obrigatório\",\n },\n toolbar: {\n noDefinition:\n \"Registre este tipo de bloco na sua configuração do SDK para editar suas propriedades\",\n },\n dataSource: {\n fetchButton: \"Carregar conteúdo\",\n changeButton: \"Alterar\",\n fetching: \"Carregando...\",\n readOnlyTooltip: \"Este valor é carregado da sua fonte de dados\",\n fetchError: \"Falha ao carregar conteúdo\",\n },\n },\n\n // Toolbar - HTML\n html: {\n content: \"Conteúdo HTML\",\n preview: \"Bloco HTML personalizado\",\n empty: \"Adicione conteúdo HTML no painel\",\n sanitizationHint:\n \"O HTML é exportado como está e não é sanitizado — garanta que o conteúdo seja seguro.\",\n },\n\n // Toolbar - Common block settings\n blockSettings: {\n spacing: \"Espaçamento\",\n padding: \"Preenchimento\",\n background: \"Plano de Fundo\",\n color: \"Cor\",\n display: \"Exibição\",\n showOnDesktop: \"Mostrar no desktop\",\n showOnMobile: \"Mostrar no mobile\",\n hiddenOnDevice: \"Oculto em {device}\",\n displayCondition: \"Condição de Exibição\",\n selectCondition: \"Selecionar condição\",\n removeCondition: \"Remover condição\",\n noCondition: \"Sempre visível\",\n conditionApplied: \"Condição aplicada\",\n customCondition: \"Condição personalizada\",\n customConditionLabel: \"Nome da condição\",\n customConditionBefore: \"Antes (lógica de abertura)\",\n customConditionAfter: \"Depois (lógica de fechamento)\",\n applyCondition: \"Aplicar\",\n cancelCondition: \"Cancelar\",\n customBadge: \"Personalizado\",\n restoreHiddenBlocks: \"Mostrar todos os blocos ocultos\",\n },\n\n // Template settings\n templateSettings: {\n layout: \"Layout\",\n widthPreset: \"Largura Predefinida\",\n customWidth: \"Largura Personalizada\",\n appearance: \"Aparência\",\n backgroundColor: \"Cor de Fundo\",\n textColor: \"Cor do Texto\",\n linkColor: \"Cor do Link\",\n linkUnderline: \"Sublinhar Links\",\n fontFamily: \"Família da Fonte\",\n preheaderText: \"Texto de Pré-cabeçalho\",\n preheaderTextPlaceholder:\n \"Texto de prévia exibido após o assunto na caixa de entrada...\",\n preheaderTextHint:\n \"Este texto aparece após o assunto nas pré-visualizações dos clientes de e-mail. Suporta merge tags.\",\n language: \"Idioma\",\n contentLocale: \"Idioma do conteúdo\",\n contentLocaleHint:\n \"Código BCP 47 (ex.: en, de, pt-BR). Define o atributo lang do e-mail renderizado para que leitores de tela pronunciem o conteúdo corretamente.\",\n contentDirection: \"Da direita para a esquerda\",\n contentDirectionHint:\n \"Direção de escrita do e-mail, independente do idioma do editor. Se não for definida, segue o idioma do conteúdo (árabe, hebraico, …).\",\n tips: \"Dicas\",\n tip1: \"600px é a largura padrão para templates de e-mail\",\n tip2: \"Use fontes seguras para web para melhor compatibilidade\",\n tip3: \"Fundos claros funcionam melhor para legibilidade\",\n },\n\n // Spacing control\n spacingControl: {\n lockAll: \"Travar todos os lados\",\n unlock: \"Destravar lados\",\n top: \"Superior\",\n right: \"Direito\",\n bottom: \"Inferior\",\n left: \"Esquerdo\",\n decreaseTop: \"Diminuir superior\",\n increaseTop: \"Aumentar superior\",\n decreaseLeft: \"Diminuir esquerdo\",\n increaseLeft: \"Aumentar esquerdo\",\n decreaseRight: \"Diminuir direito\",\n increaseRight: \"Aumentar direito\",\n decreaseBottom: \"Diminuir inferior\",\n increaseBottom: \"Aumentar inferior\",\n },\n\n // Color Picker\n colorPicker: {\n pickColor: \"Escolher uma cor\",\n hexValue: \"Valor hexadecimal da cor\",\n notSet: \"Não definido\",\n clear: \"Limpar cor\",\n presetColors: \"Cores predefinidas\",\n },\n\n // Merge Tag\n mergeTag: {\n editAsText: \"Editar como texto\",\n fieldGroup: \"Valor com merge tags\",\n remove: \"Remover merge tag\",\n insert: \"Inserir merge tag\",\n insertShort: \"Merge tag\",\n editValue: \"Editar valor da merge tag\",\n changeTag: \"Alterar merge tag: {label}\",\n unknownLabel: \"Merge tag\",\n deleteMergeTag: \"Excluir merge tag\",\n suggestionEmpty: \"Nenhuma merge tag correspondente\",\n picker: {\n title: \"Inserir merge tag\",\n changeTitle: \"Alterar merge tag\",\n searchPlaceholder: \"Pesquisar merge tags\",\n searchAriaLabel: \"Pesquisar merge tags\",\n noResults: \"Nenhuma merge tag correspondente\",\n empty: \"Nenhuma merge tag configurada\",\n otherGroup: \"Outros\",\n close: \"Fechar\",\n groupCount: \"{count}\",\n sample: \"Pré-visualização: {sample}\",\n },\n },\n\n // Logic tags (standalone, separate from merge tags)\n logicTag: {\n insert: \"Inserir lógica\",\n insertShort: \"Lógica\",\n picker: {\n title: \"Inserir lógica\",\n searchPlaceholder: \"Buscar tags de lógica\",\n searchAriaLabel: \"Buscar tags de lógica\",\n noResults: \"Nenhuma tag de lógica correspondente\",\n empty: \"Nenhuma tag de lógica configurada\",\n otherGroup: \"Outros\",\n close: \"Fechar\",\n },\n },\n\n // Canvas\n canvas: {\n noBlocks: \"Nenhum bloco ainda\",\n dragHint: \"Comece do zero arrastando blocos da barra lateral\",\n dropHere: \"Solte aqui\",\n aiHintChat: \"ou deixe a\",\n aiHintChatSuffix: \"gerar um template completo para você em segundos\",\n aiHintDesign:\n \"Tem um design existente? Envie uma captura de tela, imagem ou PDF e a\",\n aiHintDesignSuffix: \"recriará instantaneamente\",\n },\n\n // Sidebar\n savedBlocks: {\n title: \"Blocos Salvos\",\n saveAsBlock: \"Salvar como Bloco\",\n name: \"Nome\",\n namePlaceholder: \"ex.: Cabeçalho, Rodapé, CTA...\",\n save: \"Salvar Bloco\",\n saving: \"Salvando...\",\n cancel: \"Cancelar\",\n empty: \"Nenhum bloco salvo ainda\",\n emptyHint: \"Salve blocos dos seus templates para reutilizá-los depois.\",\n noResults: \"Nenhum bloco salvo corresponde à sua busca\",\n search: \"Buscar blocos salvos...\",\n loading: \"Carregando blocos salvos…\",\n category: \"Categoria\",\n categoryPlaceholder: \"ex.: Cabeçalhos, Promoções... (opcional)\",\n allCategories: \"Todas as categorias\",\n filterByCategory: \"Filtrar por categoria\",\n insert: \"Inserir\",\n rename: \"Renomear\",\n delete: \"Excluir\",\n deleteConfirm: \"Excluir este bloco salvo?\",\n blockCount: \"{count} bloco(s)\",\n browse: \"Explorar Blocos Salvos\",\n selectToPreview: \"Selecione um bloco salvo para visualizar\",\n insertAtBeginning: \"No início\",\n insertAfterBlock: \"Após {block}\",\n insertAtEnd: \"No final\",\n insertPosition: \"Posição de inserção\",\n close: \"Fechar\",\n pickToolbar: \"Seleção de bloco salvo\",\n pickCount: \"{count} bloco(s) selecionado(s)\",\n pickHint: \"Clique nos blocos para adicionar ou remover\",\n savingCount: \"Salvando {count} bloco(s):\",\n reorderHint: \"Arraste para reordenar — os blocos são salvos nesta ordem\",\n reorderHandle: \"Reordenar {block}, posição {position} de {total}\",\n reorderAnnouncement: \"{block} movido para a posição {position} de {total}\",\n expand: \"Expandir\",\n collapse: \"Recolher\",\n expandPreview: \"Expandir a prévia de {block}\",\n collapsePreview: \"Recolher a prévia de {block}\",\n },\n comments: {\n button: \"Comentários\",\n title: \"Comentários\",\n close: \"Fechar\",\n placeholder: \"Escreva um comentário...\",\n replyPlaceholder: \"Escreva uma resposta...\",\n reply: \"Responder\",\n resolve: \"Resolver\",\n unresolve: \"Reabrir\",\n delete: \"Excluir\",\n edit: \"Editar\",\n cancel: \"Cancelar\",\n save: \"Salvar\",\n noComments: \"Nenhum comentário ainda\",\n noCommentsHint:\n \"Inicie uma conversa adicionando um comentário ao template ou a um bloco específico.\",\n deleteConfirm: \"Excluir este comentário?\",\n filterAll: \"Todos\",\n filterUnresolved: \"Não resolvidos\",\n filterBlock: \"Este bloco\",\n jumpToBlock: \"Bloco\",\n ownedByYou: \"Você\",\n edited: \"editado\",\n resolvedBy: \"Resolvido por {name}\",\n replyOne: \"{count} Resposta\",\n replyMany: \"{count} Respostas\",\n missingBlock: \"Bloco ausente\",\n saveTemplateFirst: \"Salve o template antes de comentar neste bloco.\",\n },\n versionHistory: {\n tooltip: \"Histórico de versões\",\n dropdownTitle: \"Histórico de versões\",\n empty: \"Nenhuma versão ainda\",\n auto: \"auto\",\n olderVersion: \"Versão anterior\",\n newerVersion: \"Versão mais recente\",\n },\n versionPreview: {\n message: \"Você está visualizando uma versão anterior deste modelo.\",\n cancel: \"Cancelar\",\n restore: \"Restaurar esta versão\",\n restoreConfirm: {\n title: \"Restaurar esta versão?\",\n unsavedWithSave:\n \"Você tem alterações não salvas. Salve-as primeiro e elas ficarão no seu histórico — restaure sem salvar e elas serão perdidas.\",\n unsavedNoSave:\n \"Você tem alterações não salvas e nenhum lugar para salvá-las. Restaurar esta versão vai descartá-las.\",\n saveAndRestore: \"Salvar e restaurar\",\n restoreAnyway: \"Restaurar mesmo assim\",\n cancel: \"Cancelar\",\n },\n },\n previewResolution: {\n resolving: \"Resolvendo a pré-visualização…\",\n failed:\n \"Não foi possível resolver a pré-visualização — exibindo o modelo não resolvido.\",\n hint: \"A pré-visualização usa dados resolvidos pelo seu backend.\",\n },\n mergeTagPreview: {\n label: \"Exibição de merge tags\",\n sample: \"Exemplo\",\n labelView: \"Rótulo\",\n },\n testEmail: {\n title: \"Enviar E-mail de Teste\",\n recipientLabel: \"Destinatário\",\n recipientPlaceholder: \"voce@exemplo.com\",\n invalidAddress: \"Informe um endereço de e-mail válido.\",\n preview: \"Pré-visualização\",\n previewHint:\n \"As merge tags aparecem sem resolução — seu backend as preenche.\",\n previewHintSample:\n \"As merge tags mostram valores de exemplo — seu backend insere os dados reais.\",\n recipientNotAllowed:\n \"Esse endereço não está na lista de destinatários permitidos.\",\n send: \"Enviar\",\n sending: \"Enviando...\",\n cancel: \"Cancelar\",\n success: \"E-mail de teste enviado com sucesso\",\n button: \"Testar\",\n },\n sidebarNav: {\n browseSavedBlocks: \"Explorar blocos salvos\",\n palette: \"Paleta de blocos\",\n insertBlock: \"Inserir bloco {block}\",\n },\n\n // Landmark region labels for assistive technology\n landmarks: {\n canvas: \"Tela do e-mail\",\n blockToolbar: \"Propriedades do bloco\",\n rightSidebar: \"Propriedades do bloco e configurações do template\",\n reorderAnnouncements: \"Anúncios de reordenação de blocos\",\n },\n\n // Design Reference (cloud)\n errors: {\n editorLoading: \"Carregando editor...\",\n editorLoadFailed: \"Falha ao carregar o editor.\",\n retry: \"Tentar novamente\",\n },\n\n issues: {\n panelTitle: \"Problemas\",\n panelTabLabel: \"Problemas\",\n groupErrors: \"Erros\",\n groupWarnings: \"Avisos\",\n groupInfo: \"Informações\",\n jump: \"Ir para o bloco\",\n fix: \"Corrigir\",\n emptyState: \"Nenhum problema — tudo certo.\",\n badgeError: \"Possui erros\",\n badgeWarning: \"Possui avisos\",\n issueCountTooltip: \"{count} problema(s)\",\n },\n\n smallScreen: {\n title: \"Tela maior necessária\",\n message:\n \"O editor precisa de mais espaço do que esta tela oferece. Abra-o em um tablet ou desktop para começar a editar.\",\n },\n};\n\nexport default ptBR;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"socialIcons-9VrG8X3m.js","names":[],"sources":["../../../../core/src/error-reporting.ts","../../../../core/src/editor.ts","../../../../core/src/history.ts","../../../../core/src/block-actions.ts","../../../../core/src/auto-save.ts","../../../../core/src/condition-preview.ts","../../../../core/src/data-source-fetch.ts","../../../../core/src/history-interceptor.ts","../../../../core/src/saved-blocks.ts","../../../../core/src/saved-blocks-local.ts","../../../../core/src/media-local.ts","../../../../core/src/version-history.ts","../../../../core/src/comments.ts","../../../src/utils/blockTypeLabels.ts","../../../src/utils/resolveFieldColors.ts","../../../src/composables/useImageDrop.ts","../../../src/constants/socialIcons.ts"],"sourcesContent":["/**\n * Turn a thrown value into an `Error`.\n *\n * A `catch` clause's binding is `unknown` — a provider or a consumer's event\n * handler can throw a string, an object, or anything else. `onError` callbacks\n * across this package are typed to take an `Error`, so this is what makes that\n * contract honest at the one place values actually cross it.\n */\nexport function wrapReportedError(error: unknown): Error {\n return error instanceof Error\n ? error\n : new Error(String(error), { cause: error });\n}\n\n/**\n * Run a consumer's event handler without letting it fail the operation it\n * followed.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the caller has already succeeded by the time this runs. `onError` is taken\n * as a parameter rather than read from a shared reporter, so every feature\n * module keeps its own `options.onError` wiring and this stays reusable by\n * any module with the same fire-and-forget event shape.\n */\nexport function notifyHandler(\n onError: ((error: Error) => void) | undefined,\n run: () => void,\n): void {\n try {\n run();\n } catch (error) {\n onError?.(wrapReportedError(error));\n }\n}\n","import type {\n Block,\n ColumnLayout,\n Template,\n TemplateContent,\n TemplateDefaults,\n TemplatePatch,\n TemplateSaveTrigger,\n TemplateSettings,\n TemplatesProvider,\n UiTheme,\n ViewportSize,\n} from \"@templatical/types\";\nimport { createDefaultTemplateContent, SdkError } from \"@templatical/types\";\nimport { notifyHandler } from \"./error-reporting\";\n\nfunction getColumnCount(layout: ColumnLayout): number {\n if (layout === \"1\") return 1;\n if (layout === \"3\") return 3;\n return 2;\n}\nimport {\n computed,\n reactive,\n readonly,\n type DeepReadonly,\n type Ref,\n} from \"@vue/reactivity\";\n\nexport interface EditorState {\n /**\n * The template currently being edited, as the store last returned it — `null`\n * until `create()` or `load()` resolves, and always `null` without a\n * {@link TemplatesProvider}.\n *\n * Carries identity and name only for practical purposes: its `content` is the\n * store's copy from the last round-trip, whereas `state.content` is what the\n * user is editing.\n */\n template: Template | null;\n content: TemplateContent;\n selectedBlockId: string | null;\n viewport: ViewportSize;\n darkMode: boolean;\n previewMode: boolean;\n isDirty: boolean;\n /** True for the duration of a `save()` call. */\n isSaving: boolean;\n /** True for the duration of a `create()` or `load()` call. */\n isLoading: boolean;\n uiTheme: UiTheme;\n}\n\nexport interface UseEditorOptions {\n content: TemplateContent;\n defaultFontFamily?: string;\n templateDefaults?: TemplateDefaults;\n lockedBlocks?: Ref<Map<string, unknown>>;\n /**\n * Storage backend for the template's save/load lifecycle. Omit it and\n * `create()` / `load()` / `save()` reject — the editor keeps working as a\n * purely local editing surface.\n */\n templates?: TemplatesProvider;\n /**\n * Called with any error a provider call rejects with, before the rejection is\n * re-thrown to the caller.\n */\n onError?: (error: Error) => void;\n}\n\nexport interface UseEditorReturn {\n state: DeepReadonly<EditorState>;\n content: Ref<TemplateContent>;\n selectedBlock: Ref<Block | null>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n selectBlock: (blockId: string | null) => void;\n setViewport: (viewport: ViewportSize) => void;\n setDarkMode: (darkMode: boolean) => void;\n setPreviewMode: (previewMode: boolean) => void;\n setUiTheme: (theme: UiTheme) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n isBlockLocked: (blockId: string) => boolean;\n markDirty: () => void;\n findBlockLocation: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Rename the loaded template locally and mark the editor dirty. The new name\n * reaches the store on the next `save()`, in the same patch as the content —\n * a rename is an ordinary unsaved change, not a side channel.\n *\n * This method only stages it. The editor's inline rename field commits by\n * calling `setName()` and then saving immediately, because a rename reads as a\n * discrete action rather than an edit to be batched — so in the editor the\n * \"next `save()`\" is usually the one it triggers itself. A headless caller\n * decides its own moment.\n *\n * No-op when no template is loaded: there is nothing to name.\n */\n setName: (name: string) => void;\n /**\n * Persist the current content as a new template. `input.content`, when given,\n * replaces the editor's content first — so `create({ content })` both loads\n * and stores in one step.\n */\n create: (input?: {\n name?: string;\n content?: TemplateContent;\n }) => Promise<Template>;\n /** Fetch a template and make it the editor's content. */\n load: (templateId: string) => Promise<Template>;\n /**\n * Persist the loaded template's name + content as a patch.\n *\n * `trigger` is passed through to `TemplatesOptions.onSaved`. It defaults to\n * `\"api\"`, which is the honest answer for a direct call — the editor package\n * passes the value matching whichever affordance fired.\n */\n save: (trigger?: TemplateSaveTrigger) => Promise<Template>;\n /** Whether a template has been created or loaded. */\n hasTemplate: () => boolean;\n}\n\nexport function useEditor(options: UseEditorOptions): UseEditorReturn {\n const state = reactive<EditorState>({\n template: null,\n content:\n options.content ??\n createDefaultTemplateContent(\n options.defaultFontFamily,\n options.templateDefaults,\n ),\n selectedBlockId: null,\n viewport: \"desktop\",\n darkMode: false,\n previewMode: false,\n isDirty: false,\n isSaving: false,\n isLoading: false,\n uiTheme: \"auto\",\n });\n\n /**\n * Bumped by every content mutation. `save()` captures it before awaiting the\n * provider and only clears `isDirty` if it is unchanged afterwards, so an edit\n * made while a save is in flight is not reported as persisted — which would\n * also make autosave skip it, since that decides dirtiness at debounce time.\n */\n let revision = 0;\n\n function touch(): void {\n state.isDirty = true;\n revision++;\n }\n\n const content = computed({\n get: () => state.content,\n set: (value: TemplateContent) => {\n state.content = value;\n touch();\n },\n });\n\n const selectedBlock = computed(() => {\n if (!state.selectedBlockId) return null;\n return findBlockById(state.content.blocks, state.selectedBlockId);\n });\n\n function findBlockById(blocks: Block[], id: string): Block | null {\n for (const block of blocks) {\n if (block.id === id) return block;\n if (block.type === \"section\") {\n for (const column of block.children) {\n const found = findBlockById(column, id);\n if (found) return found;\n }\n }\n }\n return null;\n }\n\n function collectBlockIds(block: Block, ids: Set<string>): void {\n ids.add(block.id);\n if (block.type === \"section\") {\n for (const column of block.children) {\n for (const child of column) {\n collectBlockIds(child, ids);\n }\n }\n }\n }\n\n function findBlockParent(\n blocks: Block[],\n id: string,\n parent: {\n blocks: Block[];\n sectionId?: string;\n columnIndex?: number;\n } = { blocks },\n ): { blocks: Block[]; sectionId?: string; columnIndex?: number } | null {\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (block.id === id) return parent;\n if (block.type === \"section\") {\n for (let colIdx = 0; colIdx < block.children.length; colIdx++) {\n const result = findBlockParent(block.children[colIdx], id, {\n blocks: block.children[colIdx],\n sectionId: block.id,\n columnIndex: colIdx,\n });\n if (result) return result;\n }\n }\n }\n return null;\n }\n\n function isBlockLocked(blockId: string): boolean {\n return options.lockedBlocks?.value.has(blockId) ?? false;\n }\n\n function findBlockLocation(blockId: string): {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null {\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return null;\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index === -1) return null;\n return {\n targetSectionId: parent.sectionId,\n columnIndex: parent.columnIndex,\n index,\n };\n }\n\n // TODO(collab): the lock checks in addBlock/moveBlock/removeBlock/updateBlock\n // are shallow — they only consider the directly-targeted block id. A section\n // can still be removed, moved, or have its `children` array rewritten while a\n // peer is editing one of its descendants, which silently disrupts that peer's\n // edit. Add a `hasLockedDescendant(blockId)` helper and gate section-level\n // operations on it (and on the parent of each affected child) so cascades\n // through the tree are also blocked.\n\n function setContent(newContent: TemplateContent, markDirty = true): void {\n state.content = newContent;\n if (markDirty) {\n touch();\n }\n }\n\n function selectBlock(blockId: string | null): void {\n if (blockId && isBlockLocked(blockId)) {\n return;\n }\n state.selectedBlockId = blockId;\n }\n\n function setViewport(viewport: ViewportSize): void {\n state.viewport = viewport;\n }\n\n function setDarkMode(darkMode: boolean): void {\n state.darkMode = darkMode;\n }\n\n function setUiTheme(theme: UiTheme): void {\n state.uiTheme = theme;\n }\n\n function setPreviewMode(previewMode: boolean): void {\n state.previewMode = previewMode;\n if (previewMode) {\n state.selectedBlockId = null;\n }\n }\n\n function updateBlock(blockId: string, updates: Partial<Block>): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const block = findBlockById(state.content.blocks, blockId);\n if (block) {\n Object.assign(block, updates);\n touch();\n }\n }\n\n function updateSettings(updates: Partial<TemplateSettings>): void {\n state.content.settings = { ...state.content.settings, ...updates };\n touch();\n }\n\n function addBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex = 0,\n index?: number,\n ): void {\n // Sections cannot be nested inside a column — MJML forbids `mj-section`\n // inside `mj-column`, so the renderer drops them on export (issue #292).\n // Reject the nest up front rather than lose the content silently later.\n if (targetSectionId && block.type === \"section\") {\n return;\n }\n if (targetSectionId) {\n if (isBlockLocked(targetSectionId)) {\n return;\n }\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (section && section.type === \"section\") {\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n const targetArray = section.children[columnIndex];\n if (index !== undefined && index < targetArray.length) {\n targetArray.splice(index, 0, block);\n } else {\n targetArray.push(block);\n }\n }\n } else {\n if (index !== undefined && index < state.content.blocks.length) {\n state.content.blocks.splice(index, 0, block);\n } else {\n state.content.blocks.push(block);\n }\n }\n touch();\n }\n\n function removeBlock(blockId: string): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n const parent = findBlockParent(state.content.blocks, blockId);\n if (parent) {\n const index = parent.blocks.findIndex((b) => b.id === blockId);\n if (index !== -1) {\n const [removed] = parent.blocks.splice(index, 1);\n if (state.selectedBlockId) {\n const removedIds = new Set<string>();\n collectBlockIds(removed, removedIds);\n if (removedIds.has(state.selectedBlockId)) {\n state.selectedBlockId = null;\n }\n }\n touch();\n }\n }\n }\n\n function moveBlock(\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex = 0,\n ): void {\n if (isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && isBlockLocked(targetSectionId)) {\n return;\n }\n\n const parent = findBlockParent(state.content.blocks, blockId);\n if (!parent) return;\n\n const oldIndex = parent.blocks.findIndex((b) => b.id === blockId);\n if (oldIndex === -1) return;\n\n // Sections cannot be nested inside a column (issue #292) — refuse to move\n // a section into a section column; MJML would drop it on export.\n if (targetSectionId && parent.blocks[oldIndex].type === \"section\") {\n return;\n }\n\n // Resolve target before mutating the source — otherwise an invalid\n // targetSectionId leaves the block spliced-out and unrecoverable.\n let targetArray: Block[];\n if (targetSectionId) {\n const section = findBlockById(state.content.blocks, targetSectionId);\n if (!section || section.type !== \"section\") return;\n if (columnIndex < 0 || columnIndex >= getColumnCount(section.columns)) {\n return;\n }\n section.children[columnIndex] = section.children[columnIndex] || [];\n targetArray = section.children[columnIndex];\n } else {\n targetArray = state.content.blocks;\n }\n\n const [block] = parent.blocks.splice(oldIndex, 1);\n targetArray.splice(newIndex, 0, block);\n\n touch();\n }\n\n function markDirty(): void {\n touch();\n }\n\n // -------------------------------------------------------------------------\n // Template lifecycle (over an optional TemplatesProvider)\n // -------------------------------------------------------------------------\n\n /**\n * A refused call throws rather than resolving, because a resolved promise\n * would read as \"saved\" to whoever awaited it.\n *\n * The message names the provider key the consumer set, not an internal\n * capability flag: `EditorCapabilities` is type-only and its injection key is\n * unexported, so neither a core nor an editor consumer can read one. Note the\n * editor hides its own save controls when `save` is withheld but has no\n * create affordance to hide at all, so `create()` is reachable by definition.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(`[Templatical] Templates: ${action} ${reason}`);\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for something that landed.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function requireProvider(action: string): TemplatesProvider {\n const { templates } = options;\n if (!templates) {\n refuse(\n action,\n \"needs a templates provider. Pass one as `init({ templates })` to enable saving and loading.\",\n );\n }\n return templates;\n }\n\n function setName(name: string): void {\n if (!state.template) return;\n state.template = { ...state.template, name };\n touch();\n }\n\n async function create(input?: {\n name?: string;\n content?: TemplateContent;\n }): Promise<Template> {\n const provider = requireProvider(\"create()\");\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\n \"create()\",\n \"is disabled by the provider — its `create` is `false`.\",\n );\n }\n\n let created: Template;\n state.isLoading = true;\n try {\n if (input?.content) {\n state.content = input.content;\n }\n // Omit `name` entirely when unset rather than sending `undefined` — a\n // provider serialising the input to JSON would otherwise receive a key it\n // never asked for.\n const revisionAtRequest = revision;\n created = await providerCreate(\n input?.name !== undefined\n ? { name: input.name, content: state.content }\n : { content: state.content },\n );\n state.template = created;\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isLoading = false;\n }\n // Below the `finally` on purpose: `isLoading` is cleared there, so a handler\n // must not observe the editor still loading. Unreachable on failure — the\n // catch rethrows.\n notify(() => provider.onCreated?.(created));\n return created;\n }\n\n async function load(templateId: string): Promise<Template> {\n const provider = requireProvider(\"load()\");\n\n let loaded: Template;\n state.isLoading = true;\n try {\n loaded = await provider.load(templateId);\n state.template = loaded;\n state.content = loaded.content;\n state.isDirty = false;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isLoading = false;\n }\n // Below the `finally` on purpose: `isLoading` is cleared there, so a handler\n // must not observe the editor still loading. Unreachable on failure — the\n // catch rethrows.\n notify(() => provider.onLoaded?.(loaded));\n return loaded;\n }\n\n async function save(trigger: TemplateSaveTrigger = \"api\"): Promise<Template> {\n const provider = requireProvider(\"save()\");\n const { save: providerSave } = provider;\n if (typeof providerSave !== \"function\") {\n refuse(\"save()\", \"is disabled by the provider — its `save` is `false`.\");\n }\n const current = state.template;\n if (!current) {\n refuse(\n \"save()\",\n \"has no template loaded. Call create() or load() first.\",\n );\n }\n\n // The name rides the same patch as the content: a rename is an ordinary\n // unsaved change, so one round-trip persists both.\n const patch: TemplatePatch =\n current.name !== undefined\n ? { name: current.name, content: state.content }\n : { content: state.content };\n\n let saved: Template;\n state.isSaving = true;\n try {\n const revisionAtRequest = revision;\n saved = await providerSave(current.id, patch);\n // Adopt the response only while it still describes what is open. A\n // `load()` that landed mid-flight has already replaced the template, so\n // the response is stale.\n if (state.template?.id === current.id) {\n // The patch was built before the await, so a rename that landed during\n // it is absent from the response. Taking the response's name verbatim\n // reverts what the user typed — and because the next save reads the\n // name back off `state.template`, it would then persist the stale one,\n // losing the rename outright rather than merely on screen.\n const localName = state.template.name;\n state.template =\n localName !== current.name ? { ...saved, name: localName } : saved;\n }\n // Deliberately conditional. An edit landing while the request was in\n // flight is not persisted by it, and clearing the flag would both claim it\n // was and make autosave — which checks dirtiness at debounce time — skip\n // the follow-up save.\n if (revision === revisionAtRequest) {\n state.isDirty = false;\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n state.isSaving = false;\n }\n // Below the `finally` on purpose: `isSaving` is cleared there, and a handler\n // that navigates must observe a settled editor or the consumer's own dirty\n // guard blocks their navigation. Unreachable on failure — the catch rethrows.\n notify(() => provider.onSaved?.(saved, { trigger }));\n return saved;\n }\n\n function hasTemplate(): boolean {\n return state.template?.id !== undefined;\n }\n\n return {\n state: readonly(state),\n content,\n selectedBlock,\n isBlockLocked,\n setContent,\n selectBlock,\n setViewport,\n setDarkMode,\n setUiTheme,\n setPreviewMode,\n updateBlock,\n updateSettings,\n addBlock,\n removeBlock,\n moveBlock,\n markDirty,\n findBlockLocation,\n setName,\n create,\n load,\n save,\n hasTemplate,\n };\n}\n","import { safeClone, type TemplateContent } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"@vue/reactivity\";\n\nexport interface UseHistoryOptions {\n content: Ref<TemplateContent>;\n setContent: (content: TemplateContent, markDirty?: boolean) => void;\n isRemoteOperation?: () => boolean;\n maxSize?: number;\n}\n\nexport interface UseHistoryReturn {\n canUndo: ComputedRef<boolean>;\n canRedo: ComputedRef<boolean>;\n isNavigating: Ref<boolean>;\n undo: () => void;\n redo: () => void;\n record: () => void;\n recordDebounced: (blockId: string) => void;\n clear: () => void;\n destroy: () => void;\n}\n\ninterface DebouncedSnapshot {\n blockId: string;\n timeoutId: ReturnType<typeof setTimeout>;\n}\n\nconst MAX_STACK_SIZE = 50;\nconst DEBOUNCE_MS = 300;\nconst NAVIGATE_IDLE_MS = 1500;\n\nexport function useHistory(options: UseHistoryOptions): UseHistoryReturn {\n const {\n content,\n setContent,\n isRemoteOperation,\n maxSize = MAX_STACK_SIZE,\n } = options;\n\n const undoStack = ref<TemplateContent[]>([]);\n const redoStack = ref<TemplateContent[]>([]);\n const isNavigating = ref(false);\n let navigatingTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let pendingDebounce: DebouncedSnapshot | null = null;\n\n const canUndo = computed(() => undoStack.value.length > 0);\n const canRedo = computed(() => redoStack.value.length > 0);\n\n function cloneContent(): TemplateContent {\n // Cycle-safe deep clone (see `safeClone`). A naked JSON.stringify throws\n // `Converting circular structure to JSON` if a DOM element with a Sortable\n // expando back-ref ever sneaks into block data via a drag handler — which\n // would freeze the undo stack, since every subsequent mutation calls\n // `record()`. safeClone drops the back-ref instead of throwing.\n return safeClone(content.value);\n }\n\n function pushToUndoStack(snapshot: TemplateContent): void {\n undoStack.value.push(snapshot);\n if (undoStack.value.length > maxSize) {\n undoStack.value.splice(0, undoStack.value.length - maxSize);\n }\n }\n\n function flushPendingDebounce(): void {\n if (pendingDebounce) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce = null;\n }\n }\n\n function record(): void {\n if (isRemoteOperation?.()) {\n return;\n }\n\n flushPendingDebounce();\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n }\n\n function recordDebounced(blockId: string): void {\n if (isRemoteOperation?.()) {\n return;\n }\n\n if (pendingDebounce && pendingDebounce.blockId === blockId) {\n clearTimeout(pendingDebounce.timeoutId);\n pendingDebounce.timeoutId = setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS);\n return;\n }\n\n flushPendingDebounce();\n\n pushToUndoStack(cloneContent());\n redoStack.value = [];\n\n pendingDebounce = {\n blockId,\n timeoutId: setTimeout(() => {\n pendingDebounce = null;\n }, DEBOUNCE_MS),\n };\n }\n\n function setNavigating(): void {\n isNavigating.value = true;\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n }\n navigatingTimeoutId = setTimeout(() => {\n isNavigating.value = false;\n navigatingTimeoutId = null;\n }, NAVIGATE_IDLE_MS);\n }\n\n function undo(): void {\n if (undoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = undoStack.value.pop()!;\n redoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function redo(): void {\n if (redoStack.value.length === 0) {\n return;\n }\n\n flushPendingDebounce();\n\n const snapshot = redoStack.value.pop()!;\n undoStack.value.push(cloneContent());\n setContent(snapshot, true);\n setNavigating();\n }\n\n function clear(): void {\n undoStack.value = [];\n redoStack.value = [];\n flushPendingDebounce();\n }\n\n function destroy(): void {\n clear();\n if (navigatingTimeoutId) {\n clearTimeout(navigatingTimeoutId);\n navigatingTimeoutId = null;\n }\n }\n\n return {\n canUndo,\n canRedo,\n isNavigating,\n undo,\n redo,\n record,\n recordDebounced,\n clear,\n destroy,\n };\n}\n","import type { Block, BlockDefaults, BlockType } from \"@templatical/types\";\nimport { createBlock, generateId } from \"@templatical/types\";\n\nfunction regenerateNestedIds(block: Block): void {\n if (block.type === \"table\") {\n block.rows = block.rows.map((row) => ({\n ...row,\n id: generateId(),\n cells: row.cells.map((cell) => ({ ...cell, id: generateId() })),\n }));\n } else if (block.type === \"social\") {\n block.icons = block.icons.map((icon) => ({ ...icon, id: generateId() }));\n } else if (block.type === \"menu\") {\n block.items = block.items.map((item) => ({ ...item, id: generateId() }));\n }\n}\n\nexport interface UseBlockActionsOptions {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n selectBlock: (blockId: string | null) => void;\n /** Locate a block in the tree — used by `duplicateBlock` to insert the\n * clone right after the source instead of appending to the end. */\n findBlockLocation?: (blockId: string) => {\n targetSectionId?: string;\n columnIndex?: number;\n index: number;\n } | null;\n /**\n * Defaults merged onto every block `createAndAddBlock` creates.\n *\n * A **getter** is re-read on each insert; a plain object is captured once.\n * The editor passes a getter because half of these defaults track the\n * template's own `settings.locale`, which the author can change mid-session\n * — a snapshot would pin every later insert to the value at mount.\n */\n blockDefaults?: BlockDefaults | (() => BlockDefaults);\n}\n\nexport interface UseBlockActionsReturn {\n createAndAddBlock: (\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n duplicateBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ) => Block;\n deleteBlock: (blockId: string) => void;\n updateBlockProperty: <K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ) => void;\n}\n\nexport function useBlockActions(\n options: UseBlockActionsOptions,\n): UseBlockActionsReturn {\n const { addBlock, removeBlock, updateBlock, selectBlock, findBlockLocation } =\n options;\n\n function createAndAddBlock(\n type: BlockType,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const defaults =\n typeof options.blockDefaults === \"function\"\n ? options.blockDefaults()\n : options.blockDefaults;\n const block = createBlock(type, defaults);\n addBlock(block, targetSectionId, columnIndex);\n selectBlock(block.id);\n return block;\n }\n\n function duplicateBlock(\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n ): Block {\n const cloned = JSON.parse(JSON.stringify(block)) as Block;\n cloned.id = generateId();\n regenerateNestedIds(cloned);\n\n if (cloned.type === \"section\") {\n cloned.children = cloned.children.map((column) =>\n column.map((child) => {\n const clonedChild = JSON.parse(JSON.stringify(child)) as Block;\n clonedChild.id = generateId();\n regenerateNestedIds(clonedChild);\n return clonedChild;\n }),\n );\n }\n\n // Insert directly after the source block. Explicit target args win;\n // otherwise, resolve the source's location and bump index by 1. Falls\n // back to appending at the end if location is unknown.\n if (targetSectionId !== undefined || columnIndex !== undefined) {\n addBlock(cloned, targetSectionId, columnIndex);\n } else {\n const sourceLocation = findBlockLocation?.(block.id) ?? null;\n if (sourceLocation) {\n addBlock(\n cloned,\n sourceLocation.targetSectionId,\n sourceLocation.columnIndex,\n sourceLocation.index + 1,\n );\n } else {\n addBlock(cloned, targetSectionId, columnIndex);\n }\n }\n selectBlock(cloned.id);\n return cloned;\n }\n\n function deleteBlock(blockId: string): void {\n removeBlock(blockId);\n }\n\n function updateBlockProperty<K extends keyof Block>(\n blockId: string,\n key: K,\n value: Block[K],\n ): void {\n updateBlock(blockId, { [key]: value } as Partial<Block>);\n }\n\n return {\n createAndAddBlock,\n duplicateBlock,\n deleteBlock,\n updateBlockProperty,\n };\n}\n","import type { TemplateContent } from \"@templatical/types\";\nimport { watch, type Ref } from \"@vue/reactivity\";\n\nexport interface UseAutoSaveOptions {\n content: Ref<TemplateContent>;\n isDirty: () => boolean;\n onChange: (content: TemplateContent) => void;\n debounce?: number;\n enabled?: boolean | (() => boolean);\n}\n\nexport interface UseAutoSaveReturn {\n flush: () => void;\n cancel: () => void;\n pause: () => void;\n resume: () => void;\n destroy: () => void;\n}\n\n/**\n * Trailing debounce, in ms, measured from the *last* content mutation.\n *\n * Typing is not debounced upstream — TipTap's `onUpdate` calls `updateBlock` per\n * keystroke — so this is the only thing between a keypress and a whole-document\n * write. 1000 was too eager: ordinary prose pauses for a second constantly\n * (word choice, re-reading, reaching for the mouse), so a single paragraph could\n * produce dozens of full-content saves. 2000 roughly halves that while still\n * landing well before a user wonders whether their work was kept.\n *\n * **The single default for both entry points.** Cloud imports this constant\n * rather than declaring its own — a second copy for one setting drifts silently,\n * since nothing links the two.\n */\nexport const DEFAULT_AUTO_SAVE_DEBOUNCE_MS = 2000;\n\nexport function useAutoSave(options: UseAutoSaveOptions): UseAutoSaveReturn {\n const {\n content,\n isDirty,\n onChange,\n debounce = DEFAULT_AUTO_SAVE_DEBOUNCE_MS,\n enabled = true,\n } = options;\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let paused = false;\n\n function isEnabled(): boolean {\n return typeof enabled === \"function\" ? enabled() : enabled;\n }\n\n function pause(): void {\n paused = true;\n cancel();\n }\n\n function resume(): void {\n paused = false;\n }\n\n function cancel(): void {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n\n function flush(): void {\n cancel();\n if (isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }\n\n function scheduleOnChange(): void {\n if (!isEnabled() || paused) return;\n\n cancel();\n timeoutId = setTimeout(() => {\n timeoutId = null;\n if (isEnabled() && !paused && isDirty()) {\n onChange(JSON.parse(JSON.stringify(content.value)));\n }\n }, debounce);\n }\n\n // `watch` from `@vue/reactivity` has no scheduler, so this callback runs\n // *synchronously inside the mutation* — before the editor's trailing\n // `state.isDirty = true`, and `isDirty` sits outside the watched `content`\n // subtree so setting it never re-triggers. An `isDirty()` guard here would\n // therefore drop the first edit after every dirty-flag reset (issue #522).\n // Dirtiness is decided at debounce time instead, where the flag is settled;\n // `scheduleOnChange` still checks `enabled`/`paused` up front.\n const stopWatch = watch(\n content,\n () => {\n scheduleOnChange();\n },\n { deep: true },\n );\n\n function destroy(): void {\n stopWatch();\n cancel();\n }\n\n return {\n flush,\n cancel,\n pause,\n resume,\n destroy,\n };\n}\n","import { computed, reactive, type ComputedRef } from \"@vue/reactivity\";\n\nexport interface UseConditionPreviewReturn {\n isHidden: (blockId: string) => boolean;\n toggleBlock: (blockId: string) => void;\n reset: () => void;\n hasHiddenBlocks: ComputedRef<boolean>;\n}\n\n/**\n * The slice of an editor this needs — deliberately structural, so both the OSS\n * and the Cloud `useEditor` satisfy it without either having to grow toward the\n * other.\n */\nexport interface ConditionPreviewEditor {\n state: { readonly selectedBlockId: string | null };\n selectBlock: (blockId: string | null) => void;\n}\n\nexport function useConditionPreview(\n editor: ConditionPreviewEditor,\n): UseConditionPreviewReturn {\n const hiddenBlockIds = reactive(new Set<string>());\n\n const hasHiddenBlocks = computed(() => hiddenBlockIds.size > 0);\n\n function isHidden(blockId: string): boolean {\n return hiddenBlockIds.has(blockId);\n }\n\n function toggleBlock(blockId: string): void {\n if (hiddenBlockIds.has(blockId)) {\n hiddenBlockIds.delete(blockId);\n } else {\n hiddenBlockIds.add(blockId);\n\n if (editor.state.selectedBlockId === blockId) {\n editor.selectBlock(null);\n }\n }\n }\n\n function reset(): void {\n hiddenBlockIds.clear();\n }\n\n return {\n isHidden,\n toggleBlock,\n reset,\n hasHiddenBlocks,\n };\n}\n","import type { CustomBlock, CustomBlockDefinition } from \"@templatical/types\";\nimport type { ComputedRef, Ref } from \"@vue/reactivity\";\nimport { computed, ref } from \"@vue/reactivity\";\n\nexport function useDataSourceFetch(options: {\n definition: ComputedRef<CustomBlockDefinition | undefined>;\n block: ComputedRef<CustomBlock>;\n onUpdate: (fieldValues: Record<string, unknown>, fetched: boolean) => void;\n}): {\n isFetching: Ref<boolean>;\n fetchError: Ref<boolean>;\n fetch: () => Promise<void>;\n hasDataSource: ComputedRef<boolean>;\n needsFetch: ComputedRef<boolean>;\n} {\n const isFetching = ref(false);\n const fetchError = ref(false);\n\n const hasDataSource = computed(() => !!options.definition.value?.dataSource);\n\n const needsFetch = computed(\n () => hasDataSource.value && !options.block.value.dataSourceFetched,\n );\n\n async function fetch(): Promise<void> {\n const def = options.definition.value;\n if (!def?.dataSource) {\n return;\n }\n\n isFetching.value = true;\n fetchError.value = false;\n\n try {\n const result = await def.dataSource.onFetch({\n fieldValues: { ...options.block.value.fieldValues },\n blockId: options.block.value.id,\n });\n\n if (result == null) {\n return;\n }\n\n const merged = { ...options.block.value.fieldValues };\n for (const key of Object.keys(merged)) {\n if (key in result) {\n merged[key] = result[key];\n }\n }\n\n options.onUpdate(merged, true);\n } catch (error) {\n console.warn(\"[Templatical] Data source fetch error:\", error);\n fetchError.value = true;\n } finally {\n isFetching.value = false;\n }\n }\n\n return {\n isFetching,\n fetchError,\n fetch,\n hasDataSource,\n needsFetch,\n };\n}\n","import type { Block, TemplateSettings } from \"@templatical/types\";\nimport type { UseHistoryReturn } from \"./history\";\n\n/**\n * The mutable slice of an editor this wraps — structural, so the OSS and Cloud\n * `useEditor` returns both satisfy it without either having to grow toward the\n * other. Not `Readonly`: the whole point is to replace these members in place.\n */\nexport interface HistoryInterceptorEditor {\n addBlock: (\n block: Block,\n targetSectionId?: string,\n columnIndex?: number,\n index?: number,\n ) => void;\n removeBlock: (blockId: string) => void;\n moveBlock: (\n blockId: string,\n newIndex: number,\n targetSectionId?: string,\n columnIndex?: number,\n ) => void;\n updateBlock: (blockId: string, updates: Partial<Block>) => void;\n updateSettings: (updates: Partial<TemplateSettings>) => void;\n isBlockLocked: (blockId: string) => boolean;\n}\n\n/**\n * Wraps editor mutation methods to record history snapshots before each\n * operation. Mutates the editor object in place.\n *\n * Must be applied **after** any collaboration broadcast wrapping so the\n * call chain is: history.record() → broadcast → original mutation.\n */\nexport function useHistoryInterceptor(\n editor: HistoryInterceptorEditor,\n history: UseHistoryReturn,\n): void {\n const originalAddBlock = editor.addBlock;\n const originalRemoveBlock = editor.removeBlock;\n const originalMoveBlock = editor.moveBlock;\n const originalUpdateBlock = editor.updateBlock;\n const originalUpdateSettings = editor.updateSettings;\n\n // Skip recording when the underlying op is a no-op (e.g., a peer-locked\n // block or section), otherwise the undo stack fills with snapshots that\n // are identical to current state and undo silently does nothing.\n editor.addBlock = (block, targetSectionId?, columnIndex?, index?) => {\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalAddBlock(block, targetSectionId, columnIndex, index);\n };\n\n editor.removeBlock = (blockId) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.record();\n originalRemoveBlock(blockId);\n };\n\n editor.moveBlock = (blockId, newIndex, targetSectionId?, columnIndex?) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n if (targetSectionId && editor.isBlockLocked(targetSectionId)) {\n return;\n }\n history.record();\n originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);\n };\n\n editor.updateBlock = (blockId, updates) => {\n if (editor.isBlockLocked(blockId)) {\n return;\n }\n history.recordDebounced(blockId);\n originalUpdateBlock(blockId, updates);\n };\n\n editor.updateSettings = (updates) => {\n history.record();\n originalUpdateSettings(updates);\n };\n}\n","import type {\n Block,\n SavedBlock,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseSavedBlocksOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ savedBlocks })`, or\n * by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: SavedBlocksProvider;\n onError?: (error: Error) => void;\n}\n\nexport interface UseSavedBlocksReturn {\n /** In-memory mirror of the provider's list, kept in sync after each call. */\n savedBlocks: Ref<SavedBlock[]>;\n isLoading: Ref<boolean>;\n /**\n * Distinct categories across the loaded entries, alphabetically sorted.\n *\n * Derived rather than stored: a category exists exactly as long as an entry\n * carries it. Sorting here is not a violation of \"the editor never re-sorts\"\n * — that rule protects the provider's ordering of *entries*, whereas these\n * are strings with no given order.\n */\n categories: ComputedRef<string[]>;\n /**\n * Whether the provider supplied each mutation at all — `false` on the\n * provider means the capability does not exist for this user, and the UI\n * hides the corresponding affordance entirely.\n *\n * Constant for the provider's lifetime, but exposed as computed refs so\n * consumers can bind them the same way as everything else here.\n */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n /**\n * Whether a *particular* entry may be edited / deleted: the capability must\n * exist AND the entry must not opt out via `canUpdate` / `canDelete`.\n *\n * Use these rather than reading the flags directly, so every surface asks the\n * question the same way and the \"absent means allowed\" default lives in one\n * place.\n */\n canUpdateBlock: (block: SavedBlock) => boolean;\n canDeleteBlock: (block: SavedBlock) => boolean;\n /**\n * Re-read from the provider. The editor always calls this bare and filters\n * in memory; `params` exists for headless callers that want the provider to\n * filter instead (see {@link SavedBlocksListParams}).\n */\n load: (params?: SavedBlocksListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n name: string,\n content: Block[],\n category?: string,\n ) => Promise<SavedBlock>;\n /** Rejects when `update` is disabled or the entry opts out of editing. */\n update: (id: string, patch: SavedBlockPatch) => Promise<SavedBlock>;\n /** Rejects when `delete` is disabled or the entry opts out of deletion. */\n remove: (id: string) => Promise<void>;\n}\n\n/**\n * Reactive state over a {@link SavedBlocksProvider}.\n *\n * Owns the list, the loading flag, and the local mutations that keep the list\n * consistent after each successful call, so a provider only has to implement\n * four promise-returning methods. Errors are reported through `onError` and\n * re-thrown, leaving the local list untouched on failure.\n */\nexport function useSavedBlocks(\n options: UseSavedBlocksOptions,\n): UseSavedBlocksReturn {\n const { provider } = options;\n\n const savedBlocks = ref<SavedBlock[]>([]);\n const isLoading = ref(false);\n\n const categories = computed(() => {\n const seen = new Set<string>();\n for (const block of savedBlocks.value) {\n const category = block.category?.trim();\n if (category) seen.add(category);\n }\n return [...seen].sort((a, b) => a.localeCompare(b));\n });\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n\n /** Absent flag means allowed — the flags exist only to forbid. */\n function canUpdateBlock(block: SavedBlock): boolean {\n return canUpdate.value && block.canUpdate !== false;\n }\n\n function canDeleteBlock(block: SavedBlock): boolean {\n return canDelete.value && block.canDelete !== false;\n }\n\n /**\n * The UI hides disabled actions, so reaching one of these means a programmatic\n * caller went around it. Fail loudly rather than silently no-op: a resolved\n * promise would read as \"saved\" to whoever awaited it.\n */\n function refuse(action: string, reason: string): never {\n throw new SdkError(\n `[Templatical] Saved blocks: ${action} is ${reason}. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a saved block that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: SavedBlocksListParams): Promise<void> {\n isLoading.value = true;\n try {\n savedBlocks.value = await provider.list(params);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(\n name: string,\n content: Block[],\n category?: string,\n ): Promise<SavedBlock> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") {\n refuse(\"create\", \"disabled by the provider\");\n }\n try {\n // Omit the key entirely when unset, rather than sending `undefined` —\n // a provider serialising the input to JSON would otherwise be handed an\n // explicit null-ish field it never asked for.\n const created = await providerCreate(\n category ? { name, content, category } : { name, content },\n );\n savedBlocks.value = [created, ...savedBlocks.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function update(\n id: string,\n patch: SavedBlockPatch,\n ): Promise<SavedBlock> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") {\n refuse(\"update\", \"disabled by the provider\");\n }\n // An id absent from the local list isn't refused — a headless caller may\n // legitimately patch something it never loaded. Only a loaded entry that\n // explicitly opted out is blocked.\n const local = savedBlocks.value.find((b) => b.id === id);\n if (local && local.canUpdate === false) {\n refuse(\"update\", `not permitted for entry \"${id}\"`);\n }\n try {\n const updated = await providerUpdate(id, patch);\n savedBlocks.value = savedBlocks.value.map((b) =>\n b.id === id ? updated : b,\n );\n notify(() => provider.onUpdated?.(updated));\n return updated;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function remove(id: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") {\n refuse(\"delete\", \"disabled by the provider\");\n }\n // Captured before the delete resolves: `onDeleted` carries the removed\n // entry itself, and there is nothing left to read once the list is\n // filtered.\n const removed = savedBlocks.value.find((b) => b.id === id) ?? null;\n if (removed && removed.canDelete === false) {\n refuse(\"delete\", `not permitted for entry \"${id}\"`);\n }\n try {\n await providerDelete(id);\n savedBlocks.value = savedBlocks.value.filter((b) => b.id !== id);\n if (removed) {\n notify(() => provider.onDeleted?.(removed));\n }\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n return {\n savedBlocks,\n isLoading,\n categories,\n canCreate,\n canUpdate,\n canDelete,\n canUpdateBlock,\n canDeleteBlock,\n load,\n create,\n update,\n remove,\n };\n}\n","import type {\n SavedBlock,\n SavedBlockInput,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageSavedBlocksOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:saved-blocks\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:saved-blocks\";\n\n/**\n * Browser-local {@link SavedBlocksProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ savedBlocks: createLocalStorageSavedBlocksProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep the\n * feature — and its UI — entirely off.\n *\n * Intended for demos, prototypes, and single-device use. Entries live in one\n * browser profile only: they don't sync across devices or users, and clearing\n * site data removes them. Back saved blocks with your own API for anything\n * that needs to outlive a browser profile.\n */\nexport function createLocalStorageSavedBlocksProvider(\n options: LocalStorageSavedBlocksOptions = {},\n): SavedBlocksProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageSavedBlocksProvider requires a browser environment with localStorage. Supply your own SavedBlocksProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): SavedBlock[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no saved blocks\".\n return Array.isArray(parsed) ? (parsed as SavedBlock[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(blocks: SavedBlock[]): void {\n getStorage().setItem(storageKey, JSON.stringify(blocks));\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n // The editor calls this bare and filters in memory; the params are honored\n // for headless callers that would rather the provider did the work.\n async list(params?: SavedBlocksListParams): Promise<SavedBlock[]> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const category = params?.category?.trim();\n return all.filter((b) => {\n if (search && !b.name.toLowerCase().includes(search)) return false;\n if (category && b.category !== category) return false;\n return true;\n });\n },\n\n // All three mutations are enabled: a browser-local store has no notion of\n // permissions, so there is nothing to forbid.\n async create(input: SavedBlockInput): Promise<SavedBlock> {\n const now = new Date().toISOString();\n const created: SavedBlock = {\n id: generateId(),\n name: input.name,\n content: input.content,\n ...(input.category ? { category: input.category } : {}),\n createdAt: now,\n updatedAt: now,\n };\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: SavedBlockPatch): Promise<SavedBlock> {\n const all = readAll();\n const index = all.findIndex((b) => b.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n const updated: SavedBlock = {\n ...all[index],\n ...patch,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(id: string): Promise<void> {\n const all = readAll();\n if (!all.some((b) => b.id === id)) {\n throw new Error(`[Templatical] Saved block not found: ${id}`);\n }\n writeAll(all.filter((b) => b.id !== id));\n },\n };\n}\n","import type {\n MediaAsset,\n MediaAssetPatch,\n MediaCreateInput,\n MediaListPage,\n MediaListParams,\n MediaProvider,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\n\nexport interface LocalStorageMediaProviderOptions {\n /**\n * `localStorage` key holding the serialized array.\n *\n * @default \"templatical:media\"\n */\n key?: string;\n}\n\nconst DEFAULT_KEY = \"templatical:media\";\nconst PAGE_SIZE = 50;\n\n/**\n * Browser-local {@link MediaProvider} backed by `localStorage`.\n *\n * Opt-in: pass it explicitly as `init({ media: createLocalStorageMediaProvider() })`.\n * The editor never falls back to it, so consumers without a provider keep\n * image fields URL-only.\n *\n * `create` stores the file as a data URL. That is a demo-sized persistence\n * model — `localStorage` quotas are typically ~5 MB, and a few large\n * images will fill it. Back a real gallery with your own API.\n *\n * Folders, replace, import, usage, frequently-used and quota are `false`:\n * this adapter is a flat list of assets.\n */\nexport function createLocalStorageMediaProvider(\n options: LocalStorageMediaProviderOptions = {},\n): MediaProvider {\n const storageKey = options.key ?? DEFAULT_KEY;\n\n function getStorage(): Storage {\n // Core targets a neutral platform, so `localStorage` isn't guaranteed —\n // fail with an actionable message rather than a bare ReferenceError.\n if (typeof localStorage === \"undefined\") {\n throw new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with localStorage. Supply your own MediaProvider for server-side or non-browser use.\",\n );\n }\n return localStorage;\n }\n\n function readAll(): MediaAsset[] {\n const raw = getStorage().getItem(storageKey);\n if (!raw) return [];\n try {\n const parsed: unknown = JSON.parse(raw);\n // Tolerate a corrupted or hand-edited value rather than breaking the\n // editor: treat anything that isn't an array as \"no assets\".\n return Array.isArray(parsed) ? (parsed as MediaAsset[]) : [];\n } catch {\n return [];\n }\n }\n\n function writeAll(assets: MediaAsset[]): void {\n getStorage().setItem(storageKey, JSON.stringify(assets));\n }\n\n function readFileAsDataURL(file: File): Promise<string> {\n if (typeof FileReader === \"undefined\") {\n return Promise.reject(\n new Error(\n \"[Templatical] createLocalStorageMediaProvider requires a browser environment with FileReader.\",\n ),\n );\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result !== \"string\") {\n reject(new Error(\"[Templatical] Failed to read file as a data URL.\"));\n return;\n }\n resolve(reader.result);\n };\n reader.onerror = () => {\n reject(\n reader.error ??\n new Error(\"[Templatical] Failed to read file as a data URL.\"),\n );\n };\n reader.readAsDataURL(file);\n });\n }\n\n // Every method is `async` so a synchronous failure (a missing\n // `localStorage`, a quota error from `setItem`) surfaces as a rejected\n // promise rather than a sync throw — callers of a `Promise`-returning\n // contract must be able to rely on `.catch()`.\n return {\n async list(params?: MediaListParams): Promise<MediaListPage> {\n const all = readAll();\n const search = params?.search?.trim().toLowerCase();\n const filtered = search\n ? all.filter((asset) => {\n const filename = asset.filename?.toLowerCase() ?? \"\";\n const alt = asset.alt?.toLowerCase() ?? \"\";\n return filename.includes(search) || alt.includes(search);\n })\n : all;\n const parsed = params?.cursor ? Number.parseInt(params.cursor, 10) : 0;\n const offset = Number.isFinite(parsed) && parsed > 0 ? parsed : 0;\n const items = filtered.slice(offset, offset + PAGE_SIZE);\n const page: MediaListPage = { items };\n const nextOffset = offset + items.length;\n if (nextOffset < filtered.length) {\n page.nextCursor = String(nextOffset);\n }\n return page;\n },\n\n async create(input: MediaCreateInput): Promise<MediaAsset> {\n const url = await readFileAsDataURL(input.file);\n const now = new Date().toISOString();\n const created: MediaAsset = {\n id: generateId(),\n url,\n filename: input.filename || input.file.name,\n createdAt: now,\n updatedAt: now,\n size: input.file.size,\n };\n if (input.alt !== undefined) created.alt = input.alt;\n if (input.file.type) created.mimeType = input.file.type;\n if (input.folderId !== undefined) created.folderId = input.folderId;\n // Newest-first, matching the order the composable applies locally after\n // a create — so a reload preserves what the user just saw.\n writeAll([created, ...readAll()]);\n return created;\n },\n\n async update(id: string, patch: MediaAssetPatch): Promise<MediaAsset> {\n const all = readAll();\n const index = all.findIndex((asset) => asset.id === id);\n // Mirrors a REST 404 so provider behavior is consistent across adapters.\n if (index === -1) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n const current = all[index];\n const updated: MediaAsset = {\n ...current,\n ...patch,\n id: current.id,\n url: current.url,\n updatedAt: new Date().toISOString(),\n };\n const next = [...all];\n next[index] = updated;\n writeAll(next);\n return updated;\n },\n\n async delete(ids: string[]): Promise<void> {\n const all = readAll();\n for (const id of ids) {\n if (!all.some((asset) => asset.id === id)) {\n throw new Error(`[Templatical] Media asset not found: ${id}`);\n }\n }\n const remove = new Set(ids);\n writeAll(all.filter((asset) => !remove.has(asset.id)));\n },\n\n folders: false,\n replace: false,\n importFromUrl: false,\n checkUsage: false,\n frequentlyUsed: false,\n storage: false,\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplateVersion,\n VersionHistoryListParams,\n VersionHistoryProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { notifyHandler } from \"./error-reporting\";\n\nexport interface UseVersionHistoryOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ versionHistory })`,\n * or by Cloud's own adapter — this composable is transport-agnostic and never\n * talks to a network itself.\n */\n provider: VersionHistoryProvider;\n /**\n * Which template's history this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseVersionHistoryReturn {\n /** The provider's list, in the provider's order. Never re-sorted. */\n versions: Ref<TemplateVersion[]>;\n isLoading: Ref<boolean>;\n isRestoring: Ref<boolean>;\n /**\n * Cursor for the page after the one currently held, or `undefined` when the\n * provider signalled there is no more. The editor loads one page and ignores\n * this; it is here so a headless caller can page without reaching past the\n * composable.\n */\n nextCursor: Ref<string | undefined>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canRestore: ComputedRef<boolean>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: VersionHistoryListParams) => Promise<void>;\n /**\n * The version's content **if it is already in hand** — the provider's\n * `content` hint, or a previous `get` this composable cached. `null` means a\n * round-trip is required.\n *\n * Callers that must not block (scrubbing through history swaps the canvas in\n * the same tick) check this first and only await {@link resolveContent} when\n * it comes back null.\n */\n peekContent: (version: TemplateVersion) => TemplateContent | null;\n /**\n * `version.content ?? await provider.get(...)`, cached per version id, so a\n * lazily-loaded version costs one round-trip on its first visit and nothing\n * afterwards.\n */\n resolveContent: (version: TemplateVersion) => Promise<TemplateContent>;\n /** Rejects with an {@link SdkError} when the provider disabled `create`. */\n create: (\n content: TemplateContent,\n meta?: { label?: string },\n ) => Promise<TemplateVersion>;\n /** Rejects with an {@link SdkError} when the provider disabled `restore`. */\n restore: (versionId: string) => Promise<Template>;\n}\n\n/**\n * Reactive state over a {@link VersionHistoryProvider}.\n *\n * Owns the list, the loading flags and the per-version content cache that keeps\n * scrubbing synchronous. Errors are reported through `onError` and re-thrown,\n * leaving the list untouched on failure.\n *\n * It deliberately does **not** create versions of its own accord. The editor\n * never records a version on a save — whoever implements `TemplatesProvider.save`\n * decides that, because they are the side that pays for the storage.\n */\nexport function useVersionHistory(\n options: UseVersionHistoryOptions,\n): UseVersionHistoryReturn {\n const { provider, getTemplateId } = options;\n\n const versions = ref<TemplateVersion[]>([]);\n const isLoading = ref(false);\n const nextCursor = ref<string | undefined>(undefined);\n const isRestoring = ref(false);\n\n /**\n * Content fetched through `get`, keyed by version id. A version that carried\n * a `content` hint never lands here — the hint is read straight off the entry,\n * so a provider that eager-loads pays no second copy.\n */\n const fetched = new Map<string, TemplateContent>();\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canRestore = computed(() => typeof provider.restore === \"function\");\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op: a resolved promise reads as \"done\".\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Version history: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Version history: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a version that was recorded or restored.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n async function load(params?: VersionHistoryListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n const page = await provider.list(templateId, params);\n versions.value = page.versions;\n nextCursor.value = page.nextCursor;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isLoading.value = false;\n }\n }\n\n function peekContent(version: TemplateVersion): TemplateContent | null {\n return version.content ?? fetched.get(version.id) ?? null;\n }\n\n async function resolveContent(\n version: TemplateVersion,\n ): Promise<TemplateContent> {\n const known = peekContent(version);\n if (known) return known;\n\n const templateId = requireTemplateId(\"get\");\n try {\n const content = await provider.get(templateId, version.id);\n fetched.set(version.id, content);\n return content;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function create(\n content: TemplateContent,\n meta?: { label?: string },\n ): Promise<TemplateVersion> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n try {\n const created = await providerCreate(templateId, content, meta);\n versions.value = [created, ...versions.value];\n notify(() => provider.onCreated?.(created));\n return created;\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n }\n }\n\n async function restore(versionId: string): Promise<Template> {\n const { restore: providerRestore } = provider;\n if (typeof providerRestore !== \"function\") refuse(\"restore\");\n const templateId = requireTemplateId(\"restore\");\n\n let restored: Template;\n isRestoring.value = true;\n try {\n restored = await providerRestore(templateId, versionId);\n } catch (error) {\n options.onError?.(error as Error);\n throw error;\n } finally {\n isRestoring.value = false;\n }\n // Below the `finally` on purpose: `isRestoring` is cleared there, so a\n // handler must not observe the composable still reporting itself\n // mid-restore. Unreachable on failure — the catch rethrows.\n notify(() => provider.onRestored?.(restored));\n return restored;\n }\n\n return {\n nextCursor,\n versions,\n isLoading,\n isRestoring,\n canCreate,\n canRestore,\n load,\n peekContent,\n resolveContent,\n create,\n restore,\n };\n}\n","import type {\n Comment,\n CommentAuthor,\n CommentChange,\n CommentInput,\n CommentPatch,\n CommentsListParams,\n CommentsProvider,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport {\n computed,\n onScopeDispose,\n ref,\n watch,\n type ComputedRef,\n type Ref,\n} from \"vue\";\nimport { notifyHandler, wrapReportedError } from \"./error-reporting\";\n\nexport interface UseCommentsOptions {\n /**\n * Storage backend. Supplied by the consumer via `init({ comments })`, or by\n * Cloud's own adapter — this composable is transport-agnostic and never talks\n * to a network itself.\n */\n provider: CommentsProvider;\n /**\n * Which template's conversation this is. A getter rather than a value because a\n * session can outlive the template: `initCloud()` constructs the feature at\n * setup and only learns the id once `create()` / `load()` resolves.\n */\n getTemplateId: () => string | null;\n /**\n * Who is commenting, read at call time. `null` means the editor has no\n * identity, and every mutation refuses — an unattributable comment is worse\n * than no comment feature.\n */\n getUser: () => CommentAuthor | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseCommentsReturn {\n /** Thread roots in the provider's order. Never re-sorted. */\n comments: Ref<Comment[]>;\n isLoading: Ref<boolean>;\n isSubmitting: Ref<boolean>;\n /** Whether the provider supplied each mutation at all. */\n canCreate: ComputedRef<boolean>;\n canUpdate: ComputedRef<boolean>;\n canDelete: ComputedRef<boolean>;\n canResolve: ComputedRef<boolean>;\n /** Roots plus replies. */\n totalCount: ComputedRef<number>;\n /** Unresolved roots — the badge on the trigger. */\n unresolvedCount: ComputedRef<number>;\n /** Comments per anchored block id, roots plus their replies. */\n commentCountByBlock: ComputedRef<Map<string, number>>;\n /** Re-read the list. The editor calls this bare; `params` is for headless callers. */\n load: (params?: CommentsListParams) => Promise<void>;\n /** Rejects with an {@link SdkError} when `create` is disabled or there is no user. */\n create: (input: CommentInput) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `update` is disabled or there is no user. */\n update: (commentId: string, patch: CommentPatch) => Promise<Comment>;\n /** Rejects with an {@link SdkError} when `delete` is disabled or there is no user. */\n remove: (commentId: string) => Promise<void>;\n /** Rejects with an {@link SdkError} when `setResolved` is disabled or there is no user. */\n setResolved: (commentId: string, resolved: boolean) => Promise<Comment>;\n /** Look one up by id, root or reply. `null` when it isn't loaded. */\n find: (commentId: string) => Comment | null;\n /** Whether this comment was written by the current user. */\n isOwn: (comment: Comment) => boolean;\n /**\n * Apply a change that arrived from {@link CommentsProvider.subscribe}. Public\n * so a consumer driving this headlessly can push their own transport into it.\n */\n applyRemoteCreate: (comment: Comment) => void;\n applyRemoteUpdate: (comment: Comment) => void;\n applyRemoteDelete: (commentId: string, parentId?: string | null) => void;\n}\n\n/**\n * Reactive state over a {@link CommentsProvider}.\n *\n * Owns the thread list, the loading flags, the local mutations that keep the list\n * consistent, and the derived counts the chrome renders. Errors are reported\n * through `onError` and **re-thrown**, leaving the list untouched on failure —\n * the same discipline as `useSavedBlocks` and `useVersionHistory`.\n *\n * Mutations **reject** when the provider withheld them rather than resolving to\n * `null`: a resolved promise reads as \"saved\" to whoever awaited it. Ask through\n * `canCreate` / `canUpdate` / `canDelete` / `canResolve` first — which is what the\n * editor's own UI does, hiding each action rather than disabling it.\n */\nexport function useComments(options: UseCommentsOptions): UseCommentsReturn {\n const { provider, getTemplateId, getUser } = options;\n\n const comments = ref<Comment[]>([]);\n const isLoading = ref(false);\n const isSubmitting = ref(false);\n\n const canCreate = computed(() => typeof provider.create === \"function\");\n const canUpdate = computed(() => typeof provider.update === \"function\");\n const canDelete = computed(() => typeof provider.delete === \"function\");\n const canResolve = computed(() => typeof provider.setResolved === \"function\");\n\n const totalCount = computed(() =>\n comments.value.reduce(\n (sum, thread) => sum + 1 + (thread.replies?.length ?? 0),\n 0,\n ),\n );\n\n const unresolvedCount = computed(\n () => comments.value.filter((thread) => !thread.resolvedAt).length,\n );\n\n const commentCountByBlock = computed(() => {\n const map = new Map<string, number>();\n for (const thread of comments.value) {\n if (!thread.blockId) continue;\n map.set(\n thread.blockId,\n (map.get(thread.blockId) ?? 0) + 1 + (thread.replies?.length ?? 0),\n );\n }\n return map;\n });\n\n /**\n * The UI hides disabled actions and renders nothing before a template exists,\n * so reaching one of these means a programmatic caller went around it. Fail\n * loudly rather than silently no-op.\n */\n function requireTemplateId(action: string): string {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs a template. Call create() or load() first.`,\n );\n }\n return templateId;\n }\n\n function requireUser(action: string): CommentAuthor {\n const user = getUser();\n if (!user) {\n throw new SdkError(\n `[Templatical] Comments: ${action} needs to know who is commenting. Pass init({ user: { id, name } }) — an unattributed comment is not written.`,\n );\n }\n return user;\n }\n\n function refuse(action: string): never {\n throw new SdkError(\n `[Templatical] Comments: ${action} is disabled by the provider. Check the capability before calling — the editor's own UI hides the action.`,\n );\n }\n\n function find(commentId: string): Comment | null {\n for (const thread of comments.value) {\n if (thread.id === commentId) return thread;\n for (const reply of thread.replies ?? []) {\n if (reply.id === commentId) return reply;\n }\n }\n return null;\n }\n\n function isOwn(comment: Comment): boolean {\n const user = getUser();\n return user !== null && comment.author.id === user.id;\n }\n\n function report(error: unknown): never {\n const wrapped = wrapReportedError(error);\n options.onError?.(wrapped);\n throw wrapped;\n }\n\n /**\n * Run a consumer's event handler without letting it fail the operation.\n *\n * A handler that throws must not turn a completed write into a rejected one —\n * the UI would report a failure for a comment that was created.\n */\n function notify(run: () => void): void {\n notifyHandler(options.onError, run);\n }\n\n function replaceAt<T>(list: T[], index: number, next: T): T[] {\n return [...list.slice(0, index), next, ...list.slice(index + 1)];\n }\n\n /**\n * Key-order sensitive: identical fields serialized in a different order\n * compare unequal, so an echo whose transport frame orders keys\n * differently than the mutation response it echoes would not be\n * suppressed. Accepted rather than a defect — not worth a deep-equal\n * dependency for it.\n */\n function sameComment(a: Comment | undefined, b: Comment): boolean {\n return a !== undefined && JSON.stringify(a) === JSON.stringify(b);\n }\n\n /**\n * Insert a comment, replacing an entry with the same id rather than\n * duplicating it, and report whether the list actually changed.\n *\n * The **remote** apply paths (`applyRemoteCreate` / `applyRemoteUpdate`)\n * emit their event only when this returns `true`. A transport's own\n * broadcast can come back to the sender — `CommentsProvider.subscribe`'s\n * contract permits it — and emitting unconditionally fires the handler\n * twice for one comment. The comparison below runs against the **merged**\n * result, never the raw payload: the root branch keeps already-loaded\n * `replies` when an update payload carries none, so comparing the payload\n * directly would report a change for an echo that altered nothing.\n * Deleting the `sameComment` check as a redundant no-op reinstates the\n * double-fire.\n */\n function upsert(comment: Comment): boolean {\n if (comment.parentId) {\n let changed = false;\n const next = comments.value.map((thread) => {\n if (thread.id !== comment.parentId) return thread;\n const replies = thread.replies ?? [];\n const at = replies.findIndex((reply) => reply.id === comment.id);\n if (at !== -1 && sameComment(replies[at], comment)) return thread;\n changed = true;\n return {\n ...thread,\n replies:\n at === -1 ? [...replies, comment] : replaceAt(replies, at, comment),\n };\n });\n // Assigned only when something actually changed: `.map()` always returns\n // a new array, and reassigning `comments.value` for a no-op echo would\n // still invalidate every computed reading it (`commentCountByBlock`\n // returns a new `Map` on each recompute) for nothing.\n if (changed) comments.value = next;\n return changed;\n }\n\n const at = comments.value.findIndex((thread) => thread.id === comment.id);\n if (at === -1) {\n comments.value = [...comments.value, comment];\n return true;\n }\n // A root arriving again keeps the replies already loaded: the update payload\n // for a body edit carries none, and dropping them would empty the thread.\n const next: Comment = {\n ...comment,\n replies: comment.replies ?? comments.value[at].replies,\n };\n if (sameComment(comments.value[at], next)) return false;\n comments.value = replaceAt(comments.value, at, next);\n return true;\n }\n\n function drop(commentId: string, parentId?: string | null): Comment | null {\n const existing = find(commentId);\n // `parentId` from the caller wins so a delete can be applied without the\n // comment being loaded — the subscribe path, on a thread never opened.\n const parent = parentId ?? existing?.parentId ?? null;\n\n if (parent) {\n comments.value = comments.value.map((thread) =>\n thread.id === parent\n ? {\n ...thread,\n replies: (thread.replies ?? []).filter((r) => r.id !== commentId),\n }\n : thread,\n );\n } else {\n comments.value = comments.value.filter((c) => c.id !== commentId);\n }\n return existing;\n }\n\n async function load(params?: CommentsListParams): Promise<void> {\n const templateId = requireTemplateId(\"list\");\n isLoading.value = true;\n try {\n comments.value = await provider.list(templateId, params);\n } catch (error) {\n report(error);\n } finally {\n isLoading.value = false;\n }\n }\n\n async function create(input: CommentInput): Promise<Comment> {\n const { create: providerCreate } = provider;\n if (typeof providerCreate !== \"function\") refuse(\"create\");\n const templateId = requireTemplateId(\"create\");\n requireUser(\"create\");\n isSubmitting.value = true;\n try {\n const created = await providerCreate(templateId, input);\n upsert(created);\n notify(() => provider.onCreated?.(created, { origin: \"local\" }));\n return created;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function update(\n commentId: string,\n patch: CommentPatch,\n ): Promise<Comment> {\n const { update: providerUpdate } = provider;\n if (typeof providerUpdate !== \"function\") refuse(\"update\");\n const templateId = requireTemplateId(\"update\");\n requireUser(\"update\");\n isSubmitting.value = true;\n try {\n const updated = await providerUpdate(templateId, commentId, patch);\n upsert(updated);\n notify(() => provider.onUpdated?.(updated, { origin: \"local\" }));\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function remove(commentId: string): Promise<void> {\n const { delete: providerDelete } = provider;\n if (typeof providerDelete !== \"function\") refuse(\"delete\");\n const templateId = requireTemplateId(\"delete\");\n requireUser(\"delete\");\n // Snapshotted before the request: the event carries what was deleted, and\n // there is nothing to read afterwards.\n const existing = find(commentId);\n isSubmitting.value = true;\n try {\n await providerDelete(templateId, commentId);\n drop(commentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"local\" }));\n }\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n async function setResolved(\n commentId: string,\n resolved: boolean,\n ): Promise<Comment> {\n const { setResolved: providerSetResolved } = provider;\n if (typeof providerSetResolved !== \"function\") refuse(\"setResolved\");\n const templateId = requireTemplateId(\"setResolved\");\n requireUser(\"setResolved\");\n isSubmitting.value = true;\n try {\n const updated = await providerSetResolved(\n templateId,\n commentId,\n resolved,\n );\n upsert(updated);\n // The stored result decides which event fires, not the requested state: a\n // store may refuse to reopen, and the consumer should hear what happened.\n notify(() =>\n (updated.resolvedAt ? provider.onResolved : provider.onUnresolved)?.(\n updated,\n { origin: \"local\" },\n ),\n );\n return updated;\n } catch (error) {\n report(error);\n } finally {\n isSubmitting.value = false;\n }\n }\n\n function applyRemoteCreate(comment: Comment): void {\n // A reply whose parent thread isn't loaded reports nothing: `upsert`\n // finds no matching thread to attach it to, so the list doesn't change.\n if (upsert(comment)) {\n notify(() => provider.onCreated?.(comment, { origin: \"remote\" }));\n }\n }\n\n function applyRemoteUpdate(comment: Comment): void {\n if (!upsert(comment)) return;\n notify(() =>\n (comment.resolvedAt\n ? provider.onResolved\n : // An unresolve and a body edit are both \"not resolved\"; only a payload\n // that used to be resolved is an unresolve, and the transport doesn't\n // say. `updated` is the honest answer for a plain change.\n provider.onUpdated)?.(comment, { origin: \"remote\" }),\n );\n }\n\n function applyRemoteDelete(\n commentId: string,\n parentId?: string | null,\n ): void {\n const existing = drop(commentId, parentId);\n if (existing) {\n notify(() => provider.onDeleted?.(existing, { origin: \"remote\" }));\n }\n }\n\n return {\n comments,\n isLoading,\n isSubmitting,\n canCreate,\n canUpdate,\n canDelete,\n canResolve,\n totalCount,\n unresolvedCount,\n commentCountByBlock,\n load,\n create,\n update,\n remove,\n setResolved,\n find,\n isOwn,\n applyRemoteCreate,\n applyRemoteUpdate,\n applyRemoteDelete,\n };\n}\n\nexport interface UseCommentListenerOptions {\n comments: UseCommentsReturn;\n provider: CommentsProvider;\n getTemplateId: () => string | null;\n}\n\n/**\n * Wire {@link CommentsProvider.subscribe} into the three `applyRemote*` paths.\n *\n * A no-op when the provider has no `subscribe` — realtime is optional, and\n * comments without it work exactly the same except that a colleague's comment\n * only appears on the next read. Nothing here knows about a transport: Cloud's\n * Pusher channel lives inside `createCloudCommentsProvider`, and an SSE or\n * long-poll implementation is the same three lines on the consumer's side.\n *\n * Re-subscribes when the template id changes, and unsubscribes on scope dispose —\n * so a session that loads a second template does not keep receiving the first\n * one's comments.\n */\nexport function useCommentListener(options: UseCommentListenerOptions): void {\n const { comments, provider, getTemplateId } = options;\n const { subscribe } = provider;\n if (typeof subscribe !== \"function\") return;\n\n let unsubscribe: (() => void) | null = null;\n\n function stop(): void {\n unsubscribe?.();\n unsubscribe = null;\n }\n\n watch(\n () => getTemplateId(),\n (templateId) => {\n stop();\n if (!templateId) return;\n unsubscribe = subscribe(templateId, (change: CommentChange) => {\n switch (change.type) {\n case \"created\":\n comments.applyRemoteCreate(change.comment);\n break;\n case \"updated\":\n comments.applyRemoteUpdate(change.comment);\n break;\n case \"deleted\":\n comments.applyRemoteDelete(change.commentId, change.parentId);\n break;\n }\n });\n },\n { immediate: true },\n );\n\n onScopeDispose(stop);\n}\n","import type { Translations } from \"../i18n\";\nimport type {\n Block,\n CustomBlock,\n CustomBlockDefinition,\n} from \"@templatical/types\";\n\n/**\n * Returns the i18n label for a built-in block type.\n * Falls back to the raw type string for unknown types.\n */\nexport function getBlockTypeLabel(\n blockType: string,\n translations: Translations,\n): string {\n const labels = translations.blocks as Record<string, string>;\n return labels[blockType] ?? blockType;\n}\n\n/**\n * Display label for a block *instance*, which is what user-facing copy wants:\n * built-ins resolve to their i18n label, custom blocks to the consumer's own\n * name for them (\"Featured Article\"), not the literal type `\"custom\"`.\n *\n * `blocks` carries no `custom` key on purpose — one generic \"Custom\" would make\n * two different custom blocks indistinguishable in a list. The name comes from\n * the definition instead; it is consumer-supplied and therefore NOT localized,\n * which is already how the toolbar and the sidebar palette present it.\n *\n * Falls back to `customType` (the slug) when no definition matches — an\n * instance whose definition was never registered — mirroring `Toolbar.vue`.\n * Pass the array injected under `CUSTOM_BLOCK_DEFINITIONS_KEY`; omitting it\n * degrades to that same slug fallback rather than throwing.\n */\nexport function getBlockLabel(\n block: Block,\n translations: Translations,\n customBlockDefinitions: readonly CustomBlockDefinition[] = [],\n): string {\n if (block.type !== \"custom\") {\n return getBlockTypeLabel(block.type, translations);\n }\n const { customType } = block as CustomBlock;\n const definition = customBlockDefinitions.find((d) => d.type === customType);\n return definition?.name ?? customType;\n}\n","import type { CustomBlockColorField } from \"@templatical/types\";\nimport {\n DEFAULT_RESOLVED_COLORS,\n resolveColorsConfig,\n type ResolvedColors,\n} from \"./resolveColorsConfig\";\n\n/**\n * The palette one custom-block color field renders, plus the audit signals a\n * consumer needs to warn about a field config that could not be honoured.\n *\n * Pure — this util never logs. The warnings live at the definition-registration\n * site (`useEditorCore`, via `collectColorFieldIssues`), the same resolver /\n * consumer split `resolveColorsConfig` and `resolvePaletteBlocks` use.\n */\nexport interface ResolvedFieldColors {\n /** The preset grid this field's picker renders. */\n presets: string[];\n /** Whether the wheel + hex input are offered alongside the grid. */\n allowCustom: boolean;\n /**\n * Entries of the FIELD's own `presets` dropped as invalid hex. Never includes\n * anything inherited from the editor palette — those were already validated\n * (and already warned about) at the editor level, so they are not re-reported.\n */\n invalidPresets: string[];\n /** The field declared `presets: []` — an empty list cannot narrow anything. */\n emptyPresets: boolean;\n /**\n * The field contributed no usable presets (unset, empty, or all-invalid), so\n * {@link ResolvedFieldColors.presets} is the editor's palette.\n */\n presetsInherited: boolean;\n /**\n * The field asked for `allowCustom: true` while the editor is locked. A field\n * may only narrow, so the request is dropped and the field stays locked.\n */\n allowCustomIgnored: boolean;\n}\n\n/**\n * Resolve a custom-block color field's own `presets` / `allowCustom` against\n * the already-resolved editor-wide palette. A field narrows the author's\n * freedom, not the palette: it can lock this field or give it its own presets,\n * but never unlock what the editor locked — and its `presets` *replace* the\n * editor grid rather than intersecting it (a locked field may list colors\n * outside it):\n *\n * - `allowCustom` — locked stays locked. `editorResolved.allowCustom === false`\n * wins outright, so an explicit field `allowCustom: true` under a locked\n * editor is ignored (flagged via `allowCustomIgnored`). Otherwise the field's\n * value applies, falling back to the editor's.\n * - `presets` — the field's entries are validated by {@link resolveColorsConfig}\n * (against the unconfigured baseline, so the hex rule lives in exactly one\n * place and inheritance is decided here rather than by that util's generic\n * per-property precedence). A non-empty valid list replaces the editor's;\n * unset, `[]`, or all-invalid all inherit the editor's palette. This is why\n * `[]` cannot mean \"no chips\" — an empty grid on a locked field would leave\n * the picker with nothing to pick.\n *\n * One residual case is left to `ColorPicker`'s own guard rather than forced\n * here: a field locking itself (`allowCustom: false`) when neither it nor the\n * editor has presets resolves to `{ presets: [], allowCustom: false }`, and the\n * picker's `showFreeform = allowCustom || !hasPresets` still renders the wheel,\n * so the field is never left unable to set a color.\n */\nexport function resolveFieldColors(\n field: Pick<CustomBlockColorField, \"presets\" | \"allowCustom\">,\n editorResolved: ResolvedColors,\n): ResolvedFieldColors {\n // Validate the field's own presets through the shared resolver, against the\n // unconfigured baseline — NOT against `editorResolved`, whose per-property\n // precedence would inherit before we can tell \"field said nothing\" apart\n // from \"field said something unusable\".\n const own = resolveColorsConfig(\n { presets: field.presets },\n DEFAULT_RESOLVED_COLORS,\n );\n\n const presetsInherited = own.presets.length === 0;\n\n const requestedAllowCustom = field.allowCustom ?? editorResolved.allowCustom;\n\n return {\n presets: presetsInherited ? editorResolved.presets : own.presets,\n allowCustom:\n editorResolved.allowCustom === false ? false : requestedAllowCustom,\n invalidPresets: own.invalidPresets,\n emptyPresets: field.presets !== undefined && field.presets.length === 0,\n presetsInherited,\n allowCustomIgnored:\n field.allowCustom === true && editorResolved.allowCustom === false,\n };\n}\n","import {\n computed,\n toValue,\n type ComputedRef,\n type MaybeRefOrGetter,\n} from \"vue\";\nimport { useDropZone } from \"@vueuse/core\";\n\nexport interface UseImageDropOptions {\n /** The drop target element (a template ref). */\n target: MaybeRefOrGetter<HTMLElement | null | undefined>;\n /**\n * Called with the dropped image file(s). The editor acts on a single file\n * per drop (`files[0]`), but the array shape is kept for forward-compat.\n */\n onFiles: (files: File[]) => void;\n /**\n * Gate the zone. While falsy the drop is inert: no highlight, dropped files\n * are ignored. Components pass `canDropMedia && !isUploading` so a\n * read-only library (`create: false`) renders no affordance, a missing\n * media handler does the same, and a second drop can't race an in-flight\n * upload.\n */\n enabled?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseImageDropReturn {\n /** True while an image drag hovers the (enabled) target — drive the highlight. */\n isOver: ComputedRef<boolean>;\n}\n\nconst IMAGE_MIME_PREFIX = \"image/\";\n\n/**\n * Drag-and-drop image upload onto an image block/field (#229). Wraps\n * `useDropZone`; filters dropped files to image MIME types client-side (the\n * authoritative size/type check stays server/plan-config side) and forwards\n * the first image to `onFiles`. The caller hands the file to `onRequestMedia`,\n * which uploads it and returns a `MediaResult` — identical to the Browse Media\n * path, only the input differs.\n *\n * `preventDefaultForUnhandled` keeps the browser from navigating to the dropped\n * file even for a non-image drop on the zone; the editor-root guard covers\n * drops that land outside any zone.\n */\nexport function useImageDrop(options: UseImageDropOptions): UseImageDropReturn {\n const { target, onFiles, enabled } = options;\n\n const isEnabled = (): boolean =>\n enabled === undefined ? true : toValue(enabled);\n\n const { isOverDropZone } = useDropZone(target, {\n preventDefaultForUnhandled: true,\n onDrop: (files) => {\n if (!isEnabled()) return;\n const images = (files ?? []).filter((file) =>\n file.type.startsWith(IMAGE_MIME_PREFIX),\n );\n if (images.length > 0) onFiles([images[0]]);\n },\n });\n\n const isOver = computed(() => isEnabled() && isOverDropZone.value);\n\n return { isOver };\n}\n","import { SOCIAL_ICON_GLYPHS, type SocialPlatform } from \"@templatical/types\";\n\n/**\n * Icon glyph (SVG path + brand color) per platform. Re-exported from the\n * shared kernel in `@templatical/types` so the editor's inline SVG and the\n * renderer's PNG rasterizer never drift. Platform display labels are NOT here\n * — they come from i18n (`t.social.platforms[platform]`).\n */\nexport const socialIcons = SOCIAL_ICON_GLYPHS;\n\nexport const socialIconSizeMap: Record<\"small\" | \"medium\" | \"large\", number> = {\n small: 24,\n medium: 32,\n large: 48,\n};\n\nexport const socialPlatformOptions: SocialPlatform[] = [\n \"facebook\",\n \"twitter\",\n \"instagram\",\n \"linkedin\",\n \"youtube\",\n \"tiktok\",\n \"pinterest\",\n \"email\",\n \"website\",\n \"whatsapp\",\n \"telegram\",\n \"discord\",\n \"snapchat\",\n \"reddit\",\n \"github\",\n \"dribbble\",\n \"behance\",\n];\n"],"mappings":"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"}