@templatical/editor 0.0.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +56 -0
  2. package/README.md +69 -0
  3. package/dist/{AiChatSidebar-CNhSl_ty.js → AiChatSidebar-DgAjDWO1.js} +68 -68
  4. package/dist/{AiFeatureMenu-BFVsjUJY.js → AiFeatureMenu-j6oaiRVi.js} +25 -25
  5. package/dist/{CloudEditor-Bz5Xkxej.js → CloudEditor-yCOkIkZ-.js} +428 -437
  6. package/dist/{CollaboratorBar-D2Gu5Xj2.js → CollaboratorBar-CWVdp2Y6.js} +23 -23
  7. package/dist/CommentsSidebar-CSPHhbQH.js +439 -0
  8. package/dist/CountdownBlock-DK6P_RaQ.js +92 -0
  9. package/dist/CountdownToolbar-Dv-Sz4ui.js +210 -0
  10. package/dist/{DesignReferenceSidebar-u2tKSQK0.js → DesignReferenceSidebar-D7zfvux6.js} +58 -58
  11. package/dist/LoadingTrack-Cdz2SSq9.js +10 -0
  12. package/dist/ModuleBrowserModal-BLYuCZht.js +205 -0
  13. package/dist/ModulePreviewCanvas-CHYqP6K3.js +106 -0
  14. package/dist/NumberWithSuffix-D-5-AZc3.js +424 -0
  15. package/dist/{ParagraphEditor-CYrzYJO5.js → ParagraphEditor-DivLBNWv.js} +237 -236
  16. package/dist/{RichTextEditorContent-BeqlWlWB.js → RichTextEditorContent-G1lXgVJR.js} +42 -42
  17. package/dist/{SaveModuleDialog-Kde6X13X.js → SaveModuleDialog-DGVfiOmE.js} +29 -29
  18. package/dist/{SnapshotHistory-B3EfIcLW.js → SnapshotHistory-DM__MqSu.js} +38 -38
  19. package/dist/{TemplateScoringPanel-D2vCRGgx.js → TemplateScoringPanel-CW13Tlol.js} +80 -80
  20. package/dist/{TestEmailModal-Egk0DFDf.js → TestEmailModal-B8bYqWCY.js} +28 -28
  21. package/dist/TitleEditor-4Jrp3C4m.js +167 -0
  22. package/dist/{TplModal-BCruYWks.js → TplModal-BkoIz0oz.js} +14 -14
  23. package/dist/{_plugin-vue_export-helper-5TQrnOhO.js → _plugin-vue_export-helper-OO6pH1Dh.js} +11 -11
  24. package/dist/{blockTypeIcons-rW4BUUNC.js → blockTypeIcons-4AS3CvXn.js} +2 -2
  25. package/dist/cdn/chunks/{AiFeatureMenu-Dcbshcfu.js → AiFeatureMenu-BcHyzTQy.js} +4 -4
  26. package/dist/cdn/chunks/{AiFeatureMenu-Dcbshcfu.js.map → AiFeatureMenu-BcHyzTQy.js.map} +1 -1
  27. package/dist/cdn/chunks/{CloudEditor-Bca3RyBW.js → CloudEditor-TaEZq-YW.js} +179 -188
  28. package/dist/cdn/chunks/CloudEditor-TaEZq-YW.js.map +1 -0
  29. package/dist/cdn/chunks/{CollaboratorBar-sToKJfHi.js → CollaboratorBar-DbC5-3If.js} +4 -4
  30. package/dist/cdn/chunks/{CollaboratorBar-sToKJfHi.js.map → CollaboratorBar-DbC5-3If.js.map} +1 -1
  31. package/dist/cdn/chunks/CountdownBlock-CfPmCQ7j.js +93 -0
  32. package/dist/cdn/chunks/CountdownBlock-CfPmCQ7j.js.map +1 -0
  33. package/dist/cdn/chunks/CountdownToolbar-CB0EVxwN.js +212 -0
  34. package/dist/cdn/chunks/CountdownToolbar-CB0EVxwN.js.map +1 -0
  35. package/dist/cdn/chunks/{ModuleBrowserModal-BNhwXbWw.js → ModuleBrowserModal-Df2fp9dj.js} +7 -7
  36. package/dist/cdn/chunks/{ModuleBrowserModal-BNhwXbWw.js.map → ModuleBrowserModal-Df2fp9dj.js.map} +1 -1
  37. package/dist/cdn/chunks/{ModulePreviewCanvas-DylUylPU.js → ModulePreviewCanvas-Dw1OCpvV.js} +19 -19
  38. package/dist/cdn/chunks/{ModulePreviewCanvas-DylUylPU.js.map → ModulePreviewCanvas-Dw1OCpvV.js.map} +1 -1
  39. package/dist/cdn/chunks/NumberWithSuffix-BU6ycXyG.js +425 -0
  40. package/dist/cdn/chunks/NumberWithSuffix-BU6ycXyG.js.map +1 -0
  41. package/dist/cdn/chunks/{ParagraphEditor-BBw90y_p.js → ParagraphEditor-BMRNg_BX.js} +37 -37
  42. package/dist/cdn/chunks/{ParagraphEditor-BBw90y_p.js.map → ParagraphEditor-BMRNg_BX.js.map} +1 -1
  43. package/dist/cdn/chunks/{RichTextEditorContent-DvyIO3nR.js → RichTextEditorContent-BKYY1YMZ.js} +5 -5
  44. package/dist/cdn/chunks/{RichTextEditorContent-DvyIO3nR.js.map → RichTextEditorContent-BKYY1YMZ.js.map} +1 -1
  45. package/dist/cdn/chunks/{SaveModuleDialog-DvbSHvhF.js → SaveModuleDialog-D977Tg7t.js} +5 -5
  46. package/dist/cdn/chunks/{SaveModuleDialog-DvbSHvhF.js.map → SaveModuleDialog-D977Tg7t.js.map} +1 -1
  47. package/dist/cdn/chunks/{TitleEditor-tS3mgoVr.js → TitleEditor-q9IzNy38.js} +11 -11
  48. package/dist/cdn/chunks/{TitleEditor-tS3mgoVr.js.map → TitleEditor-q9IzNy38.js.map} +1 -1
  49. package/dist/cdn/chunks/{blockTypeIcons-DbnbyEQE.js → blockTypeIcons-L0fRc3qs.js} +3 -3
  50. package/dist/cdn/chunks/{blockTypeIcons-DbnbyEQE.js.map → blockTypeIcons-L0fRc3qs.js.map} +1 -1
  51. package/dist/cdn/chunks/{de-CgUR_S5I.js → de-3exf7MYE.js} +1 -1
  52. package/dist/cdn/chunks/{de-CgUR_S5I.js.map → de-3exf7MYE.js.map} +1 -1
  53. package/dist/cdn/chunks/{de-D8oDPhBD.js → de-C8LxVUsb.js} +1 -1
  54. package/dist/cdn/chunks/{de-D8oDPhBD.js.map → de-C8LxVUsb.js.map} +1 -1
  55. package/dist/cdn/chunks/{dist-BUmN5e4r.js → dist-D5g00M8N.js} +6 -2
  56. package/dist/cdn/chunks/dist-D5g00M8N.js.map +1 -0
  57. package/dist/cdn/chunks/{draggable-m78lz0gI.js → draggable-Bcb86AsV.js} +5 -5
  58. package/dist/cdn/chunks/draggable-Bcb86AsV.js.map +1 -0
  59. package/dist/cdn/chunks/{emojiData-CNNgvDqx.js → emojiData-DUHzsh4j.js} +1 -1
  60. package/dist/cdn/chunks/{emojiData-CNNgvDqx.js.map → emojiData-DUHzsh4j.js.map} +1 -1
  61. package/dist/cdn/chunks/{en-HseRPPeK.js → en-BoEycuqw.js} +1 -1
  62. package/dist/cdn/chunks/{en-HseRPPeK.js.map → en-BoEycuqw.js.map} +1 -1
  63. package/dist/cdn/chunks/{en-BBmfBDqY.js → en-KNPUKxYp.js} +1 -1
  64. package/dist/cdn/chunks/{en-BBmfBDqY.js.map → en-KNPUKxYp.js.map} +1 -1
  65. package/dist/cdn/chunks/{extensions-ClpY3m0W.js → extensions-BZ1POyiv.js} +11 -11
  66. package/dist/cdn/chunks/{extensions-ClpY3m0W.js.map → extensions-BZ1POyiv.js.map} +1 -1
  67. package/dist/cdn/chunks/{features-B5dGxWLi.js → features-Ccqmm6-M.js} +549 -627
  68. package/dist/cdn/chunks/features-Ccqmm6-M.js.map +1 -0
  69. package/dist/cdn/chunks/{icons-hflTyPmb.js → icons-x53__2GC.js} +8 -8
  70. package/dist/cdn/chunks/icons-x53__2GC.js.map +1 -0
  71. package/dist/cdn/chunks/{liquid.browser-BxyRVCKv.js → liquid.browser-CEMn-ZqL.js} +2 -2
  72. package/dist/cdn/chunks/liquid.browser-CEMn-ZqL.js.map +1 -0
  73. package/dist/cdn/chunks/{media-library-Dgy-V-JA.js → media-library-DUoQk-w7.js} +97 -97
  74. package/dist/cdn/chunks/media-library-DUoQk-w7.js.map +1 -0
  75. package/dist/cdn/chunks/{pusher-D-m2WSdL.js → pusher-DwSQn6BA.js} +2 -2
  76. package/dist/cdn/chunks/pusher-DwSQn6BA.js.map +1 -0
  77. package/dist/cdn/chunks/{src-TDwMmqm0.js → src-DskCIAr8.js} +8 -8
  78. package/dist/cdn/chunks/{src-TDwMmqm0.js.map → src-DskCIAr8.js.map} +1 -1
  79. package/dist/cdn/chunks/{styleConstants-DFe3I4Op.js → styleConstants-DP1VOca8.js} +1 -1
  80. package/dist/cdn/chunks/{styleConstants-DFe3I4Op.js.map → styleConstants-DP1VOca8.js.map} +1 -1
  81. package/dist/cdn/chunks/{styles-hCOCOR6K.js → styles-BIlpNe52.js} +622 -1238
  82. package/dist/cdn/chunks/styles-BIlpNe52.js.map +1 -0
  83. package/dist/cdn/chunks/{tiptap-C28NLMX8.js → tiptap-hPC_BgDO.js} +850 -826
  84. package/dist/cdn/chunks/tiptap-hPC_BgDO.js.map +1 -0
  85. package/dist/cdn/editor.css +1 -1
  86. package/dist/cdn/editor.js +92 -97
  87. package/dist/cdn/editor.js.map +1 -1
  88. package/dist/{check-B5S-C0bz.js → check-Bff5NzCh.js} +1 -1
  89. package/dist/{chevron-down-CjsV1T0W.js → chevron-down-Bbv4SJwx.js} +1 -1
  90. package/dist/{circle-alert-CVTmJHKW.js → circle-alert-Dpr8MgkR.js} +1 -1
  91. package/dist/{clock-CAjdSHBy.js → clock-raVPKfl4.js} +1 -1
  92. package/dist/{dist-DJrDTuRr.js → dist-BHgEdO-M.js} +1001 -983
  93. package/dist/dist-BibwE8Y0.js +5 -0
  94. package/dist/{dist-DPM-DFvh.js → dist-BnlZ_HwX.js} +340 -340
  95. package/dist/{dist-BTtKPcd2.js → dist-C9yhcxsf.js} +2 -2
  96. package/dist/{dist-COguaIvm.js → dist-CGffNO8z.js} +2 -2
  97. package/dist/dist-CfGpCUSx.js +5 -0
  98. package/dist/{dist-COOFiBFV.js → dist-Chd-H2S3.js} +5 -5
  99. package/dist/{dist-DfqLGwSF.js → dist-Cq0OFLMj.js} +2 -2
  100. package/dist/{dist-B4LTlrAn.js → dist-D42og5Sc.js} +191 -185
  101. package/dist/{dist-CBr-qhie.js → dist-Owxo48fF.js} +2 -2
  102. package/dist/{dist-Crqkuf-w.js → dist-UZ8UzVZ-.js} +15 -15
  103. package/dist/dist-n0PkFNGl.js +314 -0
  104. package/dist/{dist-DHYuVJ_7.js → dist-skgH9dyo.js} +2 -2
  105. package/dist/{extensions-C6xp_B7K.js → extensions-DQKaICBH.js} +81 -81
  106. package/dist/{image-up-vZa1Txr-.js → image-up-B1AEiHCh.js} +1 -1
  107. package/dist/index.d.ts +17 -6
  108. package/dist/{keys-C0MQRs8d.js → keys-CIw9mUI2.js} +1 -1
  109. package/dist/{liquid.browser-C02owkex.js → liquid.browser-Bm53dnK1.js} +1 -1
  110. package/dist/{loader-circle-DilFjHSk.js → loader-circle-BYeKJ3to.js} +1 -1
  111. package/dist/{message-circle-Dpcnc2oa.js → message-circle-PhNlI7e7.js} +1 -1
  112. package/dist/{refresh-cw-4r7rkHHX.js → refresh-cw-DhDYRBDd.js} +1 -1
  113. package/dist/{rolldown-runtime-gEudmnaM.js → rolldown-runtime-BZGGJVDF.js} +2 -5
  114. package/dist/{scan-line-684IxPQ0.js → scan-line-Bw8bauvt.js} +1 -1
  115. package/dist/{send-ChDw64yL.js → send-CJPSzbLO.js} +1 -1
  116. package/dist/{shield-check-9dhLdyST.js → shield-check-DdEvVYMH.js} +1 -1
  117. package/dist/{sparkles-Co9fkDcv.js → sparkles-DT_27AYs.js} +1 -1
  118. package/dist/style.css +2 -0
  119. package/dist/{styleConstants-RV3eYwkD.js → styleConstants-Dqz9z3aa.js} +6 -45
  120. package/dist/styles-tcwlvlEB.js +2942 -0
  121. package/dist/templatical-editor.js +114 -119
  122. package/dist/text-align-start-4Hx15GUf.js +43 -0
  123. package/dist/{trash-2-CZkMtjeC.js → trash-2-BY-xp5hg.js} +1 -1
  124. package/dist/{triangle-alert-Dvt51agD.js → triangle-alert-DG72vdb2.js} +1 -1
  125. package/dist/useEditorCore-DMH2QpUL.js +8946 -0
  126. package/dist/{useI18n-D6m7ZUgY.js → useI18n-B8CN2iWs.js} +4 -4
  127. package/dist/{useMergeTag-CSXcnFBc.js → useMergeTag-Dz_Dx2Jz.js} +3 -3
  128. package/dist/vue.runtime.esm-bundler-DryoQb-v.js +5775 -0
  129. package/dist/{x-CU2XZOkQ.js → x-D-7GkO6Z.js} +1 -1
  130. package/package.json +31 -15
  131. package/dist/CommentsSidebar-CTeEAZTj.js +0 -439
  132. package/dist/LoadingTrack--aUassFH.js +0 -10
  133. package/dist/ModuleBrowserModal-DNxODPlX.js +0 -205
  134. package/dist/ModulePreviewCanvas-DnGe1Y-2.js +0 -106
  135. package/dist/TitleEditor-DwUpo4gF.js +0 -167
  136. package/dist/cdn/chunks/CloudEditor-Bca3RyBW.js.map +0 -1
  137. package/dist/cdn/chunks/dist-BUmN5e4r.js.map +0 -1
  138. package/dist/cdn/chunks/draggable-m78lz0gI.js.map +0 -1
  139. package/dist/cdn/chunks/features-B5dGxWLi.js.map +0 -1
  140. package/dist/cdn/chunks/icons-hflTyPmb.js.map +0 -1
  141. package/dist/cdn/chunks/liquid.browser-BxyRVCKv.js.map +0 -1
  142. package/dist/cdn/chunks/media-library-Dgy-V-JA.js.map +0 -1
  143. package/dist/cdn/chunks/pusher-D-m2WSdL.js.map +0 -1
  144. package/dist/cdn/chunks/styles-hCOCOR6K.js.map +0 -1
  145. package/dist/cdn/chunks/tiptap-C28NLMX8.js.map +0 -1
  146. package/dist/dist-Ac4o1O4a.js +0 -314
  147. package/dist/dist-DEJZ9iAU.js +0 -5
  148. package/dist/dist-DmVOCJjI.js +0 -5
  149. package/dist/styles-nvxFh6L6.js +0 -3557
  150. package/dist/templatical-editor.css +0 -2
  151. package/dist/templatical-editor.umd.cjs +0 -178
  152. package/dist/useEditorCore-ColJh1ST.js +0 -5695
  153. /package/dist/cdn/chunks/{_rolldown_dynamic_import_helper-DMEI4TQ3.js → _rolldown_dynamic_import_helper-BRcA6nWq.js} +0 -0
  154. /package/dist/{de-Dh8ON-dm.js → de-CN0xu_Tp.js} +0 -0
  155. /package/dist/{emojiData-Qc8mH_zW.js → emojiData-CwKEDxVf.js} +0 -0
  156. /package/dist/{en-C7IShRSD.js → en-DXvHkaUF.js} +0 -0
  157. /package/dist/{formatRelativeTime-D8f6NR7i.js → formatRelativeTime-BSpx5DHC.js} +0 -0
  158. /package/dist/{readableTextColor-B809bF5J.js → readableTextColor-CYCxm1jQ.js} +0 -0
  159. /package/dist/{timeouts-CmBrLeZA.js → timeouts-BSGxjuUF.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"CloudEditor-Bca3RyBW.js","names":["$emit"],"sources":["../../../src/cloud/composables/useSnapshotPreview.ts","../../../src/cloud/composables/useCloudPanelState.ts","../../../src/cloud/composables/useCollabUndoWarning.ts","../../../src/cloud/composables/useCloudFeatureFlags.ts","../../../src/cloud/composables/useCloudMediaLibrary.ts","../../../src/cloud/composables/useCloudInitialization.ts","../../../src/utils/preRenderCustomBlocks.ts","../../../src/cloud/composables/useCloudLifecycle.ts","../../../src/cloud/components/CloudHeader.vue","../../../src/cloud/components/CloudHeader.vue","../../../src/cloud/components/CloudPanels.vue","../../../src/cloud/components/CloudPanels.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/SnapshotPreviewBanner.vue","../../../src/cloud/components/SnapshotPreviewBanner.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/CloudEditor.vue","../../../src/cloud/CloudEditor.vue"],"sourcesContent":["import { computed, shallowRef, ref, type ComputedRef, type Ref } from \"vue\";\nimport type { TemplateContent, TemplateSnapshot } from \"@templatical/types\";\nimport type {\n UseHistoryReturn,\n UseConditionPreviewReturn,\n UseAutoSaveReturn,\n} from \"@templatical/core\";\nimport {\n useSnapshotHistory,\n type AuthManager,\n type UseSnapshotHistoryReturn,\n} from \"@templatical/core/cloud\";\nimport type { BaseEditorReturn } from \"../../composables/useEditorCore\";\n\nexport interface UseSnapshotPreviewOptions {\n authManager: AuthManager;\n editor: BaseEditorReturn & {\n hasTemplate: () => boolean;\n createSnapshot: () => Promise<void>;\n };\n history: UseHistoryReturn;\n conditionPreview: UseConditionPreviewReturn;\n autoSave: UseAutoSaveReturn | null;\n onError?: (error: Error) => void;\n}\n\nexport interface UseSnapshotPreviewReturn {\n snapshotHistoryInstance: Ref<UseSnapshotHistoryReturn | null>;\n previewingSnapshot: Ref<TemplateSnapshot | null>;\n contentBeforePreview: Ref<TemplateContent | null>;\n isPreviewingSnapshot: ComputedRef<boolean>;\n snapshotHistorySnapshots: ComputedRef<TemplateSnapshot[]>;\n snapshotHistoryIsLoading: ComputedRef<boolean>;\n snapshotHistoryIsRestoring: ComputedRef<boolean>;\n initSnapshotHistory: () => void;\n handleRestore: (template: { content: TemplateContent }) => void;\n handleSnapshotNavigate: (snapshot: TemplateSnapshot) => Promise<void>;\n confirmRestoreSnapshot: () => Promise<void>;\n cancelPreview: () => void;\n loadSnapshotHistory: () => Promise<void>;\n}\n\nexport function useSnapshotPreview(\n options: UseSnapshotPreviewOptions,\n): UseSnapshotPreviewReturn {\n const { authManager, editor, history, conditionPreview, autoSave, onError } =\n options;\n\n const snapshotHistoryInstance = shallowRef<UseSnapshotHistoryReturn | null>(\n null,\n );\n const previewingSnapshot = ref<TemplateSnapshot | null>(null);\n const contentBeforePreview = ref<TemplateContent | null>(null);\n\n const isPreviewingSnapshot = computed(\n () => previewingSnapshot.value !== null,\n );\n const snapshotHistorySnapshots = computed(\n () => snapshotHistoryInstance.value?.snapshots.value ?? [],\n );\n const snapshotHistoryIsLoading = computed(\n () => snapshotHistoryInstance.value?.isLoading.value ?? false,\n );\n const snapshotHistoryIsRestoring = computed(\n () => snapshotHistoryInstance.value?.isRestoring.value ?? false,\n );\n\n function initSnapshotHistory(): void {\n if (editor.state.template?.id && !snapshotHistoryInstance.value) {\n snapshotHistoryInstance.value = useSnapshotHistory({\n authManager,\n templateId: editor.state.template.id,\n onRestore: handleRestore,\n onError,\n });\n snapshotHistoryInstance.value.loadSnapshots();\n }\n }\n\n function handleRestore(template: { content: TemplateContent }): void {\n editor.setContent(template.content, false);\n history.clear();\n conditionPreview.reset();\n }\n\n async function handleSnapshotNavigate(\n snapshot: TemplateSnapshot,\n ): Promise<void> {\n if (previewingSnapshot.value) {\n previewingSnapshot.value = snapshot;\n editor.setContent(snapshot.content, false);\n return;\n }\n\n if (editor.state.isDirty && editor.hasTemplate()) {\n await editor.createSnapshot();\n }\n\n contentBeforePreview.value = structuredClone(editor.content.value);\n\n autoSave?.pause();\n previewingSnapshot.value = snapshot;\n editor.setContent(snapshot.content, false);\n }\n\n async function confirmRestoreSnapshot(): Promise<void> {\n if (!previewingSnapshot.value || !snapshotHistoryInstance.value) return;\n\n try {\n await snapshotHistoryInstance.value.restoreSnapshot(\n previewingSnapshot.value.id,\n );\n await snapshotHistoryInstance.value.loadSnapshots();\n } finally {\n previewingSnapshot.value = null;\n contentBeforePreview.value = null;\n autoSave?.resume();\n }\n }\n\n function cancelPreview(): void {\n if (!previewingSnapshot.value || !contentBeforePreview.value) return;\n\n editor.setContent(contentBeforePreview.value, false);\n\n previewingSnapshot.value = null;\n contentBeforePreview.value = null;\n\n autoSave?.resume();\n }\n\n async function loadSnapshotHistory(): Promise<void> {\n if (snapshotHistoryInstance.value) {\n await snapshotHistoryInstance.value.loadSnapshots();\n }\n }\n\n return {\n snapshotHistoryInstance,\n previewingSnapshot,\n contentBeforePreview,\n isPreviewingSnapshot,\n snapshotHistorySnapshots,\n snapshotHistoryIsLoading,\n snapshotHistoryIsRestoring,\n initSnapshotHistory,\n handleRestore,\n handleSnapshotNavigate,\n confirmRestoreSnapshot,\n cancelPreview,\n loadSnapshotHistory,\n };\n}\n","import { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport type { AiFeature } from \"../components/AiFeatureMenu.vue\";\nimport type { MediaCategory } from \"@templatical/media-library\";\n\nexport interface UseCloudPanelStateReturn {\n activePanel: Ref<RightPanel | null>;\n aiChatOpen: ComputedRef<boolean> & { value: boolean };\n scoringPanelOpen: ComputedRef<boolean> & { value: boolean };\n designReferenceOpen: ComputedRef<boolean> & { value: boolean };\n commentsOpen: ComputedRef<boolean> & { value: boolean };\n testEmailModalOpen: Ref<boolean>;\n mediaLibraryOpen: Ref<boolean>;\n mediaLibraryAccept: Ref<MediaCategory[] | undefined>;\n aiMenuOpen: Ref<boolean>;\n aiMenuRef: Ref<HTMLElement | null>;\n rightPanelOpen: ComputedRef<boolean>;\n activeAiFeature: ComputedRef<AiFeature | null>;\n aiButtonActive: ComputedRef<boolean>;\n toggleAiMenu: () => void;\n handleAiFeatureSelect: (feature: AiFeature) => void;\n}\n\ntype RightPanel = \"ai-chat\" | \"scoring\" | \"design-reference\" | \"comments\";\n\nexport function useCloudPanelState(): UseCloudPanelStateReturn {\n const activePanel = ref<RightPanel | null>(null);\n\n const aiChatOpen = computed({\n get: () => activePanel.value === \"ai-chat\",\n set: (v) => (activePanel.value = v ? \"ai-chat\" : null),\n });\n const scoringPanelOpen = computed({\n get: () => activePanel.value === \"scoring\",\n set: (v) => (activePanel.value = v ? \"scoring\" : null),\n });\n const designReferenceOpen = computed({\n get: () => activePanel.value === \"design-reference\",\n set: (v) => (activePanel.value = v ? \"design-reference\" : null),\n });\n const commentsOpen = computed({\n get: () => activePanel.value === \"comments\",\n set: (v) => (activePanel.value = v ? \"comments\" : null),\n });\n\n const testEmailModalOpen = ref(false);\n const mediaLibraryOpen = ref(false);\n const mediaLibraryAccept = ref<MediaCategory[] | undefined>(undefined);\n const aiMenuOpen = ref(false);\n const aiMenuRef = ref<HTMLElement | null>(null);\n\n const rightPanelOpen = computed(() => activePanel.value !== null);\n\n const activeAiFeature = computed<AiFeature | null>(() => {\n const p = activePanel.value;\n if (p === \"ai-chat\" || p === \"design-reference\" || p === \"scoring\")\n return p;\n return null;\n });\n\n const aiButtonActive = computed(\n () =>\n aiMenuOpen.value ||\n activePanel.value === \"ai-chat\" ||\n activePanel.value === \"design-reference\" ||\n activePanel.value === \"scoring\",\n );\n\n function toggleAiMenu(): void {\n aiMenuOpen.value = !aiMenuOpen.value;\n }\n\n function handleAiFeatureSelect(feature: AiFeature): void {\n aiMenuOpen.value = false;\n activePanel.value = activePanel.value === feature ? null : feature;\n }\n\n onClickOutside(aiMenuRef, () => {\n aiMenuOpen.value = false;\n });\n\n return {\n activePanel,\n aiChatOpen,\n scoringPanelOpen,\n designReferenceOpen,\n commentsOpen,\n testEmailModalOpen,\n mediaLibraryOpen,\n mediaLibraryAccept,\n aiMenuOpen,\n aiMenuRef,\n rightPanelOpen,\n activeAiFeature,\n aiButtonActive,\n toggleAiMenu,\n handleAiFeatureSelect,\n };\n}\n","import { type ComputedRef, type Ref, ref } from \"vue\";\nimport { useTimeoutFn } from \"@vueuse/core\";\nimport { COLLAB_UNDO_WARNING_MS } from \"../../constants/timeouts\";\n\nexport interface UseCollabUndoWarningOptions {\n /** Whether collaboration is currently enabled (reactive). */\n isCollaborationEnabled: ComputedRef<boolean>;\n /** Returns the current list of collaborators. */\n getCollaboratorCount: () => number;\n /** Whether the history stack has entries to undo. */\n canUndo: ComputedRef<boolean>;\n}\n\nexport interface UseCollabUndoWarningReturn {\n collabUndoWarningVisible: Ref<boolean>;\n showCollabUndoWarning: () => void;\n}\n\nexport function useCollabUndoWarning(\n options: UseCollabUndoWarningOptions,\n): UseCollabUndoWarningReturn {\n const { isCollaborationEnabled, getCollaboratorCount, canUndo } = options;\n\n const collabUndoWarningFired = ref(false);\n const collabUndoWarningVisible = ref(false);\n\n const { start: startCollabUndoWarningTimeout } = useTimeoutFn(\n () => {\n collabUndoWarningVisible.value = false;\n },\n COLLAB_UNDO_WARNING_MS,\n { immediate: false },\n );\n\n function showCollabUndoWarning(): void {\n if (\n collabUndoWarningFired.value ||\n !isCollaborationEnabled.value ||\n getCollaboratorCount() === 0 ||\n !canUndo.value\n ) {\n return;\n }\n\n collabUndoWarningFired.value = true;\n collabUndoWarningVisible.value = true;\n startCollabUndoWarningTimeout();\n }\n\n return {\n collabUndoWarningVisible,\n showCollabUndoWarning,\n };\n}\n","import { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { useTimeoutFn } from \"@vueuse/core\";\nimport type {\n UsePlanConfigReturn,\n UseAiConfigReturn,\n} from \"@templatical/core/cloud\";\n\nexport interface UseCloudFeatureFlagsOptions {\n planConfigInstance: UsePlanConfigReturn;\n aiConfig: UseAiConfigReturn;\n editor: {\n state: {\n readonly template?: { id: string } | null;\n };\n };\n}\n\nexport interface UseCloudFeatureFlagsReturn {\n canUseAiGeneration: ComputedRef<boolean>;\n canSendTestEmail: ComputedRef<boolean>;\n hasTemplateSaved: ComputedRef<boolean>;\n isWhiteLabeled: ComputedRef<boolean>;\n templateLimit: ComputedRef<number | null>;\n templateCount: ComputedRef<number>;\n isSaveExporting: Ref<boolean>;\n saveStatus: Ref<\"idle\" | \"saved\" | \"error\">;\n saveErrorMessage: Ref<string>;\n startSaveStatusClear: () => void;\n}\n\nexport function useCloudFeatureFlags(\n options: UseCloudFeatureFlagsOptions,\n): UseCloudFeatureFlagsReturn {\n const { planConfigInstance, aiConfig, editor } = options;\n\n const canUseAiGeneration = computed(\n () =>\n planConfigInstance.hasFeature(\"ai_generation\") &&\n aiConfig.hasAnyMenuFeature.value,\n );\n const canSendTestEmail = computed(() =>\n planConfigInstance.hasFeature(\"test_email\"),\n );\n const hasTemplateSaved = computed(() => !!editor.state.template?.id);\n const isWhiteLabeled = computed(() =>\n planConfigInstance.hasFeature(\"white_label\"),\n );\n const templateLimit = computed(\n () => planConfigInstance.config.value?.limits.max_templates ?? null,\n );\n const templateCount = computed(\n () => planConfigInstance.config.value?.template_count ?? 0,\n );\n\n const isSaveExporting = ref(false);\n const saveStatus = ref<\"idle\" | \"saved\" | \"error\">(\"idle\");\n const saveErrorMessage = ref(\"\");\n\n const { start: startSaveStatusClear } = useTimeoutFn(\n () => {\n saveStatus.value = \"idle\";\n },\n 3000,\n { immediate: false },\n );\n\n return {\n canUseAiGeneration,\n canSendTestEmail,\n hasTemplateSaved,\n isWhiteLabeled,\n templateLimit,\n templateCount,\n isSaveExporting,\n saveStatus,\n saveErrorMessage,\n startSaveStatusClear,\n };\n}\n","import { onScopeDispose, type Ref } from \"vue\";\nimport type {\n MediaCategory,\n MediaItem,\n MediaRequestContext,\n} from \"@templatical/media-library\";\nimport type { MediaResult } from \"@templatical/types\";\n\nexport interface UseCloudMediaLibraryOptions {\n onRequestMedia?: (context: MediaRequestContext) => Promise<MediaItem | null>;\n mediaLibraryOpen: Ref<boolean>;\n mediaLibraryAccept: Ref<MediaCategory[] | undefined>;\n}\n\nexport interface UseCloudMediaLibraryReturn {\n handleRequestMedia: () => Promise<MediaResult | null>;\n handleMediaSelect: (item: MediaItem) => void;\n handleMediaLibraryClose: () => void;\n}\n\nexport function useCloudMediaLibrary(\n options: UseCloudMediaLibraryOptions,\n): UseCloudMediaLibraryReturn {\n const { onRequestMedia, mediaLibraryOpen, mediaLibraryAccept } = options;\n\n let mediaResolve: ((result: MediaResult | null) => void) | null = null;\n\n async function handleRequestMedia(): Promise<MediaResult | null> {\n // If consumer provides a custom media handler, use it\n if (onRequestMedia) {\n const item = await onRequestMedia({ accept: [\"images\"] });\n if (!item) return null;\n return { url: item.url, alt: item.alt_text || undefined };\n }\n\n // Otherwise open the built-in media library\n mediaLibraryAccept.value = [\"images\"];\n mediaLibraryOpen.value = true;\n return new Promise<MediaResult | null>((resolve) => {\n mediaResolve = (result) => {\n resolve(result);\n };\n });\n }\n\n function handleMediaSelect(item: MediaItem): void {\n mediaLibraryOpen.value = false;\n mediaResolve?.({ url: item.url, alt: item.alt_text || undefined });\n mediaResolve = null;\n }\n\n function handleMediaLibraryClose(): void {\n mediaLibraryOpen.value = false;\n mediaResolve?.(null);\n mediaResolve = null;\n }\n\n onScopeDispose(() => {\n if (mediaResolve) {\n mediaResolve(null);\n mediaResolve = null;\n }\n });\n\n return {\n handleRequestMedia,\n handleMediaSelect,\n handleMediaLibraryClose,\n };\n}\n","import { computed, provide, ref, watch, type ComputedRef, type Ref } from \"vue\";\nimport {\n AuthManager,\n performHealthCheck,\n useAiConfig,\n useCollaboration,\n useCollaborationBroadcast,\n useCommentListener,\n useComments,\n useEditor,\n useExport,\n useMcpListener,\n usePlanConfig,\n useSavedModules,\n useTemplateScoring,\n useTestEmail,\n useWebSocket,\n type UseAiConfigReturn,\n type UseCollaborationReturn,\n type UseCommentsReturn,\n type UseExportReturn,\n type UsePlanConfigReturn,\n type UseSavedModulesReturn,\n type UseTemplateScoringReturn,\n type UseTestEmailReturn,\n type UseWebSocketReturn,\n type UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\nimport type {\n McpOperationPayload,\n SaveResult,\n ThemeOverrides,\n UiTheme,\n} from \"@templatical/types\";\n\nimport {\n useEditorCore,\n type UseEditorCoreReturn,\n} from \"../../composables/useEditorCore\";\nimport { useDragDrop } from \"../../composables/useDragDrop\";\nimport type { UseFontsReturn } from \"../../composables/useFonts\";\nimport type { Translations } from \"../../i18n\";\nimport type { EditorCapabilities } from \"../../types/editor-capabilities\";\nimport {\n ON_REQUEST_MEDIA_KEY,\n AUTH_MANAGER_KEY,\n AI_CONFIG_KEY,\n COMMENTS_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n SCORING_KEY,\n CAPABILITIES_KEY,\n} from \"../../keys\";\nimport { DEFAULT_AUTO_SAVE_DEBOUNCE_MS } from \"../../constants/timeouts\";\nimport { logger } from \"../../utils/logger\";\n\nimport {\n useSnapshotPreview,\n type UseSnapshotPreviewReturn,\n} from \"./useSnapshotPreview\";\nimport {\n useCloudPanelState,\n type UseCloudPanelStateReturn,\n} from \"./useCloudPanelState\";\nimport { useCollabUndoWarning } from \"./useCollabUndoWarning\";\nimport {\n useCloudFeatureFlags,\n type UseCloudFeatureFlagsReturn,\n} from \"./useCloudFeatureFlags\";\nimport {\n useCloudMediaLibrary,\n type UseCloudMediaLibraryReturn,\n} from \"./useCloudMediaLibrary\";\n\nimport type { TemplaticalCloudEditorConfig } from \"../cloudConfig\";\n\n/** Minimal interface of the CommentsSidebar-filter target. */\ninterface CommentsSidebarInstance {\n filterByBlock: (blockId: string) => void;\n}\n\n/** Getter for the target — evaluated lazily so the sidebar can mount later. */\ntype CommentsSidebarGetter = () => CommentsSidebarInstance | null;\n\ntype CollaborationInstance = UseCollaborationReturn & {\n _broadcastOperation: (payload: McpOperationPayload) => void;\n _isProcessingRemoteOperation: () => boolean;\n};\n\nexport interface UseCloudInitializationOptions {\n config: TemplaticalCloudEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n emit: (event: \"ready\") => void;\n /** Lazy getter for the CommentsSidebar-filter target (for block-filter). */\n getCommentsSidebar: CommentsSidebarGetter;\n}\n\nexport interface UseCloudInitializationReturn {\n // Init state\n isInitializing: Ref<boolean>;\n isAuthReady: Ref<boolean>;\n initError: Ref<Error | null>;\n isDestroyed: () => boolean;\n\n // Infrastructure\n authManager: AuthManager;\n planConfigInstance: UsePlanConfigReturn;\n websocket: UseWebSocketReturn;\n collaboration: CollaborationInstance | null;\n isCollaborationEnabled: ComputedRef<boolean>;\n\n // Editor + core\n editor: CloudUseEditorReturn;\n core: UseEditorCoreReturn;\n\n // Cloud composables\n aiConfig: UseAiConfigReturn;\n featureFlags: UseCloudFeatureFlagsReturn;\n mediaLib: UseCloudMediaLibraryReturn;\n exporter: UseExportReturn;\n testEmail: UseTestEmailReturn;\n commentsInstance: UseCommentsReturn;\n savedModulesHeadless: UseSavedModulesReturn;\n scoringInstance: UseTemplateScoringReturn;\n panelState: UseCloudPanelStateReturn;\n snapshotPreview: UseSnapshotPreviewReturn;\n collabWarning: ReturnType<typeof useCollabUndoWarning>;\n\n // Local UI state surfaced through capabilities\n showSaveModuleDialog: Ref<boolean>;\n showModuleBrowserModal: Ref<boolean>;\n saveModulePreSelectedBlockId: Ref<string | null>;\n\n // Late-bound save hook — set by `useCloudLifecycle` after it wires saveTemplate.\n // The `onSave` keyboard shortcut + `useEditorCore` autoSave both route here.\n onSaveHook: { value: (() => Promise<SaveResult>) | null };\n\n // Methods\n initialize: () => Promise<void>;\n destroy: () => void;\n setThemeOverrides: (overrides: ThemeOverrides) => void;\n setUiTheme: (theme: UiTheme) => void;\n openCommentsForBlock: (blockId: string) => void;\n}\n\n/**\n * Owns the entire CloudEditor.vue initialization dance:\n * 1. AuthManager + PlanConfig\n * 2. Collaboration locked-blocks forward-ref\n * 3. Cloud editor\n * 4. WebSocket + MCP listener\n * 5. Collaboration (wraps editor methods — MUST precede useEditorCore)\n * 6. useEditorCore (provides all shared keys)\n * 7. Collab undo warning (needs core.history.canUndo)\n * 8. Snapshot preview (needs core.autoSave)\n * 9. Remaining cloud composables (aiConfig, exporter, testEmail, etc.)\n * 10. Cloud-only provides (including CAPABILITIES_KEY override)\n *\n * Forward-ref pattern for `snapshotPreview` / `collabWarning` is contained\n * here instead of leaking to the parent.\n */\nexport function useCloudInitialization(\n options: UseCloudInitializationOptions,\n): UseCloudInitializationReturn {\n const { config, translations, fontsManager, emit, getCommentsSidebar } =\n options;\n\n // --- Init state ---\n const isInitializing = ref(true);\n const isAuthReady = ref(false);\n const initError = ref<Error | null>(null);\n let _destroyed = false;\n\n // --- Late-bound save hook (filled in by useCloudLifecycle) ---\n const onSaveHook: { value: (() => Promise<SaveResult>) | null } = {\n value: null,\n };\n\n // --- Forward refs for circular composable dependencies ---\n let snapshotPreviewRef: UseSnapshotPreviewReturn | null = null;\n let collabWarningRef: ReturnType<typeof useCollabUndoWarning> | null = null;\n\n // --- 1. AuthManager + PlanConfig ---\n const authManager = new AuthManager({\n ...config.auth,\n onError: config.onError,\n });\n\n const planConfigInstance = usePlanConfig({\n authManager,\n onError: config.onError,\n });\n\n // --- 2. Collaboration locked blocks ref (forward-declared for step 3) ---\n const collaborationLockedBlocks = ref<Map<string, unknown>>(new Map());\n\n // --- 3. Cloud editor ---\n const editor = useEditor({\n authManager,\n defaultFontFamily: config.fonts?.defaultFont,\n templateDefaults: config.templateDefaults,\n onError: config.onError,\n lockedBlocks: collaborationLockedBlocks,\n });\n\n // --- 4. WebSocket + MCP listener ---\n const websocket = useWebSocket({\n authManager,\n onError: config.onError,\n });\n\n if (config.mcp?.enabled) {\n useMcpListener({\n editor,\n channel: websocket.channel,\n onOperation: config.mcp.onOperation,\n });\n }\n\n // --- 5. Collaboration (MUST precede useEditorCore) ---\n //\n // Order: `useCollaborationBroadcast` wraps editor mutation methods so they\n // broadcast to peers. Step 6's `useHistoryInterceptor` then wraps them\n // AGAIN to push history entries. Reversing would push history for remote\n // operations, causing local/remote state drift.\n let collaboration: CollaborationInstance | null = null;\n\n if (config.collaboration?.enabled) {\n collaboration = useCollaboration({\n authManager,\n editor,\n channel: websocket.channel,\n onError: config.onError,\n onCollaboratorJoined: config.collaboration.onCollaboratorJoined,\n onCollaboratorLeft: config.collaboration.onCollaboratorLeft,\n onBlockLocked: config.collaboration.onBlockLocked,\n onBlockUnlocked: config.collaboration.onBlockUnlocked,\n }) as CollaborationInstance;\n\n watch(\n () => collaboration!.lockedBlocks.value,\n (newLockedBlocks) => {\n collaborationLockedBlocks.value = newLockedBlocks;\n },\n { immediate: true },\n );\n\n useCollaborationBroadcast(editor, collaboration);\n }\n\n const isCollaborationEnabled = computed(\n () =>\n !!config.collaboration?.enabled &&\n planConfigInstance.hasFeature(\"collaboration\"),\n );\n\n // --- 6. useEditorCore ---\n const core = useEditorCore({\n editor,\n config: {\n uiTheme: config.uiTheme,\n theme: undefined, // applied in initialize() after plan check\n blockDefaults: config.blockDefaults,\n customBlocks: [], // deferred to initialize()\n mergeTags: config.mergeTags,\n displayConditions: config.displayConditions,\n onRequestMedia: null, // cloud handles via mediaLib.handleRequestMedia\n onSave: () => {\n onSaveHook.value?.().catch((err) => {\n config.onError?.(err as Error);\n });\n },\n },\n translations,\n fontsManager,\n historyOptions: collaboration\n ? {\n isRemoteOperation: () =>\n collaboration!._isProcessingRemoteOperation(),\n }\n : undefined,\n autoSaveOptions: {\n onChange: async () => {\n if (editor.hasTemplate()) {\n await editor.createSnapshot();\n snapshotPreviewRef?.snapshotHistoryInstance.value?.loadSnapshots();\n }\n },\n debounce: config.autoSaveDebounce ?? DEFAULT_AUTO_SAVE_DEBOUNCE_MS,\n enabled: () =>\n config.autoSave !== false && planConfigInstance.hasFeature(\"auto_save\"),\n },\n themeExtraStyles: () => ({\n \"--tpl-drop-text\": `\"${translations.canvas.dropHere}\"`,\n }),\n keyboardOptions: {\n onBeforeUndo: () => collabWarningRef?.showCollabUndoWarning(),\n },\n });\n\n // --- 7. Collab undo warning ---\n const collabWarning = useCollabUndoWarning({\n isCollaborationEnabled,\n getCollaboratorCount: () => collaboration?.collaborators.value.length ?? 0,\n canUndo: core.history.canUndo,\n });\n collabWarningRef = collabWarning;\n\n // --- 8. Snapshot preview ---\n const snapshotPreview = useSnapshotPreview({\n authManager,\n editor,\n history: core.history,\n conditionPreview: core.conditionPreview,\n autoSave: core.autoSave,\n onError: config.onError,\n });\n snapshotPreviewRef = snapshotPreview;\n\n // --- 9. Remaining cloud composables ---\n const panelState = useCloudPanelState();\n const aiConfig = useAiConfig(config.ai);\n\n const featureFlags = useCloudFeatureFlags({\n planConfigInstance,\n aiConfig,\n editor,\n });\n\n const mediaLib = useCloudMediaLibrary({\n onRequestMedia: config.onRequestMedia,\n mediaLibraryOpen: panelState.mediaLibraryOpen,\n mediaLibraryAccept: panelState.mediaLibraryAccept,\n });\n\n // Install drag-drop listeners (no return value needed).\n useDragDrop({\n onBlockMove: editor.moveBlock,\n onBlockAdd: editor.addBlock,\n });\n\n const exporter = useExport({\n authManager,\n getFontsConfig: () => config.fonts,\n canUseCustomFonts: () => planConfigInstance.hasFeature(\"custom_fonts\"),\n });\n\n const testEmail = useTestEmail({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n save: () => editor.save(),\n exportHtml: (templateId: string) => exporter.exportHtml(templateId),\n onError: config.onError,\n isAuthReady,\n onBeforeTestEmail: config.onBeforeTestEmail,\n });\n\n const commentsInstance = useComments({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n getSocketId: () => websocket.getSocketId(),\n onComment: config.onComment,\n onError: config.onError,\n isAuthReady,\n hasCommentingFeature: () =>\n config.commenting !== false &&\n planConfigInstance.hasFeature(\"commenting\"),\n });\n\n useCommentListener({\n comments: commentsInstance,\n channel: websocket.channel,\n });\n\n const savedModulesHeadless = useSavedModules({\n authManager,\n onError: config.onError,\n });\n const showSaveModuleDialog = ref(false);\n const saveModulePreSelectedBlockId = ref<string | null>(null);\n const showModuleBrowserModal = ref(false);\n\n const scoringInstance = useTemplateScoring({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n });\n\n // --- Comments block-filter bridge ---\n function openCommentsForBlock(blockId: string): void {\n panelState.commentsOpen.value = true;\n // Sidebar may not be mounted yet (async component); defer look-up.\n queueMicrotask(() => {\n getCommentsSidebar()?.filterByBlock(blockId);\n });\n }\n\n // --- 10. Cloud-only provides ---\n provide(ON_REQUEST_MEDIA_KEY, mediaLib.handleRequestMedia);\n provide(AUTH_MANAGER_KEY, authManager);\n provide(AI_CONFIG_KEY, aiConfig);\n provide(COMMENTS_KEY, commentsInstance);\n provide(SAVED_MODULES_HEADLESS_KEY, savedModulesHeadless);\n provide(SCORING_KEY, scoringInstance);\n\n // Override default capabilities from useEditorCore with cloud capabilities.\n provide(CAPABILITIES_KEY, {\n plan: planConfigInstance,\n ai: aiConfig,\n comments: {\n getBlockCount: (blockId: string) =>\n commentsInstance.commentCountByBlock.value.get(blockId) ?? 0,\n openForBlock: openCommentsForBlock,\n },\n savedModules: {\n openSaveDialog: (blockId: string) => {\n saveModulePreSelectedBlockId.value = blockId ?? null;\n showSaveModuleDialog.value = true;\n },\n openBrowser: () => {\n showModuleBrowserModal.value = true;\n },\n moduleCount: computed(() => savedModulesHeadless.modules.value.length),\n },\n } satisfies EditorCapabilities);\n\n // --- Theme setters (plan-gated) ---\n function setThemeOverrides(overrides: ThemeOverrides): void {\n if (!planConfigInstance.hasFeature(\"theme_customization\")) return;\n core.themeOverrides.value = overrides;\n }\n\n function setUiTheme(theme: UiTheme): void {\n editor.setUiTheme(theme);\n }\n\n // --- Initialize (async bootstrap) ---\n async function initialize(): Promise<void> {\n isInitializing.value = true;\n initError.value = null;\n\n try {\n await authManager.initialize();\n if (_destroyed) return;\n isAuthReady.value = true;\n\n const healthResult = await performHealthCheck({ authManager });\n if (_destroyed) return;\n\n if (!healthResult.api.ok) {\n throw new Error(\"Health check failed: API is not reachable\");\n }\n\n if (!healthResult.auth.ok) {\n throw new Error(\n `Health check failed: authentication error${healthResult.auth.error ? ` - ${healthResult.auth.error}` : \"\"}`,\n );\n }\n\n if (!healthResult.websocket.ok) {\n logger.warn(\n \"WebSocket health check failed:\",\n healthResult.websocket.error ?? \"unknown error\",\n \"-- real-time features will be disabled.\",\n );\n }\n\n await planConfigInstance.fetchConfig();\n if (_destroyed) return;\n\n fontsManager.setCustomFontsEnabled(\n planConfigInstance.hasFeature(\"custom_fonts\"),\n );\n\n if (\n config.customBlocks?.length &&\n planConfigInstance.hasFeature(\"custom_blocks\")\n ) {\n core.registerCustomBlocks(config.customBlocks);\n }\n\n if (\n config.theme &&\n planConfigInstance.hasFeature(\"theme_customization\")\n ) {\n core.themeOverrides.value = config.theme;\n }\n\n if (\n config.modules !== false &&\n planConfigInstance.hasFeature(\"saved_modules\")\n ) {\n savedModulesHeadless.loadModules();\n }\n\n emit(\"ready\");\n } catch (error) {\n if (_destroyed) return;\n const wrappedError =\n error instanceof Error\n ? error\n : new Error(\"Initialization failed\", { cause: error });\n initError.value = wrappedError;\n config.onError?.(wrappedError);\n } finally {\n if (!_destroyed) {\n isInitializing.value = false;\n }\n }\n }\n\n function destroy(): void {\n _destroyed = true;\n fontsManager.cleanupFontLinks();\n websocket.disconnect();\n core.destroy();\n config.onUnmount?.();\n }\n\n return {\n isInitializing,\n isAuthReady,\n initError,\n isDestroyed: () => _destroyed,\n\n authManager,\n planConfigInstance,\n websocket,\n collaboration,\n isCollaborationEnabled,\n\n editor,\n core,\n\n aiConfig,\n featureFlags,\n mediaLib,\n exporter,\n testEmail,\n commentsInstance,\n savedModulesHeadless,\n scoringInstance,\n panelState,\n snapshotPreview,\n collabWarning,\n\n showSaveModuleDialog,\n showModuleBrowserModal,\n saveModulePreSelectedBlockId,\n\n onSaveHook,\n\n initialize,\n destroy,\n setThemeOverrides,\n setUiTheme,\n openCommentsForBlock,\n };\n}\n","import type { Block, CustomBlock, TemplateContent } from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport type { UseBlockRegistryReturn } from \"../composables/useBlockRegistry\";\n\n/**\n * Renders every custom block in the content tree to its HTML representation\n * and stores the result on `block.renderedHtml`. Called before save so the\n * backend can include custom-block output in its MJML export.\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: UseBlockRegistryReturn,\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","import {\n resolveWebSocketConfig,\n type UseExportReturn,\n type UsePlanConfigReturn,\n type UseWebSocketReturn,\n type UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\nimport type { SaveResult, Template, TemplateContent } from \"@templatical/types\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport { preRenderCustomBlocks } from \"../../utils/preRenderCustomBlocks\";\n\nimport type { UseSnapshotPreviewReturn } from \"./useSnapshotPreview\";\nimport type { UseCloudFeatureFlagsReturn } from \"./useCloudFeatureFlags\";\nimport type { TemplaticalCloudEditorConfig } from \"../cloudConfig\";\n\nexport interface UseCloudLifecycleOptions {\n config: TemplaticalCloudEditorConfig;\n editor: CloudUseEditorReturn;\n websocket: UseWebSocketReturn;\n planConfigInstance: UsePlanConfigReturn;\n snapshotPreview: UseSnapshotPreviewReturn;\n core: UseEditorCoreReturn;\n exporter: UseExportReturn;\n featureFlags: UseCloudFeatureFlagsReturn;\n isDestroyed: () => boolean;\n}\n\nexport interface UseCloudLifecycleReturn {\n createTemplate: (content?: TemplateContent) => Promise<Template>;\n loadTemplate: (templateId: string) => Promise<Template>;\n saveTemplate: () => Promise<SaveResult>;\n}\n\n/**\n * Template-level operations on a cloud editor: create/load/save.\n * Each guards against post-unmount execution via `isDestroyed()`.\n */\nexport function useCloudLifecycle(\n options: UseCloudLifecycleOptions,\n): UseCloudLifecycleReturn {\n const {\n config,\n editor,\n websocket,\n planConfigInstance,\n snapshotPreview,\n core,\n exporter,\n featureFlags,\n isDestroyed,\n } = options;\n\n function getWebSocketConfig() {\n return resolveWebSocketConfig(planConfigInstance.config.value!.websocket);\n }\n\n async function createTemplate(content?: TemplateContent): Promise<Template> {\n const template = await editor.create(content);\n if (isDestroyed()) return template;\n config.onCreate?.(template);\n snapshotPreview.initSnapshotHistory();\n websocket.connect(template.id, getWebSocketConfig());\n return template;\n }\n\n async function loadTemplate(templateId: string): Promise<Template> {\n const template = await editor.load(templateId);\n if (isDestroyed()) return template;\n config.onLoad?.(template);\n snapshotPreview.initSnapshotHistory();\n websocket.connect(template.id, getWebSocketConfig());\n return template;\n }\n\n async function saveTemplate(): Promise<SaveResult> {\n featureFlags.isSaveExporting.value = true;\n featureFlags.saveStatus.value = \"idle\";\n try {\n // Pre-render custom blocks so backend can include them in MJML export.\n await preRenderCustomBlocks(editor.content.value, core.registry);\n if (isDestroyed()) throw new Error(\"Component unmounted during save\");\n\n const template = await editor.save();\n if (isDestroyed()) throw new Error(\"Component unmounted during save\");\n\n snapshotPreview.initSnapshotHistory();\n snapshotPreview.snapshotHistoryInstance.value?.loadSnapshots();\n\n const exportResult = await exporter.exportHtml(template.id);\n if (isDestroyed()) throw new Error(\"Component unmounted during save\");\n\n const saveResult: SaveResult = {\n templateId: template.id,\n html: exportResult.html,\n mjml: exportResult.mjml,\n content: template.content,\n };\n\n config.onSave?.(saveResult);\n\n featureFlags.saveStatus.value = \"saved\";\n featureFlags.startSaveStatusClear();\n\n return saveResult;\n } catch (error) {\n if (!isDestroyed()) {\n featureFlags.saveStatus.value = \"error\";\n featureFlags.saveErrorMessage.value =\n error instanceof Error ? error.message : \"Save failed\";\n }\n throw error;\n } finally {\n if (!isDestroyed()) {\n featureFlags.isSaveExporting.value = false;\n }\n }\n }\n\n return { createTemplate, loadTemplate, saveTemplate };\n}\n","<script setup lang=\"ts\">\nimport {\n Check,\n CircleAlert,\n LoaderCircle,\n MessageCircle,\n Save,\n Send,\n Sparkles,\n} from \"@lucide/vue\";\nimport { defineAsyncComponent } from \"vue\";\nimport type {\n AuthManager,\n UseCommentsReturn,\n UseTestEmailReturn,\n UseWebSocketReturn,\n UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport { headerBtnClass } from \"../../constants/styleConstants\";\nimport ViewportToggle from \"../../components/ViewportToggle.vue\";\nimport PreviewToggle from \"../../components/PreviewToggle.vue\";\nimport DarkModeToggle from \"../../components/DarkModeToggle.vue\";\nimport type { UseCloudFeatureFlagsReturn } from \"../composables/useCloudFeatureFlags\";\nimport type { UseCloudPanelStateReturn } from \"../composables/useCloudPanelState\";\nimport type { UseSnapshotPreviewReturn } from \"../composables/useSnapshotPreview\";\nimport type { UseCloudInitializationReturn } from \"../composables/useCloudInitialization\";\n\nconst CollaboratorBar = defineAsyncComponent(\n () => import(\"./CollaboratorBar.vue\"),\n);\nconst SnapshotHistory = defineAsyncComponent(\n () => import(\"./SnapshotHistory.vue\"),\n);\nconst AiFeatureMenu = defineAsyncComponent(() => import(\"./AiFeatureMenu.vue\"));\n\ndefineProps<{\n editor: CloudUseEditorReturn;\n core: UseEditorCoreReturn;\n authManager: AuthManager;\n featureFlags: UseCloudFeatureFlagsReturn;\n panelState: UseCloudPanelStateReturn;\n snapshotPreview: UseSnapshotPreviewReturn;\n commentsInstance: UseCommentsReturn;\n testEmail: UseTestEmailReturn;\n websocket: UseWebSocketReturn;\n collaboration: UseCloudInitializationReturn[\"collaboration\"];\n isCollaborationEnabled: boolean;\n isSaveDisabled: boolean;\n isSaving: boolean;\n}>();\n\ndefineEmits<{\n (e: \"save\"): void;\n}>();\n</script>\n\n<template>\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n box-shadow: var(--tpl-shadow-md);\n border-bottom: 1px solid var(--tpl-border);\n \"\n >\n <!-- Left: Logo + template count -->\n <div\n class=\"tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3\"\n >\n <div\n v-if=\"!featureFlags.isWhiteLabeled.value\"\n class=\"tpl-logo tpl:flex tpl:items-center tpl:gap-2.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <img\n :src=\"authManager.resolveUrl('/logo.svg')\"\n alt=\"Templatical\"\n width=\"24\"\n height=\"24\"\n class=\"tpl:shrink-0\"\n />\n <span style=\"letter-spacing: -0.01em\">{{ core.t.header.title }}</span>\n </div>\n <span\n v-if=\"featureFlags.templateLimit.value !== null\"\n class=\"tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n core.format(core.t.header.templatesUsed, {\n used: featureFlags.templateCount.value,\n max: featureFlags.templateLimit.value,\n })\n }}\n </span>\n </div>\n\n <!-- Center: viewport + preview + dark mode + collaboration + snapshots -->\n <div\n class=\"tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\"\n >\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n <CollaboratorBar\n v-if=\"collaboration && isCollaborationEnabled\"\n :collaborators=\"collaboration.collaborators.value\"\n :is-connected=\"websocket.isConnected.value\"\n />\n <SnapshotHistory\n v-if=\"snapshotPreview.snapshotHistoryInstance.value\"\n :snapshots=\"snapshotPreview.snapshotHistorySnapshots.value\"\n :is-loading=\"snapshotPreview.snapshotHistoryIsLoading.value\"\n :is-restoring=\"snapshotPreview.snapshotHistoryIsRestoring.value\"\n @load=\"snapshotPreview.loadSnapshotHistory\"\n @navigate=\"snapshotPreview.handleSnapshotNavigate\"\n />\n </div>\n\n <!-- Right: Cloud actions -->\n <div\n class=\"tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n >\n <!-- Save status indicator -->\n <div\n v-if=\"featureFlags.saveStatus.value === 'error'\"\n aria-live=\"assertive\"\n class=\"tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n :data-tooltip=\"featureFlags.saveErrorMessage.value\"\n >\n <CircleAlert :size=\"12\" :stroke-width=\"2.5\" />\n {{ core.t.header.saveFailed }}\n </div>\n <div\n v-else-if=\"featureFlags.saveStatus.value === 'saved'\"\n aria-live=\"polite\"\n class=\"tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]\"\n >\n <Check :size=\"12\" :stroke-width=\"2.5\" />\n {{ core.t.header.saved }}\n </div>\n <div\n v-else-if=\"editor.state.isDirty\"\n aria-live=\"polite\"\n class=\"tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <span\n class=\"tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]\"\n ></span>\n {{ core.t.header.unsaved }}\n </div>\n\n <!-- Comments button -->\n <button\n v-if=\"\n commentsInstance.isEnabled.value &&\n featureFlags.hasTemplateSaved.value\n \"\n :aria-label=\"\n commentsInstance.unresolvedCount.value > 0\n ? `${core.t.comments.button} (${commentsInstance.unresolvedCount.value})`\n : core.t.comments.button\n \"\n :aria-expanded=\"panelState.commentsOpen.value\"\n :class=\"headerBtnClass\"\n :style=\"{\n backgroundColor: panelState.commentsOpen.value\n ? 'var(--tpl-primary)'\n : 'transparent',\n color: panelState.commentsOpen.value\n ? 'var(--tpl-bg)'\n : 'var(--tpl-primary)',\n borderColor: 'var(--tpl-primary)',\n }\"\n @click=\"panelState.commentsOpen.value = !panelState.commentsOpen.value\"\n >\n <MessageCircle :size=\"16\" :stroke-width=\"2\" />\n {{ core.t.comments.button }}\n <span\n v-if=\"\n commentsInstance.unresolvedCount.value > 0 &&\n !panelState.commentsOpen.value\n \"\n class=\"tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n >\n {{ commentsInstance.unresolvedCount.value }}\n </span>\n </button>\n\n <!-- AI button + menu -->\n <div\n v-if=\"\n featureFlags.canUseAiGeneration.value &&\n featureFlags.hasTemplateSaved.value\n \"\n :ref=\"(el) => (panelState.aiMenuRef.value = el as HTMLElement | null)\"\n class=\"tpl:relative\"\n >\n <button\n :aria-expanded=\"panelState.aiMenuOpen.value\"\n class=\"tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200\"\n :class=\"\n panelState.aiButtonActive.value\n ? 'tpl-ai-btn--active'\n : 'tpl-ai-btn--idle'\n \"\n @click.stop=\"panelState.toggleAiMenu\"\n >\n <Sparkles :size=\"16\" :stroke-width=\"2\" class=\"tpl-ai-btn-icon\" />\n {{ core.t.aiChat.button }}\n </button>\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-150 tpl:ease-out\"\n enter-from-class=\"tpl:scale-95 tpl:opacity-0\"\n enter-to-class=\"tpl:scale-100 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-100 tpl:ease-in\"\n leave-from-class=\"tpl:scale-100 tpl:opacity-100\"\n leave-to-class=\"tpl:scale-95 tpl:opacity-0\"\n >\n <div\n v-if=\"panelState.aiMenuOpen.value\"\n class=\"tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right\"\n >\n <AiFeatureMenu\n :active-feature=\"panelState.activeAiFeature.value\"\n @select=\"panelState.handleAiFeatureSelect\"\n />\n </div>\n </Transition>\n </div>\n\n <!-- Test email button -->\n <button\n v-if=\"testEmail.isEnabled.value && featureFlags.canSendTestEmail.value\"\n :class=\"headerBtnClass\"\n style=\"\n background-color: transparent;\n color: var(--tpl-primary);\n border-color: var(--tpl-primary);\n \"\n :disabled=\"\n testEmail.isSending.value || !featureFlags.hasTemplateSaved.value\n \"\n @click=\"panelState.testEmailModalOpen.value = true\"\n >\n <Send v-if=\"!testEmail.isSending.value\" :size=\"16\" :stroke-width=\"2\" />\n <LoaderCircle v-else class=\"tpl-spinner\" :size=\"16\" :stroke-width=\"2\" />\n {{ core.t.testEmail.button }}\n </button>\n\n <!-- Save button -->\n <button\n :class=\"headerBtnClass\"\n style=\"\n background-color: transparent;\n color: var(--tpl-primary);\n border-color: var(--tpl-primary);\n \"\n :disabled=\"isSaveDisabled\"\n @click=\"$emit('save')\"\n >\n <Save v-if=\"!isSaving\" :size=\"16\" :stroke-width=\"2\" />\n <LoaderCircle v-else class=\"tpl-spinner\" :size=\"16\" :stroke-width=\"2\" />\n {{ isSaving ? core.t.header.saving : core.t.header.save }}\n </button>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport {\n Check,\n CircleAlert,\n LoaderCircle,\n MessageCircle,\n Save,\n Send,\n Sparkles,\n} from \"@lucide/vue\";\nimport { defineAsyncComponent } from \"vue\";\nimport type {\n AuthManager,\n UseCommentsReturn,\n UseTestEmailReturn,\n UseWebSocketReturn,\n UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport { headerBtnClass } from \"../../constants/styleConstants\";\nimport ViewportToggle from \"../../components/ViewportToggle.vue\";\nimport PreviewToggle from \"../../components/PreviewToggle.vue\";\nimport DarkModeToggle from \"../../components/DarkModeToggle.vue\";\nimport type { UseCloudFeatureFlagsReturn } from \"../composables/useCloudFeatureFlags\";\nimport type { UseCloudPanelStateReturn } from \"../composables/useCloudPanelState\";\nimport type { UseSnapshotPreviewReturn } from \"../composables/useSnapshotPreview\";\nimport type { UseCloudInitializationReturn } from \"../composables/useCloudInitialization\";\n\nconst CollaboratorBar = defineAsyncComponent(\n () => import(\"./CollaboratorBar.vue\"),\n);\nconst SnapshotHistory = defineAsyncComponent(\n () => import(\"./SnapshotHistory.vue\"),\n);\nconst AiFeatureMenu = defineAsyncComponent(() => import(\"./AiFeatureMenu.vue\"));\n\ndefineProps<{\n editor: CloudUseEditorReturn;\n core: UseEditorCoreReturn;\n authManager: AuthManager;\n featureFlags: UseCloudFeatureFlagsReturn;\n panelState: UseCloudPanelStateReturn;\n snapshotPreview: UseSnapshotPreviewReturn;\n commentsInstance: UseCommentsReturn;\n testEmail: UseTestEmailReturn;\n websocket: UseWebSocketReturn;\n collaboration: UseCloudInitializationReturn[\"collaboration\"];\n isCollaborationEnabled: boolean;\n isSaveDisabled: boolean;\n isSaving: boolean;\n}>();\n\ndefineEmits<{\n (e: \"save\"): void;\n}>();\n</script>\n\n<template>\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n box-shadow: var(--tpl-shadow-md);\n border-bottom: 1px solid var(--tpl-border);\n \"\n >\n <!-- Left: Logo + template count -->\n <div\n class=\"tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3\"\n >\n <div\n v-if=\"!featureFlags.isWhiteLabeled.value\"\n class=\"tpl-logo tpl:flex tpl:items-center tpl:gap-2.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <img\n :src=\"authManager.resolveUrl('/logo.svg')\"\n alt=\"Templatical\"\n width=\"24\"\n height=\"24\"\n class=\"tpl:shrink-0\"\n />\n <span style=\"letter-spacing: -0.01em\">{{ core.t.header.title }}</span>\n </div>\n <span\n v-if=\"featureFlags.templateLimit.value !== null\"\n class=\"tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n core.format(core.t.header.templatesUsed, {\n used: featureFlags.templateCount.value,\n max: featureFlags.templateLimit.value,\n })\n }}\n </span>\n </div>\n\n <!-- Center: viewport + preview + dark mode + collaboration + snapshots -->\n <div\n class=\"tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\"\n >\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n <CollaboratorBar\n v-if=\"collaboration && isCollaborationEnabled\"\n :collaborators=\"collaboration.collaborators.value\"\n :is-connected=\"websocket.isConnected.value\"\n />\n <SnapshotHistory\n v-if=\"snapshotPreview.snapshotHistoryInstance.value\"\n :snapshots=\"snapshotPreview.snapshotHistorySnapshots.value\"\n :is-loading=\"snapshotPreview.snapshotHistoryIsLoading.value\"\n :is-restoring=\"snapshotPreview.snapshotHistoryIsRestoring.value\"\n @load=\"snapshotPreview.loadSnapshotHistory\"\n @navigate=\"snapshotPreview.handleSnapshotNavigate\"\n />\n </div>\n\n <!-- Right: Cloud actions -->\n <div\n class=\"tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n >\n <!-- Save status indicator -->\n <div\n v-if=\"featureFlags.saveStatus.value === 'error'\"\n aria-live=\"assertive\"\n class=\"tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n :data-tooltip=\"featureFlags.saveErrorMessage.value\"\n >\n <CircleAlert :size=\"12\" :stroke-width=\"2.5\" />\n {{ core.t.header.saveFailed }}\n </div>\n <div\n v-else-if=\"featureFlags.saveStatus.value === 'saved'\"\n aria-live=\"polite\"\n class=\"tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]\"\n >\n <Check :size=\"12\" :stroke-width=\"2.5\" />\n {{ core.t.header.saved }}\n </div>\n <div\n v-else-if=\"editor.state.isDirty\"\n aria-live=\"polite\"\n class=\"tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <span\n class=\"tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]\"\n ></span>\n {{ core.t.header.unsaved }}\n </div>\n\n <!-- Comments button -->\n <button\n v-if=\"\n commentsInstance.isEnabled.value &&\n featureFlags.hasTemplateSaved.value\n \"\n :aria-label=\"\n commentsInstance.unresolvedCount.value > 0\n ? `${core.t.comments.button} (${commentsInstance.unresolvedCount.value})`\n : core.t.comments.button\n \"\n :aria-expanded=\"panelState.commentsOpen.value\"\n :class=\"headerBtnClass\"\n :style=\"{\n backgroundColor: panelState.commentsOpen.value\n ? 'var(--tpl-primary)'\n : 'transparent',\n color: panelState.commentsOpen.value\n ? 'var(--tpl-bg)'\n : 'var(--tpl-primary)',\n borderColor: 'var(--tpl-primary)',\n }\"\n @click=\"panelState.commentsOpen.value = !panelState.commentsOpen.value\"\n >\n <MessageCircle :size=\"16\" :stroke-width=\"2\" />\n {{ core.t.comments.button }}\n <span\n v-if=\"\n commentsInstance.unresolvedCount.value > 0 &&\n !panelState.commentsOpen.value\n \"\n class=\"tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n >\n {{ commentsInstance.unresolvedCount.value }}\n </span>\n </button>\n\n <!-- AI button + menu -->\n <div\n v-if=\"\n featureFlags.canUseAiGeneration.value &&\n featureFlags.hasTemplateSaved.value\n \"\n :ref=\"(el) => (panelState.aiMenuRef.value = el as HTMLElement | null)\"\n class=\"tpl:relative\"\n >\n <button\n :aria-expanded=\"panelState.aiMenuOpen.value\"\n class=\"tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200\"\n :class=\"\n panelState.aiButtonActive.value\n ? 'tpl-ai-btn--active'\n : 'tpl-ai-btn--idle'\n \"\n @click.stop=\"panelState.toggleAiMenu\"\n >\n <Sparkles :size=\"16\" :stroke-width=\"2\" class=\"tpl-ai-btn-icon\" />\n {{ core.t.aiChat.button }}\n </button>\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-150 tpl:ease-out\"\n enter-from-class=\"tpl:scale-95 tpl:opacity-0\"\n enter-to-class=\"tpl:scale-100 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-100 tpl:ease-in\"\n leave-from-class=\"tpl:scale-100 tpl:opacity-100\"\n leave-to-class=\"tpl:scale-95 tpl:opacity-0\"\n >\n <div\n v-if=\"panelState.aiMenuOpen.value\"\n class=\"tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right\"\n >\n <AiFeatureMenu\n :active-feature=\"panelState.activeAiFeature.value\"\n @select=\"panelState.handleAiFeatureSelect\"\n />\n </div>\n </Transition>\n </div>\n\n <!-- Test email button -->\n <button\n v-if=\"testEmail.isEnabled.value && featureFlags.canSendTestEmail.value\"\n :class=\"headerBtnClass\"\n style=\"\n background-color: transparent;\n color: var(--tpl-primary);\n border-color: var(--tpl-primary);\n \"\n :disabled=\"\n testEmail.isSending.value || !featureFlags.hasTemplateSaved.value\n \"\n @click=\"panelState.testEmailModalOpen.value = true\"\n >\n <Send v-if=\"!testEmail.isSending.value\" :size=\"16\" :stroke-width=\"2\" />\n <LoaderCircle v-else class=\"tpl-spinner\" :size=\"16\" :stroke-width=\"2\" />\n {{ core.t.testEmail.button }}\n </button>\n\n <!-- Save button -->\n <button\n :class=\"headerBtnClass\"\n style=\"\n background-color: transparent;\n color: var(--tpl-primary);\n border-color: var(--tpl-primary);\n \"\n :disabled=\"isSaveDisabled\"\n @click=\"$emit('save')\"\n >\n <Save v-if=\"!isSaving\" :size=\"16\" :stroke-width=\"2\" />\n <LoaderCircle v-else class=\"tpl-spinner\" :size=\"16\" :stroke-width=\"2\" />\n {{ isSaving ? core.t.header.saving : core.t.header.save }}\n </button>\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent, ref } from \"vue\";\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport type {\n UsePlanConfigReturn,\n UseTestEmailReturn,\n UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { UseCloudPanelStateReturn } from \"../composables/useCloudPanelState\";\nimport type { UseCloudMediaLibraryReturn } from \"../composables/useCloudMediaLibrary\";\nimport type { UseCloudInitializationReturn } from \"../composables/useCloudInitialization\";\nimport type { TemplaticalCloudEditorConfig } from \"../cloudConfig\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst CommentsSidebar = defineAsyncComponent(\n () => import(\"./CommentsSidebar.vue\"),\n);\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst TestEmailModal = defineAsyncComponent(\n () => import(\"./TestEmailModal.vue\"),\n);\nconst SaveModuleDialog = defineAsyncComponent(\n () => import(\"./SaveModuleDialog.vue\"),\n);\nconst ModuleBrowserModal = defineAsyncComponent(\n () => import(\"./ModuleBrowserModal.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n});\n\ndefineProps<{\n config: TemplaticalCloudEditorConfig;\n editor: CloudUseEditorReturn;\n core: UseEditorCoreReturn;\n panelState: UseCloudPanelStateReturn;\n planConfigInstance: UsePlanConfigReturn;\n testEmail: UseTestEmailReturn;\n mediaLib: UseCloudMediaLibraryReturn;\n savedModulesHeadless: UseCloudInitializationReturn[\"savedModulesHeadless\"];\n showSaveModuleDialog: boolean;\n saveModulePreSelectedBlockId: string | null;\n showModuleBrowserModal: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:showSaveModuleDialog\", value: boolean): void;\n (e: \"update:saveModulePreSelectedBlockId\", value: string | null): void;\n (e: \"update:showModuleBrowserModal\", value: boolean): void;\n (e: \"send-test-email\", recipient: string): void;\n (\n e: \"module-insert\",\n module: { content: Block[] },\n insertIndex: number | undefined,\n ): void;\n}>();\n\nfunction applyContent(\n content: TemplateContent,\n core: UseEditorCoreReturn,\n editor: CloudUseEditorReturn,\n): void {\n core.history.record();\n editor.setContent(content);\n core.conditionPreview.reset();\n}\n\ninterface CommentsSidebarInstance {\n filterByBlock: (blockId: string) => void;\n}\n\nconst commentsSidebar = ref<CommentsSidebarInstance | null>(null);\n\n/** Delegates to the CommentsSidebar's filterByBlock method once it's mounted. */\nfunction filterCommentsByBlock(blockId: string): void {\n commentsSidebar.value?.filterByBlock(blockId);\n}\n\ndefineExpose({ filterCommentsByBlock });\n</script>\n\n<template>\n <AiChatSidebar\n :visible=\"panelState.aiChatOpen.value\"\n :on-apply=\"(c: TemplateContent) => applyContent(c, core, editor)\"\n @close=\"panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"panelState.scoringPanelOpen.value\"\n @close=\"panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"panelState.designReferenceOpen.value = false\"\n @apply=\"(c: TemplateContent) => applyContent(c, core, editor)\"\n />\n\n <CommentsSidebar\n ref=\"commentsSidebar\"\n :visible=\"panelState.commentsOpen.value\"\n @close=\"panelState.commentsOpen.value = false\"\n />\n\n <TestEmailModal\n :visible=\"panelState.testEmailModalOpen.value\"\n :allowed-emails=\"testEmail.allowedEmails.value\"\n :is-sending=\"testEmail.isSending.value\"\n :error=\"testEmail.error.value\"\n @send=\"(recipient: string) => emit('send-test-email', recipient)\"\n @close=\"panelState.testEmailModalOpen.value = false\"\n />\n\n <SaveModuleDialog\n v-if=\"\n planConfigInstance.hasFeature('saved_modules') && config.modules !== false\n \"\n :visible=\"showSaveModuleDialog\"\n :pre-selected-block-id=\"saveModulePreSelectedBlockId\"\n @close=\"\n emit('update:showSaveModuleDialog', false);\n emit('update:saveModulePreSelectedBlockId', null);\n \"\n @saved=\"savedModulesHeadless.loadModules()\"\n />\n\n <ModuleBrowserModal\n v-if=\"\n planConfigInstance.hasFeature('saved_modules') && config.modules !== false\n \"\n :visible=\"showModuleBrowserModal\"\n @close=\"emit('update:showModuleBrowserModal', false)\"\n @insert=\"\n (mod: { content: Block[] }, idx: number | undefined) =>\n emit('module-insert', mod, idx)\n \"\n />\n\n <MediaLibraryModal\n :visible=\"panelState.mediaLibraryOpen.value\"\n :accept=\"panelState.mediaLibraryAccept.value\"\n @select=\"mediaLib.handleMediaSelect\"\n @close=\"mediaLib.handleMediaLibraryClose\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent, ref } from \"vue\";\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport type {\n UsePlanConfigReturn,\n UseTestEmailReturn,\n UseEditorReturn as CloudUseEditorReturn,\n} from \"@templatical/core/cloud\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { UseCloudPanelStateReturn } from \"../composables/useCloudPanelState\";\nimport type { UseCloudMediaLibraryReturn } from \"../composables/useCloudMediaLibrary\";\nimport type { UseCloudInitializationReturn } from \"../composables/useCloudInitialization\";\nimport type { TemplaticalCloudEditorConfig } from \"../cloudConfig\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst CommentsSidebar = defineAsyncComponent(\n () => import(\"./CommentsSidebar.vue\"),\n);\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst TestEmailModal = defineAsyncComponent(\n () => import(\"./TestEmailModal.vue\"),\n);\nconst SaveModuleDialog = defineAsyncComponent(\n () => import(\"./SaveModuleDialog.vue\"),\n);\nconst ModuleBrowserModal = defineAsyncComponent(\n () => import(\"./ModuleBrowserModal.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n});\n\ndefineProps<{\n config: TemplaticalCloudEditorConfig;\n editor: CloudUseEditorReturn;\n core: UseEditorCoreReturn;\n panelState: UseCloudPanelStateReturn;\n planConfigInstance: UsePlanConfigReturn;\n testEmail: UseTestEmailReturn;\n mediaLib: UseCloudMediaLibraryReturn;\n savedModulesHeadless: UseCloudInitializationReturn[\"savedModulesHeadless\"];\n showSaveModuleDialog: boolean;\n saveModulePreSelectedBlockId: string | null;\n showModuleBrowserModal: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:showSaveModuleDialog\", value: boolean): void;\n (e: \"update:saveModulePreSelectedBlockId\", value: string | null): void;\n (e: \"update:showModuleBrowserModal\", value: boolean): void;\n (e: \"send-test-email\", recipient: string): void;\n (\n e: \"module-insert\",\n module: { content: Block[] },\n insertIndex: number | undefined,\n ): void;\n}>();\n\nfunction applyContent(\n content: TemplateContent,\n core: UseEditorCoreReturn,\n editor: CloudUseEditorReturn,\n): void {\n core.history.record();\n editor.setContent(content);\n core.conditionPreview.reset();\n}\n\ninterface CommentsSidebarInstance {\n filterByBlock: (blockId: string) => void;\n}\n\nconst commentsSidebar = ref<CommentsSidebarInstance | null>(null);\n\n/** Delegates to the CommentsSidebar's filterByBlock method once it's mounted. */\nfunction filterCommentsByBlock(blockId: string): void {\n commentsSidebar.value?.filterByBlock(blockId);\n}\n\ndefineExpose({ filterCommentsByBlock });\n</script>\n\n<template>\n <AiChatSidebar\n :visible=\"panelState.aiChatOpen.value\"\n :on-apply=\"(c: TemplateContent) => applyContent(c, core, editor)\"\n @close=\"panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"panelState.scoringPanelOpen.value\"\n @close=\"panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"panelState.designReferenceOpen.value = false\"\n @apply=\"(c: TemplateContent) => applyContent(c, core, editor)\"\n />\n\n <CommentsSidebar\n ref=\"commentsSidebar\"\n :visible=\"panelState.commentsOpen.value\"\n @close=\"panelState.commentsOpen.value = false\"\n />\n\n <TestEmailModal\n :visible=\"panelState.testEmailModalOpen.value\"\n :allowed-emails=\"testEmail.allowedEmails.value\"\n :is-sending=\"testEmail.isSending.value\"\n :error=\"testEmail.error.value\"\n @send=\"(recipient: string) => emit('send-test-email', recipient)\"\n @close=\"panelState.testEmailModalOpen.value = false\"\n />\n\n <SaveModuleDialog\n v-if=\"\n planConfigInstance.hasFeature('saved_modules') && config.modules !== false\n \"\n :visible=\"showSaveModuleDialog\"\n :pre-selected-block-id=\"saveModulePreSelectedBlockId\"\n @close=\"\n emit('update:showSaveModuleDialog', false);\n emit('update:saveModulePreSelectedBlockId', null);\n \"\n @saved=\"savedModulesHeadless.loadModules()\"\n />\n\n <ModuleBrowserModal\n v-if=\"\n planConfigInstance.hasFeature('saved_modules') && config.modules !== false\n \"\n :visible=\"showModuleBrowserModal\"\n @close=\"emit('update:showModuleBrowserModal', false)\"\n @insert=\"\n (mod: { content: Block[] }, idx: number | undefined) =>\n emit('module-insert', mod, idx)\n \"\n />\n\n <MediaLibraryModal\n :visible=\"panelState.mediaLibraryOpen.value\"\n :accept=\"panelState.mediaLibraryAccept.value\"\n @select=\"mediaLib.handleMediaSelect\"\n @close=\"mediaLib.handleMediaLibraryClose\"\n />\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return t.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return t.error.templateNotFound;\n }\n return t.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ t.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('retry')\"\n >\n {{ t.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return t.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return t.error.templateNotFound;\n }\n return t.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ t.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('retry')\"\n >\n {{ t.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Clock } from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <Clock\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-primary)]\"\n />\n <span>{{ t.snapshotPreview.message }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <button\n class=\"tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"emit('cancel')\"\n >\n {{ t.snapshotPreview.cancel }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ t.snapshotPreview.restore }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Clock } from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <Clock\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-primary)]\"\n />\n <span>{{ t.snapshotPreview.message }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <button\n class=\"tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"emit('cancel')\"\n >\n {{ t.snapshotPreview.cancel }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ t.snapshotPreview.restore }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport { cloneBlock } from \"@templatical/types\";\n\nimport type { UseFontsReturn } from \"../composables/useFonts\";\nimport { onMounted, onUnmounted, ref } from \"vue\";\nimport { RotateCcw } from \"@lucide/vue\";\nimport type { Translations } from \"../i18n\";\n\nimport { useCloudInitialization } from \"./composables/useCloudInitialization\";\nimport { useCloudLifecycle } from \"./composables/useCloudLifecycle\";\nimport CloudHeader from \"./components/CloudHeader.vue\";\nimport CloudPanels from \"./components/CloudPanels.vue\";\nimport EditorFooter from \"../components/EditorFooter.vue\";\n\nimport Canvas from \"../components/Canvas.vue\";\nimport Sidebar from \"../components/Sidebar.vue\";\nimport RightSidebar from \"../components/RightSidebar.vue\";\nimport CloudLoadingOverlay from \"./components/CloudLoadingOverlay.vue\";\nimport CloudErrorOverlay from \"./components/CloudErrorOverlay.vue\";\nimport SnapshotPreviewBanner from \"./components/SnapshotPreviewBanner.vue\";\nimport CollabUndoToast from \"./components/CollabUndoToast.vue\";\nimport \"../styles/index.css\";\n\nexport type { TemplaticalCloudEditorConfig } from \"./cloudConfig\";\nimport type { TemplaticalCloudEditorConfig } from \"./cloudConfig\";\n\nconst props = defineProps<{\n config: TemplaticalCloudEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n}>();\nconst emit = defineEmits<{\n (e: \"ready\"): void;\n}>();\n\n// Template ref for CloudPanels. Its `filterCommentsByBlock()` method is what\n// init.openCommentsForBlock() bridges to once the panel mounts.\nconst cloudPanelsRef = ref<{\n filterCommentsByBlock: (blockId: string) => void;\n} | null>(null);\n\nconst init = useCloudInitialization({\n config: props.config,\n translations: props.translations,\n fontsManager: props.fontsManager,\n emit,\n getCommentsSidebar: () =>\n cloudPanelsRef.value\n ? { filterByBlock: cloudPanelsRef.value.filterCommentsByBlock }\n : null,\n});\n\n// Destructure heavily-used members for template readability.\nconst {\n isInitializing,\n isAuthReady,\n initError,\n authManager,\n planConfigInstance,\n websocket,\n collaboration,\n isCollaborationEnabled,\n editor,\n core,\n featureFlags,\n mediaLib,\n exporter,\n testEmail,\n commentsInstance,\n savedModulesHeadless,\n panelState,\n snapshotPreview,\n collabWarning,\n showSaveModuleDialog,\n showModuleBrowserModal,\n saveModulePreSelectedBlockId,\n setThemeOverrides,\n setUiTheme,\n} = init;\n\n// ---------------------------------------------------------------------------\n// Test email handler\n// ---------------------------------------------------------------------------\n\nasync function handleSendTestEmail(recipient: string): Promise<void> {\n try {\n await testEmail.sendTestEmail(recipient);\n panelState.testEmailModalOpen.value = false;\n } catch {\n // Error is already handled in the composable\n }\n}\n\n// ---------------------------------------------------------------------------\n// Module insert handler\n// ---------------------------------------------------------------------------\n\nfunction handleModuleInsert(\n module: { content: Block[] },\n insertIndex: number | undefined,\n): void {\n for (let i = 0; i < module.content.length; i++) {\n const cloned = cloneBlock(module.content[i]);\n const position = insertIndex !== undefined ? insertIndex + i : undefined;\n editor.addBlock(cloned, undefined, undefined, position);\n }\n showModuleBrowserModal.value = false;\n}\n\n// ---------------------------------------------------------------------------\n// Template lifecycle (create/load/save)\n// ---------------------------------------------------------------------------\n\nconst lifecycle = useCloudLifecycle({\n config: props.config,\n editor,\n websocket,\n planConfigInstance,\n snapshotPreview,\n core,\n exporter,\n featureFlags,\n isDestroyed: init.isDestroyed,\n});\n\n// Route the keyboard Save hook to lifecycle.saveTemplate.\ninit.onSaveHook.value = lifecycle.saveTemplate;\n\n// ---------------------------------------------------------------------------\n// Lifecycle\n// ---------------------------------------------------------------------------\n\nonMounted(() => {\n init.initialize();\n});\n\nonUnmounted(() => {\n init.destroy();\n});\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: setUiTheme,\n setThemeOverrides: setThemeOverrides,\n create: lifecycle.createTemplate,\n load: lifecycle.loadTemplate,\n save: lifecycle.saveTemplate,\n sendTestEmail: testEmail.sendTestEmail,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Loading overlay -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay\n :visible=\"isInitializing || editor.state.isLoading\"\n />\n </Transition>\n\n <!-- Error overlay -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"initError\"\n :visible=\"!!initError && !isInitializing\"\n @retry=\"init.initialize\"\n />\n </Transition>\n\n <CloudHeader\n :editor=\"editor\"\n :core=\"core\"\n :auth-manager=\"authManager\"\n :feature-flags=\"featureFlags\"\n :panel-state=\"panelState\"\n :snapshot-preview=\"snapshotPreview\"\n :comments-instance=\"commentsInstance\"\n :test-email=\"testEmail\"\n :websocket=\"websocket\"\n :collaboration=\"collaboration\"\n :is-collaboration-enabled=\"isCollaborationEnabled\"\n :is-saving=\"editor.state.isSaving || featureFlags.isSaveExporting.value\"\n :is-save-disabled=\"\n editor.state.isSaving ||\n featureFlags.isSaveExporting.value ||\n !editor.state.isDirty\n \"\n @save=\"\n lifecycle\n .saveTemplate()\n .catch((err: Error) => props.config.onError?.(err))\n \"\n />\n\n <!-- Snapshot preview banner -->\n <SnapshotPreviewBanner\n :visible=\"snapshotPreview.isPreviewingSnapshot.value\"\n @cancel=\"snapshotPreview.cancelPreview\"\n @confirm=\"snapshotPreview.confirmRestoreSnapshot\"\n />\n\n <!-- Collaboration undo warning toast -->\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <!-- Left sidebar -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas body -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto\"\n style=\"\n transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);\n background-color: var(--tpl-canvas-bg);\n \"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : panelState.rightPanelOpen.value\n ? 'tpl:left-12 tpl:right-[680px]'\n : 'tpl:left-12 tpl:right-[320px]',\n snapshotPreview.isPreviewingSnapshot.value\n ? 'tpl:top-[104px]'\n : 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-warning);\n border-color: var(--tpl-warning);\n backdrop-filter: blur(8px);\n \"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <main class=\"tpl-main tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n :locked-blocks=\"collaboration?.lockedBlocks.value ?? undefined\"\n @select-block=\"editor.selectBlock\"\n @open-ai-chat=\"panelState.aiChatOpen.value = true\"\n @open-design-reference=\"panelState.designReferenceOpen.value = true\"\n />\n </main>\n </div>\n\n <EditorFooter\n v-if=\"!featureFlags.isWhiteLabeled.value\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : panelState.rightPanelOpen.value\n ? 'tpl:left-12 tpl:right-[680px]'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n :shifted-left=\"panelState.rightPanelOpen.value\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.selectedBlock.value!.id, updates)\n \"\n @delete-block=\"\n core.blockActions.deleteBlock(editor.selectedBlock.value!.id)\n \"\n @duplicate-block=\"\n core.blockActions.duplicateBlock(editor.selectedBlock.value!)\n \"\n @update-settings=\"editor.updateSettings\"\n />\n\n <!-- Cloud sidebars + modals — only mount after cloud init completes -->\n <CloudPanels\n v-if=\"!isInitializing && isAuthReady\"\n ref=\"cloudPanelsRef\"\n :config=\"props.config\"\n :editor=\"editor\"\n :core=\"core\"\n :panel-state=\"panelState\"\n :plan-config-instance=\"planConfigInstance\"\n :test-email=\"testEmail\"\n :media-lib=\"mediaLib\"\n :saved-modules-headless=\"savedModulesHeadless\"\n :show-save-module-dialog=\"showSaveModuleDialog\"\n :save-module-pre-selected-block-id=\"saveModulePreSelectedBlockId\"\n :show-module-browser-modal=\"showModuleBrowserModal\"\n @update:show-save-module-dialog=\"showSaveModuleDialog = $event\"\n @update:save-module-pre-selected-block-id=\"\n saveModulePreSelectedBlockId = $event\n \"\n @update:show-module-browser-modal=\"showModuleBrowserModal = $event\"\n @send-test-email=\"handleSendTestEmail\"\n @module-insert=\"handleModuleInsert\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-restore-btn-enter-active {\n transition:\n opacity 200ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-restore-btn-leave-active {\n transition:\n opacity 150ms ease-in,\n transform 150ms ease-in;\n}\n\n.tpl-restore-btn-enter-from,\n.tpl-restore-btn-leave-to {\n opacity: 0;\n transform: translateY(-8px) scale(0.9);\n}\n\n.tpl-restore-btn-enter-to,\n.tpl-restore-btn-leave-from {\n opacity: 1;\n transform: translateY(0) scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport { cloneBlock } from \"@templatical/types\";\n\nimport type { UseFontsReturn } from \"../composables/useFonts\";\nimport { onMounted, onUnmounted, ref } from \"vue\";\nimport { RotateCcw } from \"@lucide/vue\";\nimport type { Translations } from \"../i18n\";\n\nimport { useCloudInitialization } from \"./composables/useCloudInitialization\";\nimport { useCloudLifecycle } from \"./composables/useCloudLifecycle\";\nimport CloudHeader from \"./components/CloudHeader.vue\";\nimport CloudPanels from \"./components/CloudPanels.vue\";\nimport EditorFooter from \"../components/EditorFooter.vue\";\n\nimport Canvas from \"../components/Canvas.vue\";\nimport Sidebar from \"../components/Sidebar.vue\";\nimport RightSidebar from \"../components/RightSidebar.vue\";\nimport CloudLoadingOverlay from \"./components/CloudLoadingOverlay.vue\";\nimport CloudErrorOverlay from \"./components/CloudErrorOverlay.vue\";\nimport SnapshotPreviewBanner from \"./components/SnapshotPreviewBanner.vue\";\nimport CollabUndoToast from \"./components/CollabUndoToast.vue\";\nimport \"../styles/index.css\";\n\nexport type { TemplaticalCloudEditorConfig } from \"./cloudConfig\";\nimport type { TemplaticalCloudEditorConfig } from \"./cloudConfig\";\n\nconst props = defineProps<{\n config: TemplaticalCloudEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n}>();\nconst emit = defineEmits<{\n (e: \"ready\"): void;\n}>();\n\n// Template ref for CloudPanels. Its `filterCommentsByBlock()` method is what\n// init.openCommentsForBlock() bridges to once the panel mounts.\nconst cloudPanelsRef = ref<{\n filterCommentsByBlock: (blockId: string) => void;\n} | null>(null);\n\nconst init = useCloudInitialization({\n config: props.config,\n translations: props.translations,\n fontsManager: props.fontsManager,\n emit,\n getCommentsSidebar: () =>\n cloudPanelsRef.value\n ? { filterByBlock: cloudPanelsRef.value.filterCommentsByBlock }\n : null,\n});\n\n// Destructure heavily-used members for template readability.\nconst {\n isInitializing,\n isAuthReady,\n initError,\n authManager,\n planConfigInstance,\n websocket,\n collaboration,\n isCollaborationEnabled,\n editor,\n core,\n featureFlags,\n mediaLib,\n exporter,\n testEmail,\n commentsInstance,\n savedModulesHeadless,\n panelState,\n snapshotPreview,\n collabWarning,\n showSaveModuleDialog,\n showModuleBrowserModal,\n saveModulePreSelectedBlockId,\n setThemeOverrides,\n setUiTheme,\n} = init;\n\n// ---------------------------------------------------------------------------\n// Test email handler\n// ---------------------------------------------------------------------------\n\nasync function handleSendTestEmail(recipient: string): Promise<void> {\n try {\n await testEmail.sendTestEmail(recipient);\n panelState.testEmailModalOpen.value = false;\n } catch {\n // Error is already handled in the composable\n }\n}\n\n// ---------------------------------------------------------------------------\n// Module insert handler\n// ---------------------------------------------------------------------------\n\nfunction handleModuleInsert(\n module: { content: Block[] },\n insertIndex: number | undefined,\n): void {\n for (let i = 0; i < module.content.length; i++) {\n const cloned = cloneBlock(module.content[i]);\n const position = insertIndex !== undefined ? insertIndex + i : undefined;\n editor.addBlock(cloned, undefined, undefined, position);\n }\n showModuleBrowserModal.value = false;\n}\n\n// ---------------------------------------------------------------------------\n// Template lifecycle (create/load/save)\n// ---------------------------------------------------------------------------\n\nconst lifecycle = useCloudLifecycle({\n config: props.config,\n editor,\n websocket,\n planConfigInstance,\n snapshotPreview,\n core,\n exporter,\n featureFlags,\n isDestroyed: init.isDestroyed,\n});\n\n// Route the keyboard Save hook to lifecycle.saveTemplate.\ninit.onSaveHook.value = lifecycle.saveTemplate;\n\n// ---------------------------------------------------------------------------\n// Lifecycle\n// ---------------------------------------------------------------------------\n\nonMounted(() => {\n init.initialize();\n});\n\nonUnmounted(() => {\n init.destroy();\n});\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: setUiTheme,\n setThemeOverrides: setThemeOverrides,\n create: lifecycle.createTemplate,\n load: lifecycle.loadTemplate,\n save: lifecycle.saveTemplate,\n sendTestEmail: testEmail.sendTestEmail,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Loading overlay -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay\n :visible=\"isInitializing || editor.state.isLoading\"\n />\n </Transition>\n\n <!-- Error overlay -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"initError\"\n :visible=\"!!initError && !isInitializing\"\n @retry=\"init.initialize\"\n />\n </Transition>\n\n <CloudHeader\n :editor=\"editor\"\n :core=\"core\"\n :auth-manager=\"authManager\"\n :feature-flags=\"featureFlags\"\n :panel-state=\"panelState\"\n :snapshot-preview=\"snapshotPreview\"\n :comments-instance=\"commentsInstance\"\n :test-email=\"testEmail\"\n :websocket=\"websocket\"\n :collaboration=\"collaboration\"\n :is-collaboration-enabled=\"isCollaborationEnabled\"\n :is-saving=\"editor.state.isSaving || featureFlags.isSaveExporting.value\"\n :is-save-disabled=\"\n editor.state.isSaving ||\n featureFlags.isSaveExporting.value ||\n !editor.state.isDirty\n \"\n @save=\"\n lifecycle\n .saveTemplate()\n .catch((err: Error) => props.config.onError?.(err))\n \"\n />\n\n <!-- Snapshot preview banner -->\n <SnapshotPreviewBanner\n :visible=\"snapshotPreview.isPreviewingSnapshot.value\"\n @cancel=\"snapshotPreview.cancelPreview\"\n @confirm=\"snapshotPreview.confirmRestoreSnapshot\"\n />\n\n <!-- Collaboration undo warning toast -->\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <!-- Left sidebar -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas body -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto\"\n style=\"\n transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);\n background-color: var(--tpl-canvas-bg);\n \"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : panelState.rightPanelOpen.value\n ? 'tpl:left-12 tpl:right-[680px]'\n : 'tpl:left-12 tpl:right-[320px]',\n snapshotPreview.isPreviewingSnapshot.value\n ? 'tpl:top-[104px]'\n : 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-warning);\n border-color: var(--tpl-warning);\n backdrop-filter: blur(8px);\n \"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <main class=\"tpl-main tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n :locked-blocks=\"collaboration?.lockedBlocks.value ?? undefined\"\n @select-block=\"editor.selectBlock\"\n @open-ai-chat=\"panelState.aiChatOpen.value = true\"\n @open-design-reference=\"panelState.designReferenceOpen.value = true\"\n />\n </main>\n </div>\n\n <EditorFooter\n v-if=\"!featureFlags.isWhiteLabeled.value\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : panelState.rightPanelOpen.value\n ? 'tpl:left-12 tpl:right-[680px]'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n :shifted-left=\"panelState.rightPanelOpen.value\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.selectedBlock.value!.id, updates)\n \"\n @delete-block=\"\n core.blockActions.deleteBlock(editor.selectedBlock.value!.id)\n \"\n @duplicate-block=\"\n core.blockActions.duplicateBlock(editor.selectedBlock.value!)\n \"\n @update-settings=\"editor.updateSettings\"\n />\n\n <!-- Cloud sidebars + modals — only mount after cloud init completes -->\n <CloudPanels\n v-if=\"!isInitializing && isAuthReady\"\n ref=\"cloudPanelsRef\"\n :config=\"props.config\"\n :editor=\"editor\"\n :core=\"core\"\n :panel-state=\"panelState\"\n :plan-config-instance=\"planConfigInstance\"\n :test-email=\"testEmail\"\n :media-lib=\"mediaLib\"\n :saved-modules-headless=\"savedModulesHeadless\"\n :show-save-module-dialog=\"showSaveModuleDialog\"\n :save-module-pre-selected-block-id=\"saveModulePreSelectedBlockId\"\n :show-module-browser-modal=\"showModuleBrowserModal\"\n @update:show-save-module-dialog=\"showSaveModuleDialog = $event\"\n @update:save-module-pre-selected-block-id=\"\n saveModulePreSelectedBlockId = $event\n \"\n @update:show-module-browser-modal=\"showModuleBrowserModal = $event\"\n @send-test-email=\"handleSendTestEmail\"\n @module-insert=\"handleModuleInsert\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-restore-btn-enter-active {\n transition:\n opacity 200ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-restore-btn-leave-active {\n transition:\n opacity 150ms ease-in,\n transform 150ms ease-in;\n}\n\n.tpl-restore-btn-enter-from,\n.tpl-restore-btn-leave-to {\n opacity: 0;\n transform: translateY(-8px) scale(0.9);\n}\n\n.tpl-restore-btn-enter-to,\n.tpl-restore-btn-leave-from {\n opacity: 1;\n transform: translateY(0) scale(1);\n}\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"dist-BUmN5e4r.js","names":["isSection2"],"sources":["../../../../renderer/dist/index.js"],"sourcesContent":["// src/index.ts\nimport { isSection as isSection2 } from \"@templatical/types\";\n\n// src/render-context.ts\nvar BUILT_IN_FONT_FALLBACKS = {\n arial: \"Arial, sans-serif\",\n helvetica: \"Helvetica, sans-serif\",\n georgia: \"Georgia, serif\",\n \"times new roman\": \"'Times New Roman', serif\",\n verdana: \"Verdana, sans-serif\",\n \"trebuchet ms\": \"'Trebuchet MS', sans-serif\",\n \"courier new\": \"'Courier New', monospace\",\n tahoma: \"Tahoma, sans-serif\"\n};\nvar RenderContext = class _RenderContext {\n constructor(containerWidth, customFonts, defaultFallbackFont, allowHtmlBlocks) {\n this.containerWidth = containerWidth;\n this.customFonts = customFonts;\n this.defaultFallbackFont = defaultFallbackFont;\n this.allowHtmlBlocks = allowHtmlBlocks;\n }\n /**\n * Create a new context with a different container width.\n * Used when rendering columns with narrower widths.\n */\n withContainerWidth(width) {\n return new _RenderContext(\n width,\n this.customFonts,\n this.defaultFallbackFont,\n this.allowHtmlBlocks\n );\n }\n /**\n * Resolve a font family name to include custom font fallbacks.\n * If the font matches a custom font, returns `'FontName', fallback`.\n * Otherwise returns the original font family string.\n */\n resolveFontFamily(fontFamily) {\n for (const customFont of this.customFonts) {\n if (customFont.name.toLowerCase() === fontFamily.toLowerCase()) {\n const fallback = customFont.fallback ?? this.defaultFallbackFont;\n return `'${customFont.name}', ${fallback}`;\n }\n }\n const builtIn = BUILT_IN_FONT_FALLBACKS[fontFamily.toLowerCase()];\n if (builtIn) {\n return builtIn;\n }\n return fontFamily;\n }\n};\n\n// src/renderers/index.ts\nimport {\n isSection,\n isTitle,\n isParagraph,\n isImage,\n isButton,\n isDivider,\n isSpacer,\n isHtml,\n isSocialIcons,\n isMenu,\n isTable,\n isVideo,\n isCustomBlock\n} from \"@templatical/types\";\n\n// src/renderers/title.ts\nimport { HEADING_LEVEL_FONT_SIZE } from \"@templatical/types\";\n\n// src/escape.ts\nvar HTML_ENTITIES = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#039;\"\n};\nvar HTML_ENTITY_REGEX = /[&<>\"']/g;\nfunction escapeHtml(text) {\n if (text === \"\") {\n return \"\";\n }\n return text.replace(HTML_ENTITY_REGEX, (char) => HTML_ENTITIES[char] ?? char);\n}\nfunction escapeAttr(text) {\n if (text === \"\") {\n return \"\";\n }\n return text.replace(HTML_ENTITY_REGEX, (char) => HTML_ENTITIES[char] ?? char);\n}\nfunction convertMergeTagsToValues(html) {\n if (html === \"\") {\n return \"\";\n }\n let result = html.replace(\n /<span[^>]*\\bdata-merge-tag=\"([^\"]*)\"[^>]*>.*?<\\/span>/gs,\n \"$1\"\n );\n result = result.replace(\n /<span[^>]*\\bdata-logic-merge-tag=\"([^\"]*)\"[^>]*>.*?<\\/span>/gs,\n \"$1\"\n );\n return result;\n}\n\n// src/padding.ts\nfunction toPaddingString(padding) {\n return `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`;\n}\n\n// src/visibility.ts\nfunction isHiddenOnAll(block) {\n const visibility = block.visibility;\n if (!visibility) {\n return false;\n }\n return !visibility.desktop && !visibility.tablet && !visibility.mobile;\n}\nfunction getCssClassAttr(block) {\n const classes = getCssClasses(block);\n if (classes === \"\") {\n return \"\";\n }\n return ` css-class=\"${classes}\"`;\n}\nfunction getCssClasses(block) {\n const visibility = block.visibility;\n if (!visibility) {\n return \"\";\n }\n const classes = [];\n if (!visibility.desktop) {\n classes.push(\"tpl-hide-desktop\");\n }\n if (!visibility.tablet) {\n classes.push(\"tpl-hide-tablet\");\n }\n if (!visibility.mobile) {\n classes.push(\"tpl-hide-mobile\");\n }\n return classes.join(\" \");\n}\n\n// src/renderers/title.ts\nfunction renderTitle(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const content = convertMergeTagsToValues(block.content);\n const fontSize = HEADING_LEVEL_FONT_SIZE[block.level];\n const color = block.color;\n const align = block.textAlign;\n const fontFamilyAttr = renderFontFamilyAttr(block.fontFamily, context);\n const visibilityAttr = getCssClassAttr(block);\n const tag = `h${block.level}`;\n return `<mj-text\n font-size=\"${fontSize}px\"\n color=\"${color}\"\n align=\"${align}\"\n line-height=\"1.3\"\n padding=\"${padding}\"${bgColor}${fontFamilyAttr}${visibilityAttr}\n><${tag} style=\"margin:0;font-size:inherit;color:inherit;line-height:inherit\">${content}</${tag}></mj-text>`;\n}\nfunction renderFontFamilyAttr(fontFamily, context) {\n if (!fontFamily) {\n return \"\";\n }\n const resolved = context.resolveFontFamily(fontFamily);\n return ` font-family=\"${resolved}\"`;\n}\n\n// src/renderers/paragraph.ts\nfunction renderParagraph(block, _context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const content = convertMergeTagsToValues(block.content);\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-text\n line-height=\"1.5\"\n padding=\"${padding}\"${bgColor}${visibilityAttr}\n>${content}</mj-text>`;\n}\n\n// src/renderers/image.ts\nfunction renderImage(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const width = block.width === \"full\" ? context.containerWidth + \"px\" : block.width + \"px\";\n const visibilityAttr = getCssClassAttr(block);\n let linkAttr = \"\";\n if (block.linkUrl) {\n linkAttr = ` href=\"${escapeAttr(block.linkUrl)}\"`;\n if (block.linkOpenInNewTab) {\n linkAttr += ' target=\"_blank\"';\n }\n }\n const src = escapeAttr(block.src);\n const alt = escapeAttr(block.alt);\n const align = block.align;\n return `<mj-image\n src=\"${src}\"\n alt=\"${alt}\"\n width=\"${width}\"\n align=\"${align}\"\n padding=\"${padding}\"${bgColor}${linkAttr}${visibilityAttr}\n/>`;\n}\n\n// src/renderers/button.ts\nfunction renderButton(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` container-background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const buttonPadding = toPaddingString(block.buttonPadding);\n const href = escapeAttr(block.url);\n const backgroundColor = block.backgroundColor;\n const textColor = block.textColor;\n const fontSize = block.fontSize;\n const borderRadius = block.borderRadius;\n const text = escapeHtml(block.text);\n const targetAttr = block.openInNewTab ? ' target=\"_blank\"' : \"\";\n const fontFamilyAttr = renderFontFamilyAttr2(block.fontFamily, context);\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-button\n href=\"${href}\"${targetAttr}\n background-color=\"${backgroundColor}\"\n color=\"${textColor}\"\n font-size=\"${fontSize}px\"\n font-weight=\"bold\"\n border-radius=\"${borderRadius}px\"\n inner-padding=\"${buttonPadding}\"\n padding=\"${padding}\"${bgColor}${fontFamilyAttr}${visibilityAttr}\n>${text}</mj-button>`;\n}\nfunction renderFontFamilyAttr2(fontFamily, context) {\n if (!fontFamily) {\n return \"\";\n }\n const resolved = context.resolveFontFamily(fontFamily);\n return ` font-family=\"${resolved}\"`;\n}\n\n// src/renderers/divider.ts\nfunction renderDivider(block, _context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` container-background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const width = block.width === \"full\" ? \"100%\" : block.width + \"px\";\n const thickness = block.thickness;\n const lineStyle = block.lineStyle;\n const color = block.color;\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-divider\n border-width=\"${thickness}px\"\n border-style=\"${lineStyle}\"\n border-color=\"${color}\"\n width=\"${width}\"\n padding=\"${padding}\"${bgColor}${visibilityAttr}\n/>`;\n}\n\n// src/renderers/spacer.ts\nfunction renderSpacer(block, _context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const height = block.height;\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` container-background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-spacer height=\"${height}px\" padding=\"${padding}\"${bgColor}${visibilityAttr} />`;\n}\n\n// src/renderers/html.ts\nfunction renderHtml(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n if (!context.allowHtmlBlocks) {\n return \"\";\n }\n const content = block.content;\n if (content === \"\") {\n return \"\";\n }\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-text${visibilityAttr}>\n${content}\n</mj-text>`;\n}\n\n// src/social-icons.ts\nvar SOCIAL_ICONS = {\n facebook: {\n path: \"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z\",\n color: \"#1877F2\"\n },\n twitter: {\n path: \"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\",\n color: \"#000000\"\n },\n instagram: {\n path: \"M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678c-3.405 0-6.162 2.76-6.162 6.162 0 3.405 2.76 6.162 6.162 6.162 3.405 0 6.162-2.76 6.162-6.162 0-3.405-2.76-6.162-6.162-6.162zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405c0 .795-.646 1.44-1.44 1.44-.795 0-1.44-.646-1.44-1.44 0-.794.646-1.439 1.44-1.439.793-.001 1.44.645 1.44 1.439z\",\n color: \"#E4405F\"\n },\n linkedin: {\n path: \"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\",\n color: \"#0A66C2\"\n },\n youtube: {\n path: \"M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z\",\n color: \"#FF0000\"\n },\n tiktok: {\n path: \"M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z\",\n color: \"#000000\"\n },\n pinterest: {\n path: \"M12 0C5.373 0 0 5.372 0 12c0 5.084 3.163 9.426 7.627 11.174-.105-.949-.2-2.405.042-3.441.218-.937 1.407-5.965 1.407-5.965s-.359-.719-.359-1.782c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738.098.119.112.224.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12 24c6.627 0 12-5.373 12-12 0-6.628-5.373-12-12-12z\",\n color: \"#BD081C\"\n },\n email: {\n path: \"M1.5 8.67v8.58a3 3 0 003 3h15a3 3 0 003-3V8.67l-8.928 5.493a3 3 0 01-3.144 0L1.5 8.67z M22.5 6.908V6.75a3 3 0 00-3-3h-15a3 3 0 00-3 3v.158l9.714 5.978a1.5 1.5 0 001.572 0L22.5 6.908z\",\n color: \"#6B7280\"\n },\n whatsapp: {\n path: \"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\",\n color: \"#25D366\"\n },\n telegram: {\n path: \"M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z\",\n color: \"#26A5E4\"\n },\n discord: {\n path: \"M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189z\",\n color: \"#5865F2\"\n },\n snapchat: {\n path: \"M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738a.36.36 0 01.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146 1.124.347 2.317.535 3.554.535 6.627 0 12.017-5.373 12.017-12.001C24.034 5.367 18.644 0 12.017 0z\",\n color: \"#FFFC00\"\n },\n reddit: {\n path: \"M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z\",\n color: \"#FF4500\"\n },\n github: {\n path: \"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\",\n color: \"#181717\"\n },\n dribbble: {\n path: \"M12 24C5.385 24 0 18.615 0 12S5.385 0 12 0s12 5.385 12 12-5.385 12-12 12zm10.12-10.358c-.35-.11-3.17-.953-6.384-.438 1.34 3.684 1.887 6.684 1.992 7.308 2.3-1.555 3.936-4.02 4.392-6.87zm-6.115 7.808c-.153-.9-.75-4.032-2.19-7.77l-.066.02c-5.79 2.015-7.86 6.025-8.04 6.4 1.73 1.358 3.92 2.166 6.29 2.166 1.42 0 2.77-.29 4-.814zm-11.62-2.58c.232-.4 3.045-5.055 8.332-6.765.135-.045.27-.084.405-.12-.26-.585-.54-1.167-.832-1.74C7.17 11.775 2.206 11.71 1.756 11.7l-.004.312c0 2.633.998 5.037 2.634 6.855zm-2.42-8.955c.46.008 4.683.026 9.477-1.248-1.698-3.018-3.53-5.558-3.8-5.928-2.868 1.35-5.01 3.99-5.676 7.17zM9.6 2.052c.282.38 2.145 2.914 3.822 6 3.645-1.365 5.19-3.44 5.373-3.702-1.81-1.61-4.19-2.586-6.795-2.586-.825 0-1.63.1-2.4.285zm10.335 3.483c-.218.29-1.935 2.493-5.724 4.04.24.49.47.985.68 1.486.08.18.15.36.22.53 3.41-.43 6.8.26 7.14.33-.02-2.42-.88-4.64-2.31-6.38z\",\n color: \"#EA4C89\"\n },\n behance: {\n path: \"M22 7h-7V5h7v2zm1.726 10c-.442 1.297-2.029 3-5.101 3-3.074 0-5.564-1.729-5.564-5.675 0-3.91 2.325-5.92 5.466-5.92 3.082 0 4.964 1.782 5.375 4.426.078.506.109 1.188.095 2.14H15.97c.13 3.211 3.483 3.312 4.588 2.029h3.168zm-7.686-4h4.965c-.105-1.547-1.136-2.219-2.477-2.219-1.466 0-2.277.768-2.488 2.219zm-9.574 6.988H0V5.021h6.953c5.476.081 5.58 5.444 2.72 6.906 3.461 1.26 3.577 8.061-3.207 8.061zM3 11h3.584c2.508 0 2.906-3-.312-3H3v3zm3.391 3H3v3.016h3.341c3.055 0 2.868-3.016.05-3.016z\",\n color: \"#1769FF\"\n }\n};\nfunction generateSocialIconDataUri(platform, style, size) {\n const iconData = SOCIAL_ICONS[platform];\n const path = iconData?.path ?? \"\";\n const brandColor = iconData?.color ?? \"#6B7280\";\n if (path === \"\") {\n return \"\";\n }\n const isOutlined = style === \"outlined\";\n const iconColor = isOutlined ? brandColor : \"#ffffff\";\n let bgShape;\n switch (style) {\n case \"circle\":\n bgShape = `<circle cx=\"12\" cy=\"12\" r=\"12\" fill=\"${brandColor}\"/>`;\n break;\n case \"rounded\":\n bgShape = `<rect width=\"24\" height=\"24\" rx=\"6\" fill=\"${brandColor}\"/>`;\n break;\n case \"square\":\n bgShape = `<rect width=\"24\" height=\"24\" rx=\"0\" fill=\"${brandColor}\"/>`;\n break;\n case \"outlined\":\n bgShape = `<rect width=\"22\" height=\"22\" x=\"1\" y=\"1\" rx=\"3\" fill=\"transparent\" stroke=\"${brandColor}\" stroke-width=\"2\"/>`;\n break;\n default:\n bgShape = `<rect width=\"24\" height=\"24\" rx=\"3\" fill=\"${brandColor}\"/>`;\n break;\n }\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"${size}\" height=\"${size}\">` + bgShape + `<g transform=\"translate(4.8, 4.8) scale(0.6)\"><path d=\"${path}\" fill=\"${iconColor}\"/></g></svg>`;\n return \"data:image/svg+xml;base64,\" + btoa(svg);\n}\n\n// src/renderers/social.ts\nfunction renderSocialIcons(block, _context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const icons = block.icons;\n if (icons.length === 0) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` container-background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const visibilityAttr = getCssClassAttr(block);\n const align = block.align;\n const iconSize = block.iconSize;\n const iconStyle = block.iconStyle;\n const spacing = block.spacing;\n let iconSizePx;\n switch (iconSize) {\n case \"small\":\n iconSizePx = 24;\n break;\n case \"large\":\n iconSizePx = 48;\n break;\n default:\n iconSizePx = 32;\n break;\n }\n let borderRadius;\n switch (iconStyle) {\n case \"circle\":\n borderRadius = \"50%\";\n break;\n case \"rounded\":\n borderRadius = \"8px\";\n break;\n case \"square\":\n borderRadius = \"0\";\n break;\n default:\n borderRadius = \"4px\";\n break;\n }\n const iconCount = icons.length;\n const socialElements = icons.map((icon, index) => {\n const platform = icon.platform;\n const url = escapeAttr(icon.url);\n const iconSrc = generateSocialIconDataUri(platform, iconStyle, iconSizePx);\n const rightPad = index === iconCount - 1 ? 0 : spacing;\n return `<mj-social-element src=\"${iconSrc}\" href=\"${url}\" icon-size=\"${iconSizePx}px\" padding=\"0 ${rightPad}px 0 0\" border-radius=\"${borderRadius}\" background-color=\"transparent\"></mj-social-element>`;\n });\n const socialContent = socialElements.join(\"\\n\");\n return `<mj-social\n mode=\"horizontal\"\n align=\"${align}\"\n icon-padding=\"0\"\n padding=\"${padding}\"${bgColor}${visibilityAttr}\n>\n${socialContent}\n</mj-social>`;\n}\n\n// src/renderers/menu.ts\nfunction renderMenu(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n if (block.items.length === 0) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const visibilityAttr = getCssClassAttr(block);\n const fontFamilyAttr = renderFontFamilyAttr3(block.fontFamily, context);\n const align = block.textAlign;\n const fontSize = block.fontSize;\n const color = block.color;\n const content = renderMenuItems(block);\n return `<mj-text\n font-size=\"${fontSize}px\"\n color=\"${color}\"\n align=\"${align}\"\n line-height=\"1.5\"\n padding=\"${padding}\"${bgColor}${fontFamilyAttr}${visibilityAttr}\n>${content}</mj-text>`;\n}\nfunction renderMenuItems(block) {\n const items = block.items;\n const separator = escapeHtml(block.separator);\n const separatorColor = escapeAttr(block.separatorColor);\n const spacing = block.spacing;\n const linkColor = block.linkColor ?? block.color;\n const parts = [];\n const itemCount = items.length;\n for (let index = 0; index < itemCount; index++) {\n parts.push(renderMenuItem(items[index], linkColor));\n if (index < itemCount - 1) {\n parts.push(\n `<span style=\"color: ${separatorColor}; padding: 0 ${spacing}px;\">${separator}</span>`\n );\n }\n }\n return parts.join(\"\");\n}\nfunction renderMenuItem(item, linkColor) {\n const text = escapeHtml(item.text);\n const url = escapeAttr(item.url);\n const color = item.color ?? linkColor;\n const target = item.openInNewTab ? ' target=\"_blank\"' : \"\";\n const styles = [`color: ${color}`, \"text-decoration: none\"];\n if (item.bold) {\n styles.push(\"font-weight: bold\");\n }\n if (item.underline) {\n styles.push(\"text-decoration: underline\");\n }\n const styleAttr = styles.join(\"; \");\n return `<a href=\"${url}\" style=\"${styleAttr}\"${target}>${text}</a>`;\n}\nfunction renderFontFamilyAttr3(fontFamily, context) {\n if (!fontFamily) {\n return \"\";\n }\n const resolved = context.resolveFontFamily(fontFamily);\n return ` font-family=\"${resolved}\"`;\n}\n\n// src/renderers/table.ts\nfunction renderTable(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n if (block.rows.length === 0) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const visibilityAttr = getCssClassAttr(block);\n const fontFamilyAttr = renderFontFamilyAttr4(block.fontFamily, context);\n const fontSize = block.fontSize;\n const color = block.color;\n const align = block.textAlign;\n const tableHtml = renderTableElement(block);\n return `<mj-text\n font-size=\"${fontSize}px\"\n color=\"${color}\"\n align=\"${align}\"\n line-height=\"1.5\"\n padding=\"${padding}\"${bgColor}${fontFamilyAttr}${visibilityAttr}\n>${tableHtml}</mj-text>`;\n}\nfunction renderTableElement(block) {\n const borderColor = escapeAttr(block.borderColor);\n const borderWidth = block.borderWidth;\n const tableStyle = \"width: 100%; border-collapse: collapse;\";\n let rowsHtml = \"\";\n for (let index = 0; index < block.rows.length; index++) {\n const row = block.rows[index];\n const isHeader = block.hasHeaderRow && index === 0;\n rowsHtml += renderRow(row, block, isHeader, borderColor, borderWidth);\n }\n return `<table style=\"${tableStyle}\">${rowsHtml}</table>`;\n}\nfunction renderRow(row, block, isHeader, borderColor, borderWidth) {\n let cellsHtml = \"\";\n for (const cell of row.cells) {\n cellsHtml += renderCell(cell, block, isHeader, borderColor, borderWidth);\n }\n return `<tr>${cellsHtml}</tr>`;\n}\nfunction renderCell(cell, block, isHeader, borderColor, borderWidth) {\n const cellPadding = block.cellPadding;\n const styles = [\n `border: ${borderWidth}px solid ${borderColor}`,\n `padding: ${cellPadding}px`\n ];\n if (isHeader) {\n styles.push(\"font-weight: bold\");\n if (block.headerBackgroundColor) {\n styles.push(\n `background-color: ${escapeAttr(block.headerBackgroundColor)}`\n );\n }\n }\n const styleAttr = styles.join(\"; \");\n const content = convertMergeTagsToValues(cell.content);\n const tag = isHeader ? \"th\" : \"td\";\n return `<${tag} style=\"${styleAttr}\">${content}</${tag}>`;\n}\nfunction renderFontFamilyAttr4(fontFamily, context) {\n if (!fontFamily) {\n return \"\";\n }\n const resolved = context.resolveFontFamily(fontFamily);\n return ` font-family=\"${resolved}\"`;\n}\n\n// src/renderers/custom.ts\nfunction renderCustom(block, _context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const content = block.renderedHtml;\n if (!content || content === \"\") {\n return \"\";\n }\n const visibilityAttr = getCssClassAttr(block);\n return `<mj-text${visibilityAttr}>\n${content}\n</mj-text>`;\n}\n\n// src/columns.ts\nfunction getWidthPercentages(layout) {\n switch (layout) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n}\nfunction getWidthPixels(layout, containerWidth) {\n switch (layout) {\n case \"2\":\n return [containerWidth * 0.5, containerWidth * 0.5];\n case \"3\":\n return [containerWidth / 3, containerWidth / 3, containerWidth / 3];\n case \"1-2\":\n return [containerWidth / 3, containerWidth * 2 / 3];\n case \"2-1\":\n return [containerWidth * 2 / 3, containerWidth / 3];\n default:\n return [containerWidth];\n }\n}\n\n// src/renderers/section.ts\nfunction renderSection(block, context, renderBlock2) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const columnsLayout = block.columns;\n const columnWidths = getWidthPercentages(columnsLayout);\n const columnWidthsPx = getWidthPixels(columnsLayout, context.containerWidth);\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const visibilityAttr = getCssClassAttr(block);\n const children = block.children;\n const columnsContent = [];\n for (let index = 0; index < children.length; index++) {\n const column = children[index];\n const width = columnWidths[index] ?? \"100%\";\n const columnWidth = Math.floor(\n columnWidthsPx[index] ?? context.containerWidth\n );\n const filteredColumn = filterHtmlBlocks(column, context.allowHtmlBlocks);\n const columnContext = context.withContainerWidth(columnWidth);\n const columnBlocks = filteredColumn.map((child) => renderBlock2(child, columnContext)).filter((value) => value !== \"\").join(\"\\n\");\n const content = columnBlocks === \"\" ? \"<mj-text>&nbsp;</mj-text>\" : columnBlocks;\n columnsContent.push(`<mj-column width=\"${width}\">\n${content}\n</mj-column>`);\n }\n const columns = columnsContent.join(\"\\n\");\n return `<mj-section${bgColor} padding=\"${padding}\"${visibilityAttr}>\n${columns}\n</mj-section>`;\n}\nfunction filterHtmlBlocks(blocks, allowHtmlBlocks) {\n if (allowHtmlBlocks) {\n return blocks;\n }\n return blocks.filter((block) => block.type !== \"html\");\n}\n\n// src/renderers/video.ts\nfunction getVideoThumbnail(url, customThumbnail) {\n if (customThumbnail) {\n return customThumbnail;\n }\n if (!url) {\n return null;\n }\n const youtubePatterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]{11})/\n ];\n for (const pattern of youtubePatterns) {\n const match = url.match(pattern);\n if (match) {\n return `https://img.youtube.com/vi/${match[1]}/maxresdefault.jpg`;\n }\n }\n const vimeoMatch = url.match(/vimeo\\.com\\/(?:video\\/)?(\\d+)/);\n if (vimeoMatch) {\n return `https://vumbnail.com/${vimeoMatch[1]}.jpg`;\n }\n return null;\n}\nfunction renderVideo(block, context) {\n if (isHiddenOnAll(block)) {\n return \"\";\n }\n const thumbnailUrl = getVideoThumbnail(block.url, block.thumbnailUrl);\n if (!thumbnailUrl) {\n return \"\";\n }\n const padding = toPaddingString(block.styles.padding);\n const bgColor = block.styles.backgroundColor ? ` background-color=\"${block.styles.backgroundColor}\"` : \"\";\n const width = block.width === \"full\" ? context.containerWidth + \"px\" : block.width + \"px\";\n const visibilityAttr = getCssClassAttr(block);\n const src = escapeAttr(thumbnailUrl);\n const alt = escapeAttr(block.alt);\n const align = block.align;\n const href = escapeAttr(block.url);\n return `<mj-image\n src=\"${src}\"\n alt=\"${alt}\"\n width=\"${width}\"\n align=\"${align}\"\n padding=\"${padding}\"\n href=\"${href}\"\n target=\"_blank\"${bgColor}${visibilityAttr}\n/>`;\n}\n\n// src/renderers/index.ts\nfunction renderBlock(block, context) {\n if (isSection(block)) {\n return renderSection(block, context, renderBlock);\n }\n if (isTitle(block)) {\n return renderTitle(block, context);\n }\n if (isParagraph(block)) {\n return renderParagraph(block, context);\n }\n if (isImage(block)) {\n return renderImage(block, context);\n }\n if (isButton(block)) {\n return renderButton(block, context);\n }\n if (isDivider(block)) {\n return renderDivider(block, context);\n }\n if (isSpacer(block)) {\n return renderSpacer(block, context);\n }\n if (isHtml(block)) {\n return renderHtml(block, context);\n }\n if (isSocialIcons(block)) {\n return renderSocialIcons(block, context);\n }\n if (isMenu(block)) {\n return renderMenu(block, context);\n }\n if (isTable(block)) {\n return renderTable(block, context);\n }\n if (isVideo(block)) {\n return renderVideo(block, context);\n }\n if (isCustomBlock(block)) {\n return renderCustom(block, context);\n }\n return \"\";\n}\n\n// src/index.ts\nfunction renderToMjml(content, options) {\n const customFonts = options?.customFonts ?? [];\n const defaultFallbackFont = options?.defaultFallbackFont ?? \"Arial, sans-serif\";\n const allowHtmlBlocks = options?.allowHtmlBlocks ?? true;\n const renderContext = new RenderContext(\n content.settings.width,\n customFonts,\n defaultFallbackFont,\n allowHtmlBlocks\n );\n const blocks = filterHtmlBlocks2(content.blocks, allowHtmlBlocks);\n const fontFamily = renderContext.resolveFontFamily(\n content.settings.fontFamily\n );\n const backgroundColor = content.settings.backgroundColor;\n const bodyContent = blocks.map((block) => renderTopLevelBlock(block, renderContext)).filter((value) => value !== \"\").join(\"\\n\");\n const fontDeclarations = generateFontDeclarations(customFonts);\n const previewTag = generatePreviewTag(content.settings.preheaderText);\n return `<mjml>\n <mj-head>${previewTag}\n <mj-attributes>\n <mj-all font-family=\"${fontFamily}\" />\n <mj-section padding=\"0\" />\n <mj-column padding=\"0\" />\n <mj-image fluid-on-mobile=\"true\" />\n </mj-attributes>${fontDeclarations}\n <mj-style>\n a { color: inherit; text-decoration: none; }\n @media only screen and (max-width: 480px) {\n .tpl-hide-mobile { display: none !important; mso-hide: all !important; }\n }\n @media only screen and (min-width: 481px) and (max-width: 768px) {\n .tpl-hide-tablet { display: none !important; mso-hide: all !important; }\n }\n @media only screen and (min-width: 769px) {\n .tpl-hide-desktop { display: none !important; mso-hide: all !important; }\n }\n </mj-style>\n </mj-head>\n <mj-body width=\"${renderContext.containerWidth}px\" background-color=\"${backgroundColor}\">\n${bodyContent}\n </mj-body>\n</mjml>`;\n}\nfunction renderTopLevelBlock(block, context) {\n if (isSection2(block)) {\n const rendered = renderBlock(block, context);\n return wrapWithDisplayCondition(block, rendered);\n }\n const content = renderBlock(block, context);\n const wrapped = wrapInSection(content);\n return wrapWithDisplayCondition(block, wrapped);\n}\nfunction wrapWithDisplayCondition(block, rendered) {\n if (rendered === \"\") {\n return \"\";\n }\n const displayCondition = block.displayCondition;\n if (!displayCondition) {\n return rendered;\n }\n return `<mj-raw>${displayCondition.before}</mj-raw>\n` + rendered + `\n<mj-raw>${displayCondition.after}</mj-raw>`;\n}\nfunction wrapInSection(content) {\n if (content === \"\") {\n return \"\";\n }\n return `<mj-section>\n <mj-column>\n${content}\n </mj-column>\n</mj-section>`;\n}\nfunction generatePreviewTag(preheaderText) {\n if (!preheaderText) {\n return \"\";\n }\n const trimmed = preheaderText.trim();\n if (trimmed === \"\") {\n return \"\";\n }\n const escaped = escapeHtml(trimmed);\n return `\n <mj-preview>${escaped}</mj-preview>`;\n}\nfunction generateFontDeclarations(customFonts) {\n if (customFonts.length === 0) {\n return \"\";\n }\n return customFonts.map(\n (font) => `\n <mj-font name=\"${escapeAttr(font.name)}\" href=\"${escapeAttr(font.url)}\" />`\n ).join(\"\");\n}\nfunction filterHtmlBlocks2(blocks, allowHtmlBlocks) {\n if (allowHtmlBlocks) {\n return blocks;\n }\n return blocks.filter((block) => block.type !== \"html\");\n}\nexport {\n RenderContext,\n SOCIAL_ICONS,\n convertMergeTagsToValues,\n escapeAttr,\n escapeHtml,\n generateSocialIconDataUri,\n getCssClassAttr,\n getCssClasses,\n getWidthPercentages,\n getWidthPixels,\n isHiddenOnAll,\n renderBlock,\n renderToMjml,\n toPaddingString\n};\n//# sourceMappingURL=index.js.map"],"mappings":"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"}