@templatical/editor 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/{AiChatSidebar-BwLECwsO.js → AiChatSidebar-H7SIsxeC.js} +49 -59
  2. package/dist/{AiFeatureMenu-CVHKharv.js → AiFeatureMenu-CJGX-fP5.js} +1 -1
  3. package/dist/{CloudEditor-S7AzuNwC.js → CloudEditor-BBs3p-mW.js} +124 -120
  4. package/dist/{CollaboratorBar-VZKOv_Zn.js → CollaboratorBar-CiEo25lb.js} +2 -2
  5. package/dist/{CommentsSidebar-BiRtaXYD.js → CommentsSidebar-DikjSLtH.js} +1 -1
  6. package/dist/{CountdownToolbar-CojjlZet.js → CountdownToolbar-Bn4RwJVN.js} +2 -2
  7. package/dist/{DesignReferenceSidebar-Ci9HIGbf.js → DesignReferenceSidebar-D08CLZPA.js} +1 -1
  8. package/dist/{ModuleBrowserModal-D7IYx1Nh.js → ModuleBrowserModal-D6637s8j.js} +4 -4
  9. package/dist/{ModulePreviewCanvas-I-uMcK5G.js → ModulePreviewCanvas-DBFmwq-y.js} +1 -1
  10. package/dist/{NumberWithSuffix-CLTBb2Rj.js → NumberWithSuffix-CyFX9w1e.js} +1 -1
  11. package/dist/{ParagraphEditor-BRQTNDFO.js → ParagraphEditor-DC2jOjNh.js} +16 -16
  12. package/dist/{RichTextEditorContent-C3QSg7gd.js → RichTextEditorContent-8NkjTIua.js} +1 -1
  13. package/dist/{SaveModuleDialog-DGGGNZfm.js → SaveModuleDialog-NKT7bWWs.js} +2 -2
  14. package/dist/{SnapshotHistory-C97Iw6xw.js → SnapshotHistory-XNqYxPBj.js} +2 -2
  15. package/dist/{TestEmailModal-B7S8H-VG.js → TestEmailModal-B_-Qo5Us.js} +2 -2
  16. package/dist/{TitleEditor-DhFTYzrw.js → TitleEditor-DnH3yyTr.js} +7 -7
  17. package/dist/{TplModal-BgABm6ju.js → TplModal-BoEZFe3U.js} +2 -2
  18. package/dist/{blockTypeIcons-BSf-3RJ-.js → blockTypeIcons-CUcYuWAO.js} +1 -1
  19. package/dist/bundle-stats.json +7 -7
  20. package/dist/cdn/chunks/{AccessibilityPanel-kgNBRaZV.js → AccessibilityPanel-CQt87OMk.js} +5 -5
  21. package/dist/cdn/chunks/{AccessibilityPanel-kgNBRaZV.js.map → AccessibilityPanel-CQt87OMk.js.map} +1 -1
  22. package/dist/cdn/chunks/{AiFeatureMenu-CRkzwdMg.js → AiFeatureMenu-SxIqfqA0.js} +8 -8
  23. package/dist/cdn/chunks/{AiFeatureMenu-CRkzwdMg.js.map → AiFeatureMenu-SxIqfqA0.js.map} +1 -1
  24. package/dist/cdn/chunks/{BlockA11yBadge-BfuH2Hrg.js → BlockA11yBadge-QqW6nomn.js} +3 -3
  25. package/dist/cdn/chunks/{BlockA11yBadge-BfuH2Hrg.js.map → BlockA11yBadge-QqW6nomn.js.map} +1 -1
  26. package/dist/cdn/chunks/{CloudEditor-BGiN3M_f.js → CloudEditor-ChWUIMw7.js} +165 -161
  27. package/dist/cdn/chunks/CloudEditor-ChWUIMw7.js.map +1 -0
  28. package/dist/cdn/chunks/{CollaboratorBar-CMl3lAZU.js → CollaboratorBar-73LfbzqF.js} +3 -3
  29. package/dist/cdn/chunks/{CollaboratorBar-CMl3lAZU.js.map → CollaboratorBar-73LfbzqF.js.map} +1 -1
  30. package/dist/cdn/chunks/{CountdownBlock-D1RoaOgF.js → CountdownBlock-WYz9b9Ex.js} +2 -2
  31. package/dist/cdn/chunks/{CountdownBlock-D1RoaOgF.js.map → CountdownBlock-WYz9b9Ex.js.map} +1 -1
  32. package/dist/cdn/chunks/{CountdownToolbar-DltwuOer.js → CountdownToolbar-B9fPfFqp.js} +3 -3
  33. package/dist/cdn/chunks/{CountdownToolbar-DltwuOer.js.map → CountdownToolbar-B9fPfFqp.js.map} +1 -1
  34. package/dist/cdn/chunks/{ModuleBrowserModal-3uu2mXR9.js → ModuleBrowserModal-DfnJQBkM.js} +8 -8
  35. package/dist/cdn/chunks/{ModuleBrowserModal-3uu2mXR9.js.map → ModuleBrowserModal-DfnJQBkM.js.map} +1 -1
  36. package/dist/cdn/chunks/{ModulePreviewCanvas-K9CQFr7p.js → ModulePreviewCanvas-DE4_oHA_.js} +20 -20
  37. package/dist/cdn/chunks/{ModulePreviewCanvas-K9CQFr7p.js.map → ModulePreviewCanvas-DE4_oHA_.js.map} +1 -1
  38. package/dist/cdn/chunks/{NumberWithSuffix-BIFgtqGs.js → NumberWithSuffix-BST0yRor.js} +2 -2
  39. package/dist/cdn/chunks/{NumberWithSuffix-BIFgtqGs.js.map → NumberWithSuffix-BST0yRor.js.map} +1 -1
  40. package/dist/cdn/chunks/{ParagraphEditor-DlpZX_x4.js → ParagraphEditor-CQfijF3i.js} +36 -36
  41. package/dist/cdn/chunks/{ParagraphEditor-DlpZX_x4.js.map → ParagraphEditor-CQfijF3i.js.map} +1 -1
  42. package/dist/cdn/chunks/{RichTextEditorContent-CPGT8h-O.js → RichTextEditorContent-CBds8dnm.js} +5 -5
  43. package/dist/cdn/chunks/{RichTextEditorContent-CPGT8h-O.js.map → RichTextEditorContent-CBds8dnm.js.map} +1 -1
  44. package/dist/cdn/chunks/{SaveModuleDialog-_uaJscj1.js → SaveModuleDialog-DXhuZgZ4.js} +13 -13
  45. package/dist/cdn/chunks/{SaveModuleDialog-_uaJscj1.js.map → SaveModuleDialog-DXhuZgZ4.js.map} +1 -1
  46. package/dist/cdn/chunks/{TitleEditor-Kerz6US8.js → TitleEditor-BdQvH7eK.js} +7 -7
  47. package/dist/cdn/chunks/{TitleEditor-Kerz6US8.js.map → TitleEditor-BdQvH7eK.js.map} +1 -1
  48. package/dist/cdn/chunks/{blockTypeIcons-BeOhGtoo.js → blockTypeIcons-D3CGFW5P.js} +8 -8
  49. package/dist/cdn/chunks/{blockTypeIcons-BeOhGtoo.js.map → blockTypeIcons-D3CGFW5P.js.map} +1 -1
  50. package/dist/cdn/chunks/{extensions-BZYkPlKr.js → extensions-BE81zpqP.js} +50 -48
  51. package/dist/cdn/chunks/{extensions-BZYkPlKr.js.map → extensions-BE81zpqP.js.map} +1 -1
  52. package/dist/cdn/chunks/{features-DzIN-Nua.js → features-CXF_YKlL.js} +1313 -1122
  53. package/dist/cdn/chunks/features-CXF_YKlL.js.map +1 -0
  54. package/dist/cdn/chunks/{icons-Nvr6w13E.js → icons-KbmhF7rg.js} +2 -2
  55. package/dist/cdn/chunks/{icons-Nvr6w13E.js.map → icons-KbmhF7rg.js.map} +1 -1
  56. package/dist/cdn/chunks/{media-library-BzT7BoGU.js → media-library-BERe10wA.js} +103 -103
  57. package/dist/cdn/chunks/{media-library-BzT7BoGU.js.map → media-library-BERe10wA.js.map} +1 -1
  58. package/dist/cdn/chunks/{quality-C328caFm.js → quality-CmkY1kMU.js} +309 -309
  59. package/dist/cdn/chunks/{quality-C328caFm.js.map → quality-CmkY1kMU.js.map} +1 -1
  60. package/dist/cdn/chunks/{renderer-eDlxzlJI.js → renderer-OttGEYkY.js} +50 -50
  61. package/dist/cdn/chunks/{renderer-eDlxzlJI.js.map → renderer-OttGEYkY.js.map} +1 -1
  62. package/dist/cdn/chunks/{src-DKNTQJVO.js → src-C21GI0p6.js} +6 -6
  63. package/dist/cdn/chunks/{src-DKNTQJVO.js.map → src-C21GI0p6.js.map} +1 -1
  64. package/dist/cdn/chunks/styles-Cy-My9zK.js +2976 -0
  65. package/dist/cdn/chunks/styles-Cy-My9zK.js.map +1 -0
  66. package/dist/cdn/editor.css +1 -1
  67. package/dist/cdn/editor.js +109 -106
  68. package/dist/cdn/editor.js.map +1 -1
  69. package/dist/{cloud-a3VovHva.js → cloud-k0DgeCo2.js} +11 -0
  70. package/dist/{dist-DDJIWTRY.js → dist-B2jcQhv8.js} +1 -1
  71. package/dist/{dist-wzMIGj-D.js → dist-BYt3jdCR.js} +1 -1
  72. package/dist/{dist-aRzjfSRN.js → dist-CISFttkF.js} +1 -1
  73. package/dist/{dist-D6uC2xhi.js → dist-CfQPBf15.js} +1 -1
  74. package/dist/{dist-DjviJBCi.js → dist-DD-9hatO.js} +1 -1
  75. package/dist/{dist-KoBJjK1G.js → dist-DUvhjQ2-.js} +1 -1
  76. package/dist/{dist-BaQIYPsn.js → dist-DVwOAodp.js} +1 -1
  77. package/dist/{dist-Cp0zXPAD.js → dist-DxD1kSdH.js} +1 -1
  78. package/dist/{dist-Pk4MhWMP.js → dist-U5guDm2w.js} +1 -1
  79. package/dist/{dist-D90y8dvT.js → dist-hfu7I2rO.js} +3 -3
  80. package/dist/{dist-us-RpCWN.js → dist-v8h02hhE.js} +1 -1
  81. package/dist/{extensions-D-J02CiP.js → extensions-p6UBqXfg.js} +30 -28
  82. package/dist/index.d.ts +8 -8
  83. package/dist/style.css +1 -1
  84. package/dist/{styles-DeNciUTX.js → styles-BP15QmP3.js} +1078 -1056
  85. package/dist/templatical-editor.js +108 -105
  86. package/dist/useAliveFlag-D8GoB4VE.js +10 -0
  87. package/dist/{useEditorCore-uCU9Ny0M.js → useEditorCore-_rbu6iMu.js} +969 -791
  88. package/package.json +7 -7
  89. package/dist/cdn/chunks/CloudEditor-BGiN3M_f.js.map +0 -1
  90. package/dist/cdn/chunks/features-DzIN-Nua.js.map +0 -1
  91. package/dist/cdn/chunks/styles-lcwmaOou.js +0 -2955
  92. package/dist/cdn/chunks/styles-lcwmaOou.js.map +0 -1
  93. /package/dist/{AccessibilityPanel-D-PqmHdH.js → AccessibilityPanel-BeUibrUb.js} +0 -0
  94. /package/dist/{BlockA11yBadge-BFIw0h1m.js → BlockA11yBadge-BhyoMZJ9.js} +0 -0
  95. /package/dist/{CountdownBlock-Cq8A8WrM.js → CountdownBlock-ByJ0_8Pk.js} +0 -0
  96. /package/dist/{TemplateScoringPanel-V79yrEPC.js → TemplateScoringPanel-kYOGtac3.js} +0 -0
  97. /package/dist/{de-BhIWu_bO.js → de-C9hp3n_a.js} +0 -0
  98. /package/dist/{de-Di4MEjjx.js → de-GOtR9DwW.js} +0 -0
  99. /package/dist/{dist-iLBdeBDR.js → dist-CQQS9SRN.js} +0 -0
  100. /package/dist/{emojiData-DX3E0XT-.js → emojiData-DrBuvEoP.js} +0 -0
  101. /package/dist/{en-D7HRbYah.js → en-Dv776AqL.js} +0 -0
  102. /package/dist/{en-BSuzi-Pd.js → en-dFFQVzNn.js} +0 -0
  103. /package/dist/{formatRelativeTime-BOEf47hq.js → formatRelativeTime-bvx5sFh5.js} +0 -0
  104. /package/dist/{liquid.browser-CdMv1BTn.js → liquid.browser-DX8ZHRdq.js} +0 -0
  105. /package/dist/{pt-BR-K32lt6YP.js → pt-BR-6n58Fl_q.js} +0 -0
  106. /package/dist/{pt-BR-CCVBRais.js → pt-BR-iOr79aDg.js} +0 -0
  107. /package/dist/{readableTextColor-CY3SiRnt.js → readableTextColor-CXzrEnNb.js} +0 -0
  108. /package/dist/{styleConstants-fWzlIIwN.js → styleConstants-DkfOPzGu.js} +0 -0
  109. /package/dist/{usePopoverRoot-BxJrqnMD.js → usePopoverRoot-BwzFSPLy.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"editor.js","names":[],"sources":["../../src/Editor.vue","../../src/Editor.vue","../../src/i18n/index.ts","../../src/utils/toMjml.ts","../../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onMounted, onUnmounted } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport { resolveAccessibilityOptions } from \"./utils/resolveAccessibilityOptions\";\nimport type { Translations } from \"./i18n\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\n\nimport { RotateCcw } from \"@lucide/vue\";\nimport Canvas from \"./components/Canvas.vue\";\nimport Sidebar from \"./components/Sidebar.vue\";\nimport RightSidebar from \"./components/RightSidebar.vue\";\nimport ViewportToggle from \"./components/ViewportToggle.vue\";\nimport PreviewToggle from \"./components/PreviewToggle.vue\";\nimport DarkModeToggle from \"./components/DarkModeToggle.vue\";\nimport EditorFooter from \"./components/EditorFooter.vue\";\nimport \"./styles/index.css\";\n\nconst props = defineProps<{\n config: TemplaticalEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n /**\n * Shadow root the editor is mounted into. Supplied by `init()` when\n * `shadowDom: true`; undefined in light-DOM mode. Passed through to\n * `useEditorCore` so shadow-DOM-aware composables can resolve the\n * effective root via `EDITOR_ROOT_KEY`.\n */\n shadowRoot?: ShadowRoot;\n}>();\n\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n editorRoot: props.shadowRoot,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\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 <!-- Header — absolute, full width, above everything -->\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 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\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 \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\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 </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\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 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\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 <div class=\"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 @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\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 @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n\n <!-- Popover mount — Teleport target for toolbars, link dialog, modal.\n Replaces the historical body-level teleport pattern so popups\n render inside the editor's effective DOM root (shadow-aware). -->\n <div\n :ref=\"(el) => (core.popoverRoot.value = el as HTMLElement | null)\"\n class=\"tpl-popover-root\"\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 { onMounted, onUnmounted } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport { resolveAccessibilityOptions } from \"./utils/resolveAccessibilityOptions\";\nimport type { Translations } from \"./i18n\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\n\nimport { RotateCcw } from \"@lucide/vue\";\nimport Canvas from \"./components/Canvas.vue\";\nimport Sidebar from \"./components/Sidebar.vue\";\nimport RightSidebar from \"./components/RightSidebar.vue\";\nimport ViewportToggle from \"./components/ViewportToggle.vue\";\nimport PreviewToggle from \"./components/PreviewToggle.vue\";\nimport DarkModeToggle from \"./components/DarkModeToggle.vue\";\nimport EditorFooter from \"./components/EditorFooter.vue\";\nimport \"./styles/index.css\";\n\nconst props = defineProps<{\n config: TemplaticalEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n /**\n * Shadow root the editor is mounted into. Supplied by `init()` when\n * `shadowDom: true`; undefined in light-DOM mode. Passed through to\n * `useEditorCore` so shadow-DOM-aware composables can resolve the\n * effective root via `EDITOR_ROOT_KEY`.\n */\n shadowRoot?: ShadowRoot;\n}>();\n\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n editorRoot: props.shadowRoot,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\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 <!-- Header — absolute, full width, above everything -->\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 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\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 \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\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 </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\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 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\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 <div class=\"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 @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\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 @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n\n <!-- Popover mount — Teleport target for toolbars, link dialog, modal.\n Replaces the historical body-level teleport pattern so popups\n render inside the editor's effective DOM root (shadow-aware). -->\n <div\n :ref=\"(el) => (core.popoverRoot.value = el as HTMLElement | null)\"\n class=\"tpl-popover-root\"\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","/// <reference types=\"vite/client\" />\n\nimport type en from \"./locales/en\";\nimport type cloudEn from \"./locales/cloud/en\";\n\nexport type Translations = typeof en;\nexport type CloudTranslations = typeof cloudEn;\nexport type TranslationKey = keyof Translations;\n\n// Vite resolves these globs at build time. Adding a new locale file\n// automatically registers it — no array to keep in sync.\nconst ossModules = import.meta.glob<{ default: Translations }>(\n \"./locales/*.ts\",\n);\nconst cloudModules = import.meta.glob<{ default: CloudTranslations }>(\n \"./locales/cloud/*.ts\",\n);\n\nfunction localesFromGlob(modules: Record<string, unknown>): string[] {\n return Object.keys(modules)\n .map((path) => path.match(/\\/([^/]+)\\.ts$/)?.[1])\n .filter((locale): locale is string => Boolean(locale));\n}\n\nconst supportedLocales = localesFromGlob(ossModules);\nconst supportedCloudLocales = localesFromGlob(cloudModules);\n\nfunction canonicalize(locale: string): string {\n return locale.trim().replace(/_/g, \"-\").toLowerCase();\n}\n\nfunction findSupportedLocale(\n locale: string,\n supported: string[],\n): string | undefined {\n const canonical = canonicalize(locale);\n return supported.find((s) => canonicalize(s) === canonical);\n}\n\n/**\n * Get the base language code from a locale string.\n * e.g., 'en-GB' -> 'en', 'de_DE' -> 'de'\n */\nexport function getBaseLocale(locale: string): string {\n return canonicalize(locale).split(\"-\")[0];\n}\n\nfunction tryResolveLocale(\n locale: string,\n supported: string[],\n): string | undefined {\n return (\n findSupportedLocale(locale, supported) ??\n findSupportedLocale(getBaseLocale(locale), supported)\n );\n}\n\nfunction resolveLocale(locale: string, supported: string[]): string {\n return tryResolveLocale(locale, supported) ?? \"en\";\n}\n\n/**\n * Load OSS translations for a given locale.\n * Falls back to English if the locale is not supported.\n */\nexport async function loadTranslations(locale: string): Promise<Translations> {\n const target = resolveLocale(locale, supportedLocales);\n const loader = ossModules[`./locales/${target}.ts`];\n const mod = await loader();\n return mod.default;\n}\n\n/**\n * Load cloud translations for a given locale.\n * Cloud translations cover features available only via initCloud() —\n * AI, comments, collaboration, scoring, snapshots, plan limits, etc.\n * OSS contributors don't need to translate these; cloud locales fall back\n * to English independently of the OSS locale.\n */\nexport async function loadCloudTranslations(\n locale: string,\n): Promise<CloudTranslations> {\n const target = resolveLocale(locale, supportedCloudLocales);\n const loader = cloudModules[`./locales/cloud/${target}.ts`];\n const mod = await loader();\n return mod.default;\n}\n\n/** Check if a locale has OSS translations (matched by exact locale, then base). */\nexport function isLocaleSupported(locale: string): boolean {\n return tryResolveLocale(locale, supportedLocales) !== undefined;\n}\n\n/** Check if a locale has cloud translations (matched by exact locale, then base). */\nexport function isCloudLocaleSupported(locale: string): boolean {\n return tryResolveLocale(locale, supportedCloudLocales) !== undefined;\n}\n\n/** List of OSS-supported locales. */\nexport function getSupportedLocales(): string[] {\n return [...supportedLocales];\n}\n\n/** List of cloud-supported locales. May be a subset of OSS locales. */\nexport function getSupportedCloudLocales(): string[] {\n return [...supportedCloudLocales];\n}\n","import type { CustomBlock, TemplateContent } from \"@templatical/types\";\n\n/**\n * Minimal slice of the editor surface needed by `toMjmlForInstance`.\n * Decoupled from the full `TemplaticalEditor` type so this helper can be\n * tested in isolation with a stub object.\n */\nexport interface ToMjmlSource {\n getContent(): TemplateContent;\n renderCustomBlock(block: CustomBlock): Promise<string>;\n}\n\n/**\n * Lazy-load `@templatical/renderer` and render the editor's current content\n * to MJML, wiring the editor's own custom block resolver into the renderer's\n * `renderCustomBlock` callback.\n *\n * The renderer is an optional peer dependency (small, MIT-licensed). It is\n * only loaded when an export is actually requested. Consumers that don't\n * need MJML export at all (e.g., embedding the editor in an app where the\n * backend handles export) can omit the install entirely; calling `toMjml()`\n * in that case throws a clear error naming the missing package.\n *\n * The dynamic import is cached by the module system, so subsequent calls\n * skip the import overhead.\n */\nexport async function toMjmlForInstance(\n instance: ToMjmlSource,\n): Promise<string> {\n let renderer: typeof import(\"@templatical/renderer\");\n try {\n renderer = await import(\"@templatical/renderer\");\n } catch {\n throw new Error(\n \"[Templatical] toMjml() requires the @templatical/renderer package. Please install it.\",\n );\n }\n return renderer.renderToMjml(instance.getContent(), {\n renderCustomBlock: instance.renderCustomBlock,\n });\n}\n","// eslint-disable-next-line @typescript-eslint/triple-slash-reference -- ambient-module declaration must be loaded for cross-package typecheck (workspace path alias resolves to source)\n/// <reference path=\"./virtual-modules.d.ts\" />\nimport { createApp, h, ref, type App, type Ref } from \"vue\";\nimport { INIT_TIMEOUT_MS } from \"./constants/timeouts\";\nimport type {\n BlockDefaults,\n CustomBlock,\n CustomBlockDefinition,\n DisplayConditionsConfig,\n FontsConfig,\n MediaResult,\n MergeTagsConfig,\n SaveResult,\n Template,\n TemplateContent,\n TemplateDefaults,\n ThemeOverrides,\n UiTheme,\n} from \"@templatical/types\";\nimport type { MediaRequestContext } from \"@templatical/media-library\";\n\nimport Editor from \"./Editor.vue\";\nimport { loadTranslations, loadCloudTranslations } from \"./i18n\";\nimport { useFonts } from \"./composables\";\nimport { toMjmlForInstance } from \"./utils/toMjml\";\n// Compiled-CSS-as-string for shadow root adoption. The `virtual:editor-css`\n// module is owned by `scripts/inline-style-css-plugin.ts` — at build time it\n// captures every emitted CSS asset (Tailwind utilities + every `.vue` SFC\n// `<style>` block + `styles/index.css` rules) and replaces this import's\n// runtime value with the full library CSS string. In dev/test the plugin\n// returns `styles/index.css` source as a fallback.\n//\n// Separate concern from the side-effecting `import \"./styles/index.css\"` in\n// `Editor.vue` / `CloudEditor.vue`, which injects styles into `document.head`\n// for light-DOM mode and survives untouched.\n//\n// Ambient declaration for `virtual:editor-css` lives in `virtual-modules.d.ts`\n// referenced at the top of this file via triple-slash so it's visible to any\n// consumer typechecking through the workspace path alias.\nimport editorStylesInline from \"virtual:editor-css\";\n\n// ---------------------------------------------------------------------------\n// OSS config + return types\n// ---------------------------------------------------------------------------\n\nexport interface TemplaticalEditorConfig {\n container: string | HTMLElement;\n content?: TemplateContent;\n\n /**\n * Mount the editor inside a Shadow DOM (open mode) for CSS isolation\n * from the host page. Defaults to `true` — host stylesheets cannot\n * cascade past the shadow boundary into editor elements (`p`, `a`,\n * `input`, etc.), and editor utility classes never collide with host\n * class names.\n *\n * Set to `false` to mount in light DOM. Opt out when:\n * - Your host integration uses `document.querySelector` to reach\n * editor internals (with shadow DOM, use `container.shadowRoot\n * .querySelector(...)` instead).\n * - You need to support Firefox <101 or Safari <16.4, which lack the\n * `adoptedStyleSheets` API the shadow path relies on.\n *\n * Light-mode consumers should keep this set to `false` explicitly so\n * future SDK changes don't silently flip the default again.\n *\n * @default true\n */\n shadowDom?: boolean;\n\n onChange?: (content: TemplateContent) => void;\n onSave?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n\n onRequestMedia?: OnRequestMedia;\n\n mergeTags?: MergeTagsConfig;\n displayConditions?: DisplayConditionsConfig;\n customBlocks?: CustomBlockDefinition[];\n fonts?: FontsConfig;\n\n blockDefaults?: BlockDefaults;\n templateDefaults?: TemplateDefaults;\n\n theme?: ThemeOverrides;\n uiTheme?: UiTheme;\n locale?: string;\n\n /**\n * Show the \"Powered by Templatical\" footer. Defaults to `true`.\n * Set to `false` to hide the footer (no attribution required by the license).\n */\n branding?: boolean;\n\n /**\n * Accessibility linter (`@templatical/quality`) configuration.\n *\n * - When unset, the linter loads on demand once the user opens the panel.\n * - When `disabled: true`, the optional peer is never imported (saves the\n * chunk download) and the sidebar tab + inline badges are suppressed.\n * - `rules`/`thresholds` follow the shape exported by `@templatical/quality`.\n */\n accessibility?: import(\"@templatical/quality\").A11yOptions;\n}\n\n/** Function type for media browser requests, used by both OSS and Cloud editors. */\nexport type OnRequestMedia = (\n context?: MediaRequestContext,\n) => Promise<MediaResult | null>;\n\ninterface TemplaticalEditorBase {\n getContent(): TemplateContent;\n setContent(content: TemplateContent): void;\n setTheme(theme: UiTheme): void;\n unmount(): void;\n}\n\nexport interface TemplaticalEditor extends TemplaticalEditorBase {\n /**\n * Render the current template to an MJML string. Resolves custom blocks\n * via the editor's internal block registry. Throws if the optional\n * `@templatical/renderer` package is not installed.\n */\n toMjml(): Promise<string>;\n /**\n * Render a single custom block to its HTML representation, using the\n * registered custom block definition's template and the block's current\n * field values. Exposed for headless callers that want to reuse the\n * editor's renderer (e.g., to drive `@templatical/renderer`'s\n * `renderCustomBlock` option from outside the editor instance).\n */\n renderCustomBlock(block: CustomBlock): Promise<string>;\n}\n\n/**\n * Cloud editor does not expose `toMjml` or `renderCustomBlock`: the cloud\n * backend performs MJML conversion server-side with additional processing\n * (e.g., signed image URLs, attachment handling) that isn't available client\n * side. Use the cloud `save()` flow to persist content; the backend handles\n * MJML/HTML export from there.\n */\nexport interface TemplaticalCloudEditor extends TemplaticalEditorBase {\n create(content?: TemplateContent): Promise<Template>;\n load(templateId: string): Promise<Template>;\n save(): Promise<SaveResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Shadow root helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Module-cached `CSSStyleSheet` built once from the inline editor CSS string.\n * `adoptedStyleSheets` accepts the same sheet object across multiple shadow\n * roots — sharing one sheet costs zero per-instance memory, regardless of\n * how many editors mount.\n */\nlet cachedEditorStyleSheet: CSSStyleSheet | null = null;\nfunction getEditorStyleSheet(): CSSStyleSheet {\n if (cachedEditorStyleSheet === null) {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(editorStylesInline);\n cachedEditorStyleSheet = sheet;\n }\n return cachedEditorStyleSheet;\n}\n\ninterface MountTarget {\n target: Element;\n shadowRoot: ShadowRoot | null;\n /**\n * Disposer for any dev-mode side effects attached to the shadow root\n * (currently: the document.head `<style>` mirror's MutationObserver).\n * Always safe to call — no-op when no side effects were registered.\n */\n cleanup: () => void;\n}\n\n/**\n * Dev-only: mirror every `<style>` tag in `document.head` into the\n * shadow root's `adoptedStyleSheets`, and observe `document.head` so\n * Vite's HMR-injected style updates flow through to the shadow root\n * automatically.\n *\n * Background: Vite dev injects each `.vue` `<style scoped>` block as a\n * separate `<style>` element in `document.head` via HMR. Those don't\n * cross the shadow boundary, so a shadow-mounted editor in dev would\n * render with only the bundled `styles/index.css` rules — every SFC\n * scoped style (block selection outlines, sidebar layout, etc.) missing.\n *\n * In production builds, `inline-style-css-plugin.ts` captures every\n * emitted CSS asset at `generateBundle` time and inlines the full\n * library CSS as a single string adopted by the shadow root. This\n * dev-only mirror does the same thing at runtime by observing whatever\n * Vite ends up injecting.\n *\n * The dead-code-elimination on `import.meta.env.DEV` ensures the\n * observer + filter logic is stripped from production bundles entirely.\n *\n * Caveats:\n * - In a real consumer's Vite-dev environment, this would also adopt\n * the consumer's page-level styles (whatever they put in\n * `document.head`). That's harmless when consumers install the\n * editor from npm dist (the dev branch is dead-coded out). It only\n * matters when a consumer source-resolves this package, which is\n * unusual outside this repo's own playground.\n * - `replaceSync` strips `@import` rules per the CSSOM spec. Styles\n * containing `@import` are skipped silently (the catch below). The\n * primary bundled sheet covers Tailwind imports already, so this\n * should never matter in practice.\n */\nfunction attachDevStyleMirror(shadowRoot: ShadowRoot): () => void {\n if (!import.meta.env?.DEV) return () => {};\n\n function buildSheets(): CSSStyleSheet[] {\n const sheets: CSSStyleSheet[] = [];\n // shadow-ok: dev-only HMR style mirror (production path is plugin-driven via adoptedStyleSheets)\n document.head.querySelectorAll(\"style\").forEach((el) => {\n const text = el.textContent;\n if (!text) return;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(text);\n sheets.push(sheet);\n } catch {\n // Skip styles that contain disallowed constructs (e.g. `@import`).\n }\n });\n return sheets;\n }\n\n function refresh(): void {\n // Keep the editor's primary (bundled) sheet first so its declarations\n // are the cascade fallback; dev sheets adopted after may override them\n // — same precedence model as production where everything ends up in\n // one stylesheet anyway.\n shadowRoot.adoptedStyleSheets = [getEditorStyleSheet(), ...buildSheets()];\n }\n\n refresh();\n\n const observer = new MutationObserver(() => refresh());\n // childList catches new/removed <style> tags; characterData + subtree\n // catches Vite HMR mutating an existing style's textContent in place.\n // shadow-ok: dev-only HMR style mirror observer\n observer.observe(document.head, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n\n return () => observer.disconnect();\n}\n\n/**\n * Resolve where Vue should mount: directly on the consumer's container\n * (light-DOM mode) or on a fresh `<div>` inside a newly-attached open\n * shadow root (shadow mode). In shadow mode, also attaches the editor's\n * cached `CSSStyleSheet` to the root so chrome renders with the right\n * styles inside the boundary, plus a dev-only mirror that observes\n * `document.head` (see `attachDevStyleMirror`).\n *\n * Idempotent: a second call on the same container reuses any existing\n * shadow root, clearing its contents so a prior mount's stale DOM doesn't\n * accumulate.\n */\nfunction resolveMountTarget(\n container: Element,\n shadowDom: boolean | undefined,\n): MountTarget {\n if (!shadowDom) {\n return { target: container, shadowRoot: null, cleanup: () => {} };\n }\n\n const shadowRoot =\n container.shadowRoot ?? container.attachShadow({ mode: \"open\" });\n\n // Adopt the editor stylesheet. Idempotent — repeated assignment of the\n // same sheet is fine. Dev mirror (below) will overwrite this with the\n // primary sheet + mirrored sheets, then keep them in sync.\n shadowRoot.adoptedStyleSheets = [getEditorStyleSheet()];\n\n // Clear stale content from a prior mount (re-init on same container).\n while (shadowRoot.firstChild) {\n shadowRoot.removeChild(shadowRoot.firstChild);\n }\n\n const host = document.createElement(\"div\");\n host.className = \"tpl-editor-host\";\n // Match the container's layout. Without an explicit size the host is a\n // default block element (height: auto) and the editor's `tpl:h-full`\n // template root collapses to content height because the height-100% chain\n // breaks at the shadow boundary. `width: 100%` is redundant for block\n // elements but harmless and explicit.\n host.style.cssText = \"display:block;height:100%;width:100%;\";\n shadowRoot.appendChild(host);\n\n const cleanup = attachDevStyleMirror(shadowRoot);\n\n return { target: host, shadowRoot, cleanup };\n}\n\n// ---------------------------------------------------------------------------\n// OSS init — sync\n// ---------------------------------------------------------------------------\n\ninterface OssEntry {\n app: App;\n editorRef: Ref<InstanceType<typeof Editor> | null>;\n /** Tear down dev-mode side effects (style mirror observer, etc.). */\n cleanup: () => void;\n}\n\n// Per-container registry so two `init()` calls with different containers\n// produce independent editor instances (multi-instance support). Re-init\n// on the same container still auto-unmounts the previous instance.\nconst ossEntries = new Map<Element, OssEntry>();\n\n// \"Last init'd\" container preserves the legacy single-instance behavior\n// of the top-level `unmount()` export — the playground (and other one-\n// editor consumers) calls bare `unmount()` and expects it to tear down\n// whatever was most recently mounted.\nlet lastOssContainer: Element | null = null;\n\nfunction unmountOssContainer(container: Element): void {\n const entry = ossEntries.get(container);\n if (!entry) return;\n entry.cleanup();\n entry.app.unmount();\n ossEntries.delete(container);\n if (lastOssContainer === container) {\n lastOssContainer = null;\n }\n}\n\nexport async function init(\n config: TemplaticalEditorConfig,\n): Promise<TemplaticalEditor> {\n const container =\n typeof config.container === \"string\"\n ? document.querySelector(config.container)\n : config.container;\n\n if (!container) {\n throw new Error(\n `[Templatical] Container element not found: ${config.container}`,\n );\n }\n\n // Load translations before mounting so child components can use useI18n synchronously\n const translations = await loadTranslations(config.locale ?? \"en\");\n\n // Create fonts manager to pass to Editor\n const fontsManager = useFonts(config.fonts);\n\n // Auto-unmount any prior instance on the SAME container *after* awaits\n // — checking before the await would let two concurrent init() calls\n // both pass the guard and orphan the first-mounted app on this\n // container.\n unmountOssContainer(container);\n\n const mount = resolveMountTarget(container, config.shadowDom ?? true);\n const editorRef: Ref<InstanceType<typeof Editor> | null> = ref(null);\n\n const app = createApp({\n setup() {\n return () =>\n h(Editor, {\n config,\n translations,\n fontsManager,\n shadowRoot: mount.shadowRoot ?? undefined,\n ref: editorRef,\n });\n },\n });\n\n app.mount(mount.target);\n\n ossEntries.set(container, { app, editorRef, cleanup: mount.cleanup });\n lastOssContainer = container;\n\n const instance: TemplaticalEditor = {\n getContent() {\n if (editorRef.value) {\n return JSON.parse(JSON.stringify(editorRef.value.getContent()));\n }\n return JSON.parse(JSON.stringify(config.content));\n },\n setContent(content: TemplateContent) {\n if (editorRef.value) {\n editorRef.value.setContent(content);\n }\n config.content = content;\n },\n setTheme(theme: UiTheme) {\n if (editorRef.value) {\n editorRef.value.setTheme(theme);\n }\n },\n unmount: () => unmountOssContainer(container),\n renderCustomBlock(block: CustomBlock) {\n if (!editorRef.value) {\n return Promise.reject(new Error(\"[Templatical] Editor not ready\"));\n }\n return editorRef.value.renderCustomBlock(block);\n },\n toMjml: () => toMjmlForInstance(instance),\n };\n\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Cloud init — async, flat config, tree-shaken when not called\n// ---------------------------------------------------------------------------\n\ninterface CloudEntry {\n app: App;\n editorRef: Ref<InstanceType<\n typeof import(\"./cloud/CloudEditor.vue\").default\n > | null>;\n cleanup: () => void;\n}\n\nconst cloudEntries = new Map<Element, CloudEntry>();\nlet lastCloudContainer: Element | null = null;\n\nfunction unmountCloudContainer(container: Element): void {\n const entry = cloudEntries.get(container);\n if (!entry) return;\n entry.cleanup();\n entry.app.unmount();\n cloudEntries.delete(container);\n if (lastCloudContainer === container) {\n lastCloudContainer = null;\n }\n}\n\nexport async function initCloud(\n config: import(\"./cloud/CloudEditor.vue\").TemplaticalCloudEditorConfig,\n): Promise<TemplaticalCloudEditor> {\n const container =\n typeof config.container === \"string\"\n ? document.querySelector(config.container)\n : config.container;\n\n if (!container) {\n throw new Error(\n `[Templatical] Container element not found: ${config.container}`,\n );\n }\n\n // Dynamic import — CloudEditor.vue is tree-shaken from the OSS bundle\n const { default: CloudEditor } = await import(\"./cloud/CloudEditor.vue\");\n\n // Load OSS + cloud translations in parallel so child components can use\n // useI18n / useCloudI18n synchronously\n const [translations, cloudTranslations] = await Promise.all([\n loadTranslations(config.locale ?? \"en\"),\n loadCloudTranslations(config.locale ?? \"en\"),\n ]);\n\n // Create fonts manager to pass to CloudEditor\n const fontsManager = useFonts(config.fonts);\n\n // Auto-unmount any prior instance on the SAME container *after* awaits\n // — checking before the await would let two concurrent initCloud()\n // calls both pass the guard and orphan the first-mounted app on this\n // container.\n unmountCloudContainer(container);\n\n const mount = resolveMountTarget(container, config.shadowDom ?? true);\n const cloudEditorRef: CloudEntry[\"editorRef\"] = ref(null);\n\n // Promise that resolves when CloudEditor emits 'ready'\n const readyPromise = new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"[Templatical] Cloud editor initialization timed out\"));\n }, INIT_TIMEOUT_MS);\n\n const app = createApp({\n setup() {\n return () =>\n h(CloudEditor, {\n config,\n translations,\n cloudTranslations,\n fontsManager,\n shadowRoot: mount.shadowRoot ?? undefined,\n ref: cloudEditorRef,\n onReady: () => {\n clearTimeout(timeout);\n resolve();\n },\n });\n },\n });\n\n app.mount(mount.target);\n\n cloudEntries.set(container, {\n app,\n editorRef: cloudEditorRef,\n cleanup: mount.cleanup,\n });\n lastCloudContainer = container;\n });\n\n await readyPromise;\n\n const instance: TemplaticalCloudEditor = {\n getContent() {\n if (cloudEditorRef.value) {\n return JSON.parse(JSON.stringify(cloudEditorRef.value.getContent()));\n }\n return JSON.parse(JSON.stringify(config.content));\n },\n setContent(content: TemplateContent) {\n if (cloudEditorRef.value) {\n cloudEditorRef.value.setContent(content);\n }\n },\n setTheme(theme: UiTheme) {\n if (cloudEditorRef.value) {\n cloudEditorRef.value.setTheme(theme);\n }\n },\n unmount: () => unmountCloudContainer(container),\n create(content?: TemplateContent) {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.create(content);\n },\n load(templateId: string) {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.load(templateId);\n },\n save() {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.save();\n },\n };\n\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Unmount helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Unmount the most-recently-created OSS editor. Single-instance legacy\n * API — callers managing multiple editors should use `instance.unmount()`\n * from each returned object, which targets the specific container.\n */\nexport function unmount(): void {\n if (lastOssContainer) {\n unmountOssContainer(lastOssContainer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport type { TemplaticalCloudEditorConfig } from \"./cloud/CloudEditor.vue\";\nexport type {\n BlockDefaults,\n TemplateContent,\n TemplateDefaults,\n ThemeOverrides,\n UiTheme,\n MergeTagsConfig,\n DisplayConditionsConfig,\n CustomBlockDefinition,\n ViewportSize,\n CustomFont,\n FontsConfig,\n SaveResult,\n Template,\n} from \"@templatical/types\";\n\nexport type { UseFontsReturn, FontOption } from \"./composables/useFonts\";\nexport { useFonts } from \"./composables/useFonts\";\nexport type { EditorCapabilities } from \"./types/editor-capabilities\";\n\nexport {\n getSupportedLocales,\n getSupportedCloudLocales,\n isLocaleSupported,\n isCloudLocaleSupported,\n getBaseLocale,\n} from \"./i18n\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"editor.js","names":[],"sources":["../../src/Editor.vue","../../src/Editor.vue","../../src/i18n/index.ts","../../src/utils/toMjml.ts","../../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport { resolveAccessibilityOptions } from \"./utils/resolveAccessibilityOptions\";\nimport type { Translations } from \"./i18n\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\n\nimport { RotateCcw } from \"@lucide/vue\";\nimport Canvas from \"./components/Canvas.vue\";\nimport Sidebar from \"./components/Sidebar.vue\";\nimport RightSidebar from \"./components/RightSidebar.vue\";\nimport ViewportToggle from \"./components/ViewportToggle.vue\";\nimport PreviewToggle from \"./components/PreviewToggle.vue\";\nimport DarkModeToggle from \"./components/DarkModeToggle.vue\";\nimport EditorFooter from \"./components/EditorFooter.vue\";\nimport \"./styles/index.css\";\n\nconst props = defineProps<{\n config: TemplaticalEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n /**\n * Shadow root the editor is mounted into. Supplied by `init()` when\n * `shadowDom: true`; undefined in light-DOM mode. Passed through to\n * `useEditorCore` so shadow-DOM-aware composables can resolve the\n * effective root via `EDITOR_ROOT_KEY`.\n */\n shadowRoot?: ShadowRoot;\n}>();\n\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// Outer `.tpl` ref — passed to `useEditorCore` so the active-editor\n// tracker can route keyboard shortcuts when two editors share a page.\nconst rootEl = ref<HTMLElement | null>(null);\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n containerEl: rootEl,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n editorRoot: props.shadowRoot,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\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 <!-- Header — absolute, full width, above everything -->\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 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\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 \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\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 </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\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 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\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 <div class=\"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 @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\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 @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n\n <!-- Popover mount — Teleport target for toolbars, link dialog, modal.\n Replaces the historical body-level teleport pattern so popups\n render inside the editor's effective DOM root (shadow-aware). -->\n <div\n :ref=\"(el) => (core.popoverRoot.value = el as HTMLElement | null)\"\n class=\"tpl-popover-root\"\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 { onMounted, onUnmounted, ref } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport { resolveAccessibilityOptions } from \"./utils/resolveAccessibilityOptions\";\nimport type { Translations } from \"./i18n\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\n\nimport { RotateCcw } from \"@lucide/vue\";\nimport Canvas from \"./components/Canvas.vue\";\nimport Sidebar from \"./components/Sidebar.vue\";\nimport RightSidebar from \"./components/RightSidebar.vue\";\nimport ViewportToggle from \"./components/ViewportToggle.vue\";\nimport PreviewToggle from \"./components/PreviewToggle.vue\";\nimport DarkModeToggle from \"./components/DarkModeToggle.vue\";\nimport EditorFooter from \"./components/EditorFooter.vue\";\nimport \"./styles/index.css\";\n\nconst props = defineProps<{\n config: TemplaticalEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n /**\n * Shadow root the editor is mounted into. Supplied by `init()` when\n * `shadowDom: true`; undefined in light-DOM mode. Passed through to\n * `useEditorCore` so shadow-DOM-aware composables can resolve the\n * effective root via `EDITOR_ROOT_KEY`.\n */\n shadowRoot?: ShadowRoot;\n}>();\n\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// Outer `.tpl` ref — passed to `useEditorCore` so the active-editor\n// tracker can route keyboard shortcuts when two editors share a page.\nconst rootEl = ref<HTMLElement | null>(null);\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n containerEl: rootEl,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n editorRoot: props.shadowRoot,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\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 <!-- Header — absolute, full width, above everything -->\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 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\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 \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\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 </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\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 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\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 <div class=\"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 @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\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 @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n\n <!-- Popover mount — Teleport target for toolbars, link dialog, modal.\n Replaces the historical body-level teleport pattern so popups\n render inside the editor's effective DOM root (shadow-aware). -->\n <div\n :ref=\"(el) => (core.popoverRoot.value = el as HTMLElement | null)\"\n class=\"tpl-popover-root\"\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","/// <reference types=\"vite/client\" />\n\nimport type en from \"./locales/en\";\nimport type cloudEn from \"./locales/cloud/en\";\n\nexport type Translations = typeof en;\nexport type CloudTranslations = typeof cloudEn;\nexport type TranslationKey = keyof Translations;\n\n// Vite resolves these globs at build time. Adding a new locale file\n// automatically registers it — no array to keep in sync.\nconst ossModules = import.meta.glob<{ default: Translations }>(\n \"./locales/*.ts\",\n);\nconst cloudModules = import.meta.glob<{ default: CloudTranslations }>(\n \"./locales/cloud/*.ts\",\n);\n\nfunction localesFromGlob(modules: Record<string, unknown>): string[] {\n return Object.keys(modules)\n .map((path) => path.match(/\\/([^/]+)\\.ts$/)?.[1])\n .filter((locale): locale is string => Boolean(locale));\n}\n\nconst supportedLocales = localesFromGlob(ossModules);\nconst supportedCloudLocales = localesFromGlob(cloudModules);\n\nfunction canonicalize(locale: string): string {\n return locale.trim().replace(/_/g, \"-\").toLowerCase();\n}\n\nfunction findSupportedLocale(\n locale: string,\n supported: string[],\n): string | undefined {\n const canonical = canonicalize(locale);\n return supported.find((s) => canonicalize(s) === canonical);\n}\n\n/**\n * Get the base language code from a locale string.\n * e.g., 'en-GB' -> 'en', 'de_DE' -> 'de'\n */\nexport function getBaseLocale(locale: string): string {\n return canonicalize(locale).split(\"-\")[0];\n}\n\nfunction tryResolveLocale(\n locale: string,\n supported: string[],\n): string | undefined {\n return (\n findSupportedLocale(locale, supported) ??\n findSupportedLocale(getBaseLocale(locale), supported)\n );\n}\n\nfunction resolveLocale(locale: string, supported: string[]): string {\n return tryResolveLocale(locale, supported) ?? \"en\";\n}\n\n/**\n * Load OSS translations for a given locale.\n * Falls back to English if the locale is not supported.\n */\nexport async function loadTranslations(locale: string): Promise<Translations> {\n const target = resolveLocale(locale, supportedLocales);\n const loader = ossModules[`./locales/${target}.ts`];\n const mod = await loader();\n return mod.default;\n}\n\n/**\n * Load cloud translations for a given locale.\n * Cloud translations cover features available only via initCloud() —\n * AI, comments, collaboration, scoring, snapshots, plan limits, etc.\n * OSS contributors don't need to translate these; cloud locales fall back\n * to English independently of the OSS locale.\n */\nexport async function loadCloudTranslations(\n locale: string,\n): Promise<CloudTranslations> {\n const target = resolveLocale(locale, supportedCloudLocales);\n const loader = cloudModules[`./locales/cloud/${target}.ts`];\n const mod = await loader();\n return mod.default;\n}\n\n/** Check if a locale has OSS translations (matched by exact locale, then base). */\nexport function isLocaleSupported(locale: string): boolean {\n return tryResolveLocale(locale, supportedLocales) !== undefined;\n}\n\n/** Check if a locale has cloud translations (matched by exact locale, then base). */\nexport function isCloudLocaleSupported(locale: string): boolean {\n return tryResolveLocale(locale, supportedCloudLocales) !== undefined;\n}\n\n/** List of OSS-supported locales. */\nexport function getSupportedLocales(): string[] {\n return [...supportedLocales];\n}\n\n/** List of cloud-supported locales. May be a subset of OSS locales. */\nexport function getSupportedCloudLocales(): string[] {\n return [...supportedCloudLocales];\n}\n","import type { CustomBlock, TemplateContent } from \"@templatical/types\";\n\n/**\n * Minimal slice of the editor surface needed by `toMjmlForInstance`.\n * Decoupled from the full `TemplaticalEditor` type so this helper can be\n * tested in isolation with a stub object.\n */\nexport interface ToMjmlSource {\n getContent(): TemplateContent;\n renderCustomBlock(block: CustomBlock): Promise<string>;\n}\n\n/**\n * Lazy-load `@templatical/renderer` and render the editor's current content\n * to MJML, wiring the editor's own custom block resolver into the renderer's\n * `renderCustomBlock` callback.\n *\n * The renderer is an optional peer dependency (small, MIT-licensed). It is\n * only loaded when an export is actually requested. Consumers that don't\n * need MJML export at all (e.g., embedding the editor in an app where the\n * backend handles export) can omit the install entirely; calling `toMjml()`\n * in that case throws a clear error naming the missing package.\n *\n * The dynamic import is cached by the module system, so subsequent calls\n * skip the import overhead.\n */\nexport async function toMjmlForInstance(\n instance: ToMjmlSource,\n): Promise<string> {\n let renderer: typeof import(\"@templatical/renderer\");\n try {\n renderer = await import(\"@templatical/renderer\");\n } catch {\n throw new Error(\n \"[Templatical] toMjml() requires the @templatical/renderer package. Please install it.\",\n );\n }\n return renderer.renderToMjml(instance.getContent(), {\n renderCustomBlock: instance.renderCustomBlock,\n });\n}\n","// eslint-disable-next-line @typescript-eslint/triple-slash-reference -- ambient-module declaration must be loaded for cross-package typecheck (workspace path alias resolves to source)\n/// <reference path=\"./virtual-modules.d.ts\" />\nimport { createApp, h, ref, type App, type Ref } from \"vue\";\nimport { INIT_TIMEOUT_MS } from \"./constants/timeouts\";\nimport type {\n BlockDefaults,\n CustomBlock,\n CustomBlockDefinition,\n DisplayConditionsConfig,\n FontsConfig,\n MediaResult,\n MergeTagsConfig,\n SaveResult,\n Template,\n TemplateContent,\n TemplateDefaults,\n ThemeOverrides,\n UiTheme,\n} from \"@templatical/types\";\nimport type { MediaRequestContext } from \"@templatical/media-library\";\n\nimport Editor from \"./Editor.vue\";\nimport { loadTranslations, loadCloudTranslations } from \"./i18n\";\nimport { useFonts } from \"./composables\";\nimport { toMjmlForInstance } from \"./utils/toMjml\";\n// Compiled-CSS-as-string for shadow root adoption. The `virtual:editor-css`\n// module is owned by `scripts/inline-style-css-plugin.ts` — at build time it\n// captures every emitted CSS asset (Tailwind utilities + every `.vue` SFC\n// `<style>` block + `styles/index.css` rules) and replaces this import's\n// runtime value with the full library CSS string. In dev/test the plugin\n// returns `styles/index.css` source as a fallback.\n//\n// Separate concern from the side-effecting `import \"./styles/index.css\"` in\n// `Editor.vue` / `CloudEditor.vue`, which injects styles into `document.head`\n// for light-DOM mode and survives untouched.\n//\n// Ambient declaration for `virtual:editor-css` lives in `virtual-modules.d.ts`\n// referenced at the top of this file via triple-slash so it's visible to any\n// consumer typechecking through the workspace path alias.\nimport editorStylesInline from \"virtual:editor-css\";\n\n// ---------------------------------------------------------------------------\n// OSS config + return types\n// ---------------------------------------------------------------------------\n\nexport interface TemplaticalEditorConfig {\n container: string | HTMLElement;\n content?: TemplateContent;\n\n /**\n * Mount the editor inside a Shadow DOM (open mode) for CSS isolation\n * from the host page. Defaults to `true` — host stylesheets cannot\n * cascade past the shadow boundary into editor elements (`p`, `a`,\n * `input`, etc.), and editor utility classes never collide with host\n * class names.\n *\n * Set to `false` to mount in light DOM. Opt out when:\n * - Your host integration uses `document.querySelector` to reach\n * editor internals (with shadow DOM, use `container.shadowRoot\n * .querySelector(...)` instead).\n * - You need to support Firefox <101 or Safari <16.4, which lack the\n * `adoptedStyleSheets` API the shadow path relies on.\n *\n * Light-mode consumers should keep this set to `false` explicitly so\n * future SDK changes don't silently flip the default again.\n *\n * @default true\n */\n shadowDom?: boolean;\n\n onChange?: (content: TemplateContent) => void;\n onSave?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n\n onRequestMedia?: OnRequestMedia;\n\n mergeTags?: MergeTagsConfig;\n displayConditions?: DisplayConditionsConfig;\n customBlocks?: CustomBlockDefinition[];\n fonts?: FontsConfig;\n\n blockDefaults?: BlockDefaults;\n templateDefaults?: TemplateDefaults;\n\n theme?: ThemeOverrides;\n uiTheme?: UiTheme;\n locale?: string;\n\n /**\n * Show the \"Powered by Templatical\" footer. Defaults to `true`.\n * Set to `false` to hide the footer (no attribution required by the license).\n */\n branding?: boolean;\n\n /**\n * Accessibility linter (`@templatical/quality`) configuration.\n *\n * - When unset, the linter loads on demand once the user opens the panel.\n * - When `disabled: true`, the optional peer is never imported (saves the\n * chunk download) and the sidebar tab + inline badges are suppressed.\n * - `rules`/`thresholds` follow the shape exported by `@templatical/quality`.\n */\n accessibility?: import(\"@templatical/quality\").A11yOptions;\n}\n\n/** Function type for media browser requests, used by both OSS and Cloud editors. */\nexport type OnRequestMedia = (\n context?: MediaRequestContext,\n) => Promise<MediaResult | null>;\n\ninterface TemplaticalEditorBase {\n getContent(): TemplateContent;\n setContent(content: TemplateContent): void;\n setTheme(theme: UiTheme): void;\n unmount(): void;\n}\n\nexport interface TemplaticalEditor extends TemplaticalEditorBase {\n /**\n * Render the current template to an MJML string. Resolves custom blocks\n * via the editor's internal block registry. Throws if the optional\n * `@templatical/renderer` package is not installed.\n */\n toMjml(): Promise<string>;\n /**\n * Render a single custom block to its HTML representation, using the\n * registered custom block definition's template and the block's current\n * field values. Exposed for headless callers that want to reuse the\n * editor's renderer (e.g., to drive `@templatical/renderer`'s\n * `renderCustomBlock` option from outside the editor instance).\n */\n renderCustomBlock(block: CustomBlock): Promise<string>;\n}\n\n/**\n * Cloud editor does not expose `toMjml` or `renderCustomBlock`: the cloud\n * backend performs MJML conversion server-side with additional processing\n * (e.g., signed image URLs, attachment handling) that isn't available client\n * side. Use the cloud `save()` flow to persist content; the backend handles\n * MJML/HTML export from there.\n */\nexport interface TemplaticalCloudEditor extends TemplaticalEditorBase {\n create(content?: TemplateContent): Promise<Template>;\n load(templateId: string): Promise<Template>;\n save(): Promise<SaveResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Shadow root helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Module-cached `CSSStyleSheet` built once from the inline editor CSS string.\n * `adoptedStyleSheets` accepts the same sheet object across multiple shadow\n * roots — sharing one sheet costs zero per-instance memory, regardless of\n * how many editors mount.\n */\nlet cachedEditorStyleSheet: CSSStyleSheet | null = null;\nfunction getEditorStyleSheet(): CSSStyleSheet {\n if (cachedEditorStyleSheet === null) {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(editorStylesInline);\n cachedEditorStyleSheet = sheet;\n }\n return cachedEditorStyleSheet;\n}\n\ninterface MountTarget {\n target: Element;\n shadowRoot: ShadowRoot | null;\n /**\n * Disposer for any dev-mode side effects attached to the shadow root\n * (currently: the document.head `<style>` mirror's MutationObserver).\n * Always safe to call — no-op when no side effects were registered.\n */\n cleanup: () => void;\n}\n\n/**\n * Dev-only: mirror every `<style>` tag in `document.head` into the\n * shadow root's `adoptedStyleSheets`, and observe `document.head` so\n * Vite's HMR-injected style updates flow through to the shadow root\n * automatically.\n *\n * Background: Vite dev injects each `.vue` `<style scoped>` block as a\n * separate `<style>` element in `document.head` via HMR. Those don't\n * cross the shadow boundary, so a shadow-mounted editor in dev would\n * render with only the bundled `styles/index.css` rules — every SFC\n * scoped style (block selection outlines, sidebar layout, etc.) missing.\n *\n * In production builds, `inline-style-css-plugin.ts` captures every\n * emitted CSS asset at `generateBundle` time and inlines the full\n * library CSS as a single string adopted by the shadow root. This\n * dev-only mirror does the same thing at runtime by observing whatever\n * Vite ends up injecting.\n *\n * The dead-code-elimination on `import.meta.env.DEV` ensures the\n * observer + filter logic is stripped from production bundles entirely.\n *\n * Caveats:\n * - In a real consumer's Vite-dev environment, this would also adopt\n * the consumer's page-level styles (whatever they put in\n * `document.head`). That's harmless when consumers install the\n * editor from npm dist (the dev branch is dead-coded out). It only\n * matters when a consumer source-resolves this package, which is\n * unusual outside this repo's own playground.\n * - `replaceSync` strips `@import` rules per the CSSOM spec. Styles\n * containing `@import` are skipped silently (the catch below). The\n * primary bundled sheet covers Tailwind imports already, so this\n * should never matter in practice.\n */\nfunction attachDevStyleMirror(shadowRoot: ShadowRoot): () => void {\n if (!import.meta.env?.DEV) return () => {};\n\n function buildSheets(): CSSStyleSheet[] {\n const sheets: CSSStyleSheet[] = [];\n // shadow-ok: dev-only HMR style mirror (production path is plugin-driven via adoptedStyleSheets)\n document.head.querySelectorAll(\"style\").forEach((el) => {\n const text = el.textContent;\n if (!text) return;\n try {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(text);\n sheets.push(sheet);\n } catch {\n // Skip styles that contain disallowed constructs (e.g. `@import`).\n }\n });\n return sheets;\n }\n\n function refresh(): void {\n // Keep the editor's primary (bundled) sheet first so its declarations\n // are the cascade fallback; dev sheets adopted after may override them\n // — same precedence model as production where everything ends up in\n // one stylesheet anyway.\n shadowRoot.adoptedStyleSheets = [getEditorStyleSheet(), ...buildSheets()];\n }\n\n refresh();\n\n const observer = new MutationObserver(() => refresh());\n // childList catches new/removed <style> tags; characterData + subtree\n // catches Vite HMR mutating an existing style's textContent in place.\n // shadow-ok: dev-only HMR style mirror observer\n observer.observe(document.head, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n\n return () => observer.disconnect();\n}\n\n/**\n * Resolve where Vue should mount: directly on the consumer's container\n * (light-DOM mode) or on a fresh `<div>` inside a newly-attached open\n * shadow root (shadow mode). In shadow mode, also attaches the editor's\n * cached `CSSStyleSheet` to the root so chrome renders with the right\n * styles inside the boundary, plus a dev-only mirror that observes\n * `document.head` (see `attachDevStyleMirror`).\n *\n * Idempotent: a second call on the same container reuses any existing\n * shadow root, clearing its contents so a prior mount's stale DOM doesn't\n * accumulate.\n */\nfunction resolveMountTarget(\n container: Element,\n shadowDom: boolean | undefined,\n): MountTarget {\n if (!shadowDom) {\n return { target: container, shadowRoot: null, cleanup: () => {} };\n }\n\n const shadowRoot =\n container.shadowRoot ?? container.attachShadow({ mode: \"open\" });\n\n // Adopt the editor stylesheet. Idempotent — repeated assignment of the\n // same sheet is fine. Dev mirror (below) will overwrite this with the\n // primary sheet + mirrored sheets, then keep them in sync.\n shadowRoot.adoptedStyleSheets = [getEditorStyleSheet()];\n\n // Clear stale content from a prior mount (re-init on same container).\n while (shadowRoot.firstChild) {\n shadowRoot.removeChild(shadowRoot.firstChild);\n }\n\n const host = document.createElement(\"div\");\n host.className = \"tpl-editor-host\";\n // Match the container's layout. Without an explicit size the host is a\n // default block element (height: auto) and the editor's `tpl:h-full`\n // template root collapses to content height because the height-100% chain\n // breaks at the shadow boundary. `width: 100%` is redundant for block\n // elements but harmless and explicit.\n host.style.cssText = \"display:block;height:100%;width:100%;\";\n shadowRoot.appendChild(host);\n\n const cleanup = attachDevStyleMirror(shadowRoot);\n\n return { target: host, shadowRoot, cleanup };\n}\n\n// ---------------------------------------------------------------------------\n// OSS init — sync\n// ---------------------------------------------------------------------------\n\ninterface OssEntry {\n app: App;\n editorRef: Ref<InstanceType<typeof Editor> | null>;\n /** Tear down dev-mode side effects (style mirror observer, etc.). */\n cleanup: () => void;\n}\n\n// Per-container registry so two `init()` calls with different containers\n// produce independent editor instances (multi-instance support). Re-init\n// on the same container still auto-unmounts the previous instance.\nconst ossEntries = new Map<Element, OssEntry>();\n\n// \"Last init'd\" container preserves the legacy single-instance behavior\n// of the top-level `unmount()` export — the playground (and other one-\n// editor consumers) calls bare `unmount()` and expects it to tear down\n// whatever was most recently mounted.\nlet lastOssContainer: Element | null = null;\n\nfunction unmountOssContainer(container: Element): void {\n const entry = ossEntries.get(container);\n if (!entry) return;\n entry.cleanup();\n entry.app.unmount();\n ossEntries.delete(container);\n if (lastOssContainer === container) {\n lastOssContainer = null;\n }\n}\n\nexport async function init(\n config: TemplaticalEditorConfig,\n): Promise<TemplaticalEditor> {\n const container =\n typeof config.container === \"string\"\n ? document.querySelector(config.container)\n : config.container;\n\n if (!container) {\n throw new Error(\n `[Templatical] Container element not found: ${config.container}`,\n );\n }\n\n // Load translations before mounting so child components can use useI18n synchronously\n const translations = await loadTranslations(config.locale ?? \"en\");\n\n // Create fonts manager to pass to Editor\n const fontsManager = useFonts(config.fonts);\n\n // Auto-unmount any prior instance on the SAME container *after* awaits\n // — checking before the await would let two concurrent init() calls\n // both pass the guard and orphan the first-mounted app on this\n // container.\n unmountOssContainer(container);\n\n const mount = resolveMountTarget(container, config.shadowDom ?? true);\n const editorRef: Ref<InstanceType<typeof Editor> | null> = ref(null);\n\n const app = createApp({\n setup() {\n return () =>\n h(Editor, {\n config,\n translations,\n fontsManager,\n shadowRoot: mount.shadowRoot ?? undefined,\n ref: editorRef,\n });\n },\n });\n\n app.mount(mount.target);\n\n ossEntries.set(container, { app, editorRef, cleanup: mount.cleanup });\n lastOssContainer = container;\n\n const instance: TemplaticalEditor = {\n getContent() {\n if (editorRef.value) {\n return JSON.parse(JSON.stringify(editorRef.value.getContent()));\n }\n return JSON.parse(JSON.stringify(config.content));\n },\n setContent(content: TemplateContent) {\n if (editorRef.value) {\n editorRef.value.setContent(content);\n }\n config.content = content;\n },\n setTheme(theme: UiTheme) {\n if (editorRef.value) {\n editorRef.value.setTheme(theme);\n }\n },\n unmount: () => unmountOssContainer(container),\n renderCustomBlock(block: CustomBlock) {\n if (!editorRef.value) {\n return Promise.reject(new Error(\"[Templatical] Editor not ready\"));\n }\n return editorRef.value.renderCustomBlock(block);\n },\n toMjml: () => toMjmlForInstance(instance),\n };\n\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Cloud init — async, flat config, tree-shaken when not called\n// ---------------------------------------------------------------------------\n\ninterface CloudEntry {\n app: App;\n editorRef: Ref<InstanceType<\n typeof import(\"./cloud/CloudEditor.vue\").default\n > | null>;\n cleanup: () => void;\n}\n\nconst cloudEntries = new Map<Element, CloudEntry>();\nlet lastCloudContainer: Element | null = null;\n\nfunction unmountCloudContainer(container: Element): void {\n const entry = cloudEntries.get(container);\n if (!entry) return;\n entry.cleanup();\n entry.app.unmount();\n cloudEntries.delete(container);\n if (lastCloudContainer === container) {\n lastCloudContainer = null;\n }\n}\n\nexport async function initCloud(\n config: import(\"./cloud/CloudEditor.vue\").TemplaticalCloudEditorConfig,\n): Promise<TemplaticalCloudEditor> {\n const container =\n typeof config.container === \"string\"\n ? document.querySelector(config.container)\n : config.container;\n\n if (!container) {\n throw new Error(\n `[Templatical] Container element not found: ${config.container}`,\n );\n }\n\n // Dynamic import — CloudEditor.vue is tree-shaken from the OSS bundle\n const { default: CloudEditor } = await import(\"./cloud/CloudEditor.vue\");\n\n // Load OSS + cloud translations in parallel so child components can use\n // useI18n / useCloudI18n synchronously\n const [translations, cloudTranslations] = await Promise.all([\n loadTranslations(config.locale ?? \"en\"),\n loadCloudTranslations(config.locale ?? \"en\"),\n ]);\n\n // Create fonts manager to pass to CloudEditor\n const fontsManager = useFonts(config.fonts);\n\n // Auto-unmount any prior instance on the SAME container *after* awaits\n // — checking before the await would let two concurrent initCloud()\n // calls both pass the guard and orphan the first-mounted app on this\n // container.\n unmountCloudContainer(container);\n\n const mount = resolveMountTarget(container, config.shadowDom ?? true);\n const cloudEditorRef: CloudEntry[\"editorRef\"] = ref(null);\n\n // Promise that resolves when CloudEditor emits 'ready'\n const readyPromise = new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"[Templatical] Cloud editor initialization timed out\"));\n }, INIT_TIMEOUT_MS);\n\n const app = createApp({\n setup() {\n return () =>\n h(CloudEditor, {\n config,\n translations,\n cloudTranslations,\n fontsManager,\n shadowRoot: mount.shadowRoot ?? undefined,\n ref: cloudEditorRef,\n onReady: () => {\n clearTimeout(timeout);\n resolve();\n },\n });\n },\n });\n\n app.mount(mount.target);\n\n cloudEntries.set(container, {\n app,\n editorRef: cloudEditorRef,\n cleanup: mount.cleanup,\n });\n lastCloudContainer = container;\n });\n\n await readyPromise;\n\n const instance: TemplaticalCloudEditor = {\n getContent() {\n if (cloudEditorRef.value) {\n return JSON.parse(JSON.stringify(cloudEditorRef.value.getContent()));\n }\n return JSON.parse(JSON.stringify(config.content));\n },\n setContent(content: TemplateContent) {\n if (cloudEditorRef.value) {\n cloudEditorRef.value.setContent(content);\n }\n },\n setTheme(theme: UiTheme) {\n if (cloudEditorRef.value) {\n cloudEditorRef.value.setTheme(theme);\n }\n },\n unmount: () => unmountCloudContainer(container),\n create(content?: TemplateContent) {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.create(content);\n },\n load(templateId: string) {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.load(templateId);\n },\n save() {\n if (!cloudEditorRef.value) {\n return Promise.reject(\n new Error(\"[Templatical] Cloud editor not ready\"),\n );\n }\n return cloudEditorRef.value.save();\n },\n };\n\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Unmount helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Unmount the most-recently-created OSS editor. Single-instance legacy\n * API — callers managing multiple editors should use `instance.unmount()`\n * from each returned object, which targets the specific container.\n */\nexport function unmount(): void {\n if (lastOssContainer) {\n unmountOssContainer(lastOssContainer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport type { TemplaticalCloudEditorConfig } from \"./cloud/CloudEditor.vue\";\nexport type {\n BlockDefaults,\n TemplateContent,\n TemplateDefaults,\n ThemeOverrides,\n UiTheme,\n MergeTagsConfig,\n DisplayConditionsConfig,\n CustomBlockDefinition,\n ViewportSize,\n CustomFont,\n FontsConfig,\n SaveResult,\n Template,\n} from \"@templatical/types\";\n\nexport type { UseFontsReturn, FontOption } from \"./composables/useFonts\";\nexport { useFonts } from \"./composables/useFonts\";\nexport type { EditorCapabilities } from \"./types/editor-capabilities\";\n\nexport {\n getSupportedLocales,\n getSupportedCloudLocales,\n isLocaleSupported,\n isCloudLocaleSupported,\n getBaseLocale,\n} from \"./i18n\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAoBA,IAAM,IAAQ,GAcR,IAAS,GAAU;GACvB,SAAS,EAAM,OAAO;GACtB,kBAAkB,EAAM,OAAO;GAChC,CAAC,EAII,IAAS,EAAwB,KAAK,EAGtC,IAAO,EAAc;GACzB;GACA,aAAa;GACb,QAAQ;IACN,SAAS,EAAM,OAAO;IACtB,OAAO,EAAM,OAAO;IACpB,eAAe,EAAM,OAAO;IAC5B,cAAc,EAAM,OAAO;IAC3B,WAAW,EAAM,OAAO;IACxB,mBAAmB,EAAM,OAAO;IAChC,gBAAgB,EAAM,OAAO;IAC7B,eAAe,GAA4B,EAAM,OAAO;IACxD,QAAQ,EAAM,OAAO,eAEf,EAAM,OAAO,OAAQ,KAAK,MAAM,KAAK,UAAU,EAAO,MAAM,QAAQ,CAAC,CAAA,GACvE,KAAA;IACL;GACD,cAAc,EAAM;GACpB,cAAc,EAAM;GACpB,iBAAiB,EAAM,OAAO,WAC1B,EACE,gBACE,EAAM,OAAO,SACX,KAAK,MAAM,KAAK,UAAU,EAAO,MAAM,QAAQ,CAAC,CACjD,EACL,GACA;GACJ,YAAY,EAAM;GACnB,CAAC;SAGF,EAAU,YAAY;AACpB,SAAM,EAAM,aAAa,iBAAiB;IAC1C,EAEF,QAAkB;AAEhB,GADA,EAAM,aAAa,kBAAkB,EACrC,EAAK,SAAS;IACd,EAGF,EAAa;GACX,kBAAkB,EAAO,QAAQ;GACjC,aAAa,MAA6B,EAAO,WAAW,EAAQ;GACpE,WAAW,MAAmB,EAAO,WAAW,EAAM;GACtD,mBAAmB,EAAK,SAAS;GAClC,CAAC,kBAIA,EAqIM,OAAA;YApIA;GAAJ,KAAI;GACJ,OAAK,EAAA,CAAC,mDAAiD,EAAA,YACjC,EAAA,EAAM,CAAC,MAAM,UAAQ,CAAA,CAAA;GAC1C,kBAAgB,EAAA,EAAI,CAAC,cAAc;GACnC,OAAK,GAAE,EAAA,EAAI,CAAC,YAAY,MAAK;;GAG9B,EA+BS,UA/BT,GA+BS;aAtBP,EAAyD,OAAA,EAApD,OAAM,yCAAuC,EAAA,MAAA,GAAA;IAGlD,EAaM,OAbN,GAaM;KAZJ,EAGE,GAAA;MAFC,UAAU,EAAA,EAAM,CAAC,MAAM;MACvB,UAAQ,EAAA,EAAM,CAAC;;KAElB,EAGE,GAAA;MAFC,aAAW,EAAA,EAAM,CAAC,MAAM;MACxB,UAAQ,EAAA,EAAM,CAAC;;KAElB,EAGE,GAAA;MAFC,gBAAc,EAAA,EAAM,CAAC,MAAM;MAC3B,UAAQ,EAAA,EAAM,CAAC;;;aAKpB,EAEO,OAAA,EADL,OAAM,yEAAuE,EAAA,MAAA,GAAA;;KAKjF,EAA8C,GAAA,MAAA,MAAA,IAAA,EAAA,CAAA,CAAA,GAAA,CAA5B,EAAA,EAAM,CAAC,MAAM,YAAW,CAAA,CAAA;GAG1C,EAkCM,OAAA;IAjCJ,OAAK,EAAA,CAAC,sFAAoF,CAExE,EAAA,EAAM,CAAC,MAAM,cAAA,2BAAA,iCAAA,aAAA,CAAA,CAAA;IAD/B,OAAA,EAAA,YAAA,+CAA+D;OAS/D,EAYM,OAZN,GAYM,CAXJ,EAUa,GAAA,EAVD,MAAK,mBAAiB,EAAA;qBASvB,CAPD,EAAA,EAAI,CAAC,iBAAiB,gBAAgB,SAAA,GAAA,EAD9C,EAQS,UAAA;;KANP,OAAM;KACN,OAAA,EAAA,mBAAA,aAAkC;KACjC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,EAAI,CAAC,iBAAiB,OAAK;QAEnC,EAA0C,EAAA,GAAA,EAAA;KAA9B,MAAM;KAAK,gBAAc;UAAK,MAC1C,EAAG,EAAA,EAAI,CAAC,EAAE,cAAc,oBAAmB,EAAA,EAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA;;SAIjD,EASM,OATN,IASM,CARJ,EAOE,GAAA;IANC,UAAU,EAAA,EAAM,CAAC,MAAM;IACvB,SAAS,EAAA,EAAM,CAAC,QAAQ;IACxB,qBAAmB,EAAA,EAAM,CAAC,MAAM;IAChC,aAAW,EAAA,EAAM,CAAC,MAAM;IACxB,gBAAc,EAAA,EAAM,CAAC,MAAM;IAC3B,eAAc,EAAA,EAAM,CAAC;;;;;;;;;GAMpB,EAAA,OAAO,aAAQ,kBAAA,GAAA,EADvB,EAOE,GAAA;;IALC,kBAAc,CAAY,EAAA,EAAM,CAAC,MAAM,cAAA,2BAAA,gCAAA;;GAQ1C,EAQM,OAAA;IAPJ,OAAM;IACN,MAAK;IACL,aAAU;IACV,eAAY;IACX,cAAY,EAAA,EAAI,CAAC,EAAE,UAAU;QAE3B,EAAA,EAAI,CAAC,gBAAgB,aAAa,MAAK,EAAA,GAAA,GAAA;KAI5C,EAsBE,IAAA;IApBC,kBAAgB,EAAA,EAAM,CAAC,cAAc;IACrC,UAAU,EAAA,EAAM,CAAC,QAAQ,MAAM;IAC/B,eAAY,AAAA,EAAA,QAAY,MAAY,EAAA,EAAM,CAAC,YAAY,EAAA,EAAM,CAAC,MAAM,iBAAkB,EAAO;IAG7F,eAAY,AAAA,EAAA,aAAA;KAAiC,EAAA,EAAM,CAAC,MAAM,mBAA+B,EAAA,EAAI,CAAC,aAAa,YAAY,EAAA,EAAM,CAAC,MAAM,gBAAe;;IAOnJ,kBAAe,AAAA,EAAA,aAAA;KAAiC,EAAA,EAAM,CAAC,cAAc,SAAqB,EAAA,EAAI,CAAC,aAAa,eAAe,EAAA,EAAM,CAAC,cAAc,MAAK;;IAOrJ,kBAAe,AAAA,EAAA,QAAG,MAAY,EAAA,EAAM,CAAC,eAAe,EAAO;sDApBnD,EAAA,EAAM,CAAC,MAAM,YAAW,CAAA,CAAA;GA0BnC,EAGE,OAAA;IAFC,MAAM,MAAQ,EAAA,EAAI,CAAC,YAAY,QAAQ;IACxC,OAAM;;;;yCEtNN,IAAa,uBAAA,OAAA;CAAA,yBAAA,OAAA;CAAA,yBAAA,OAAA;CAAA,4BAAA,OAAA;CAAA,CAElB,EACK,IAAe,uBAAA,OAAA;CAAA,+BAAA,OAAA;CAAA,+BAAA,OAAA;CAAA,kCAAA,OAAA;CAAA,CAEpB;AAED,SAAS,EAAgB,GAA4C;AACnE,QAAO,OAAO,KAAK,EAAQ,CACxB,KAAK,MAAS,EAAK,MAAM,iBAAiB,GAAG,GAAG,CAChD,QAAQ,MAA6B,EAAQ,EAAQ;;AAG1D,IAAM,IAAmB,EAAgB,EAAW,EAC9C,IAAwB,EAAgB,EAAa;AAE3D,SAAS,EAAa,GAAwB;AAC5C,QAAO,EAAO,MAAM,CAAC,QAAQ,MAAM,IAAI,CAAC,aAAa;;AAGvD,SAAS,EACP,GACA,GACoB;CACpB,IAAM,IAAY,EAAa,EAAO;AACtC,QAAO,EAAU,MAAM,MAAM,EAAa,EAAE,KAAK,EAAU;;AAO7D,SAAgB,EAAc,GAAwB;AACpD,QAAO,EAAa,EAAO,CAAC,MAAM,IAAI,CAAC;;AAGzC,SAAS,EACP,GACA,GACoB;AACpB,QACE,EAAoB,GAAQ,EAAU,IACtC,EAAoB,EAAc,EAAO,EAAE,EAAU;;AAIzD,SAAS,EAAc,GAAgB,GAA6B;AAClE,QAAO,EAAiB,GAAQ,EAAU,IAAI;;AAOhD,eAAsB,EAAiB,GAAuC;CAE5E,IAAM,IAAS,EAAW,aADX,EAAc,GAAQ,EACE,CAAO;AAE9C,SAAO,MADW,GAAQ,EACf;;AAUb,eAAsB,EACpB,GAC4B;CAE5B,IAAM,IAAS,EAAa,mBADb,EAAc,GAAQ,EACU,CAAO;AAEtD,SAAO,MADW,GAAQ,EACf;;AAIb,SAAgB,GAAkB,GAAyB;AACzD,QAAO,EAAiB,GAAQ,EAAiB,KAAK,KAAA;;AAIxD,SAAgB,GAAuB,GAAyB;AAC9D,QAAO,EAAiB,GAAQ,EAAsB,KAAK,KAAA;;AAI7D,SAAgB,KAAgC;AAC9C,QAAO,CAAC,GAAG,EAAiB;;AAI9B,SAAgB,KAAqC;AACnD,QAAO,CAAC,GAAG,EAAsB;;;;AC/EnC,eAAsB,GACpB,GACiB;CACjB,IAAI;AACJ,KAAI;AACF,MAAW,MAAM,OAAO,iCAAA,MAAA,MAAA,EAAA,EAAA;SAClB;AACN,QAAU,MACR,wFACD;;AAEH,QAAO,EAAS,aAAa,EAAS,YAAY,EAAE,EAClD,mBAAmB,EAAS,mBAC7B,CAAC;;;;sCCsHA,IAA+C;AACnD,SAAS,KAAqC;AAC5C,KAAI,MAA2B,MAAM;EACnC,IAAM,IAAQ,IAAI,eAAe;AAEjC,EADA,EAAM,YAAY,GAAmB,EACrC,IAAyB;;AAE3B,QAAO;;AA+CT,SAAS,GAAqB,GAAoC;AACrC,cAAa;;AAsD1C,SAAS,EACP,GACA,GACa;AACb,KAAI,CAAC,EACH,QAAO;EAAE,QAAQ;EAAW,YAAY;EAAM,eAAe;EAAI;CAGnE,IAAM,IACJ,EAAU,cAAc,EAAU,aAAa,EAAE,MAAM,QAAQ,CAAC;AAQlE,MAHA,EAAW,qBAAqB,CAAC,IAAqB,CAAC,EAGhD,EAAW,YAChB,GAAW,YAAY,EAAW,WAAW;CAG/C,IAAM,IAAO,SAAS,cAAc,MAAM;AAY1C,QAXA,EAAK,YAAY,mBAMjB,EAAK,MAAM,UAAU,yCACrB,EAAW,YAAY,EAAK,EAIrB;EAAE,QAAQ;EAAM;EAAY,SAFnB,GAAqB,EAEF;EAAS;;AAiB9C,IAAM,oBAAa,IAAI,KAAwB,EAM3C,IAAmC;AAEvC,SAAS,EAAoB,GAA0B;CACrD,IAAM,IAAQ,EAAW,IAAI,EAAU;AAClC,OACL,EAAM,SAAS,EACf,EAAM,IAAI,SAAS,EACnB,EAAW,OAAO,EAAU,EACxB,MAAqB,MACvB,IAAmB;;AAIvB,eAAsB,GACpB,GAC4B;CAC5B,IAAM,IACJ,OAAO,EAAO,aAAc,WACxB,SAAS,cAAc,EAAO,UAAU,GACxC,EAAO;AAEb,KAAI,CAAC,EACH,OAAU,MACR,8CAA8C,EAAO,YACtD;CAIH,IAAM,IAAe,MAAM,EAAiB,EAAO,UAAU,KAAK,EAG5D,IAAe,EAAS,EAAO,MAAM;AAM3C,GAAoB,EAAU;CAE9B,IAAM,IAAQ,EAAmB,GAAW,EAAO,aAAa,GAAK,EAC/D,IAAqD,EAAI,KAAK,EAE9D,IAAM,EAAU,EACpB,QAAQ;AACN,eACE,EAAE,IAAQ;GACR;GACA;GACA;GACA,YAAY,EAAM,cAAc,KAAA;GAChC,KAAK;GACN,CAAC;IAEP,CAAC;AAKF,CAHA,EAAI,MAAM,EAAM,OAAO,EAEvB,EAAW,IAAI,GAAW;EAAE;EAAK;EAAW,SAAS,EAAM;EAAS,CAAC,EACrE,IAAmB;CAEnB,IAAM,IAA8B;EAClC,aAAa;AAIX,UAFS,KAAK,MAAM,KAAK,UADrB,EAAU,QACqB,EAAU,MAAM,YAAY,GAE9B,EAAO,QAH1B,CACmD;;EAInE,WAAW,GAA0B;AAInC,GAHI,EAAU,SACZ,EAAU,MAAM,WAAW,EAAQ,EAErC,EAAO,UAAU;;EAEnB,SAAS,GAAgB;AACvB,GAAI,EAAU,SACZ,EAAU,MAAM,SAAS,EAAM;;EAGnC,eAAe,EAAoB,EAAU;EAC7C,kBAAkB,GAAoB;AAIpC,UAHK,EAAU,QAGR,EAAU,MAAM,kBAAkB,EAAM,GAFtC,QAAQ,OAAO,gBAAI,MAAM,iCAAiC,CAAC;;EAItE,cAAc,GAAkB,EAAS;EAC1C;AAED,QAAO;;AAeT,IAAM,oBAAe,IAAI,KAA0B,EAC/C,IAAqC;AAEzC,SAAS,EAAsB,GAA0B;CACvD,IAAM,IAAQ,EAAa,IAAI,EAAU;AACpC,OACL,EAAM,SAAS,EACf,EAAM,IAAI,SAAS,EACnB,EAAa,OAAO,EAAU,EAC1B,MAAuB,MACzB,IAAqB;;AAIzB,eAAsB,GACpB,GACiC;CACjC,IAAM,IACJ,OAAO,EAAO,aAAc,WACxB,SAAS,cAAc,EAAO,UAAU,GACxC,EAAO;AAEb,KAAI,CAAC,EACH,OAAU,MACR,8CAA8C,EAAO,YACtD;CAIH,IAAM,EAAE,SAAS,MAAgB,MAAM,OAAO,qCAIxC,CAAC,GAAc,KAAqB,MAAM,QAAQ,IAAI,CAC1D,EAAiB,EAAO,UAAU,KAAK,EACvC,EAAsB,EAAO,UAAU,KAAK,CAC7C,CAAC,EAGI,IAAe,EAAS,EAAO,MAAM;AAM3C,GAAsB,EAAU;CAEhC,IAAM,IAAQ,EAAmB,GAAW,EAAO,aAAa,GAAK,EAC/D,IAA0C,EAAI,KAAK;AAkFzD,QA9CA,MAAM,IAjCmB,SAAe,GAAS,MAAW;EAC1D,IAAM,IAAU,iBAAiB;AAC/B,KAAO,gBAAI,MAAM,sDAAsD,CAAC;KACvE,EAAgB,EAEb,IAAM,EAAU,EACpB,QAAQ;AACN,gBACE,EAAE,GAAa;IACb;IACA;IACA;IACA;IACA,YAAY,EAAM,cAAc,KAAA;IAChC,KAAK;IACL,eAAe;AAEb,KADA,aAAa,EAAQ,EACrB,GAAS;;IAEZ,CAAC;KAEP,CAAC;AASF,EAPA,EAAI,MAAM,EAAM,OAAO,EAEvB,EAAa,IAAI,GAAW;GAC1B;GACA,WAAW;GACX,SAAS,EAAM;GAChB,CAAC,EACF,IAAqB;GAGjB,EA8CC;EA3CL,aAAa;AAIX,UAFS,KAAK,MAAM,KAAK,UADrB,EAAe,QACgB,EAAe,MAAM,YAAY,GAEnC,EAAO,QAHrB,CACmD;;EAIxE,WAAW,GAA0B;AACnC,GAAI,EAAe,SACjB,EAAe,MAAM,WAAW,EAAQ;;EAG5C,SAAS,GAAgB;AACvB,GAAI,EAAe,SACjB,EAAe,MAAM,SAAS,EAAM;;EAGxC,eAAe,EAAsB,EAAU;EAC/C,OAAO,GAA2B;AAMhC,UALK,EAAe,QAKb,EAAe,MAAM,OAAO,EAAQ,GAJlC,QAAQ,OACb,gBAAI,MAAM,uCAAuC,CAClD;;EAIL,KAAK,GAAoB;AAMvB,UALK,EAAe,QAKb,EAAe,MAAM,KAAK,EAAW,GAJnC,QAAQ,OACb,gBAAI,MAAM,uCAAuC,CAClD;;EAIL,OAAO;AAML,UALK,EAAe,QAKb,EAAe,MAAM,MAAM,GAJzB,QAAQ,OACb,gBAAI,MAAM,uCAAuC,CAClD;;EAMA;;AAYT,SAAgB,KAAgB;AAC9B,CAAI,KACF,EAAoB,EAAiB"}
@@ -590,12 +590,23 @@ function x(t) {
590
590
  function j() {
591
591
  a.isDirty = !0;
592
592
  }
593
+ function M(e) {
594
+ let t = f(a.content.blocks, e);
595
+ if (!t) return null;
596
+ let n = t.blocks.findIndex((t) => t.id === e);
597
+ return n === -1 ? null : {
598
+ targetSectionId: t.sectionId,
599
+ columnIndex: t.columnIndex,
600
+ index: n
601
+ };
602
+ }
593
603
  return {
594
604
  state: r(a),
595
605
  content: c,
596
606
  selectedBlock: l,
597
607
  savedBlockIds: u,
598
608
  isBlockLocked: p,
609
+ findBlockLocation: M,
599
610
  setContent: m,
600
611
  selectBlock: h,
601
612
  setViewport: g,
@@ -1,4 +1,4 @@
1
- import { Gt as e, R as t, Ut as n, Y as r, b as i, bn as a, c as o, nt as s, q as c, xn as l } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, R as t, Ut as n, Y as r, b as i, bn as a, c as o, nt as s, q as c, xn as l } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/linkifyjs@4.3.2/node_modules/linkifyjs/dist/linkify.mjs
3
3
  var u = "aaa1rp3bb0ott3vie4c1le2ogado5udhabi7c0ademy5centure6ountant0s9o1tor4d0s1ult4e0g1ro2tna4f0l1rica5g0akhan5ency5i0g1rbus3force5tel5kdn3l0ibaba4pay4lfinanz6state5y2sace3tom5m0azon4ericanexpress7family11x2fam3ica3sterdam8nalytics7droid5quan4z2o0l2partments8p0le4q0uarelle8r0ab1mco4chi3my2pa2t0e3s0da2ia2sociates9t0hleta5torney7u0ction5di0ble3o3spost5thor3o0s4w0s2x0a2z0ure5ba0by2idu3namex4d1k2r0celona5laycard4s5efoot5gains6seball5ketball8uhaus5yern5b0c1t1va3cg1n2d1e0ats2uty4er2rlin4st0buy5t2f1g1h0arti5i0ble3d1ke2ng0o3o1z2j1lack0friday9ockbuster8g1omberg7ue3m0s1w2n0pparibas9o0ats3ehringer8fa2m1nd2o0k0ing5sch2tik2on4t1utique6x2r0adesco6idgestone9oadway5ker3ther5ussels7s1t1uild0ers6siness6y1zz3v1w1y1z0h3ca0b1fe2l0l1vinklein9m0era3p2non3petown5ital0one8r0avan4ds2e0er0s4s2sa1e1h1ino4t0ering5holic7ba1n1re3c1d1enter4o1rn3f0a1d2g1h0anel2nel4rity4se2t2eap3intai5ristmas6ome4urch5i0priani6rcle4sco3tadel4i0c2y3k1l0aims4eaning6ick2nic1que6othing5ud3ub0med6m1n1o0ach3des3ffee4llege4ogne5m0mbank4unity6pany2re3uter5sec4ndos3struction8ulting7tact3ractors9oking4l1p2rsica5untry4pon0s4rses6pa2r0edit0card4union9icket5own3s1uise0s6u0isinella9v1w1x1y0mru3ou3z2dad1nce3ta1e1ing3sun4y2clk3ds2e0al0er2s3gree4livery5l1oitte5ta3mocrat6ntal2ist5si0gn4v2hl2iamonds6et2gital5rect0ory7scount3ver5h2y2j1k1m1np2o0cs1tor4g1mains5t1wnload7rive4tv2ubai3nlop4pont4rban5vag2r2z2earth3t2c0o2deka3u0cation8e1g1mail3erck5nergy4gineer0ing9terprises10pson4quipment8r0icsson6ni3s0q1tate5t1u0rovision8s2vents5xchange6pert3osed4ress5traspace10fage2il1rwinds6th3mily4n0s2rm0ers5shion4t3edex3edback6rrari3ero6i0delity5o2lm2nal1nce1ial7re0stone6mdale6sh0ing5t0ness6j1k1lickr3ghts4r2orist4wers5y2m1o0o0d1tball6rd1ex2sale4um3undation8x2r0ee1senius7l1ogans4ntier7tr2ujitsu5n0d2rniture7tbol5yi3ga0l0lery3o1up4me0s3p1rden4y2b0iz3d0n2e0a1nt0ing5orge5f1g0ee3h1i0ft0s3ves2ing5l0ass3e1obal2o4m0ail3bh2o1x2n1odaddy5ld0point6f2o0dyear5g0le4p1t1v2p1q1r0ainger5phics5tis4een3ipe3ocery4up4s1t1u0cci3ge2ide2tars5ru3w1y2hair2mburg5ngout5us3bo2dfc0bank7ealth0care8lp1sinki6re1mes5iphop4samitsu7tachi5v2k0t2m1n1ockey4ldings5iday5medepot5goods5s0ense7nda3rse3spital5t0ing5t0els3mail5use3w2r1sbc3t1u0ghes5yatt3undai7ibm2cbc2e1u2d1e0ee3fm2kano4l1m0amat4db2mo0bilien9n0c1dustries8finiti5o2g1k1stitute6urance4e4t0ernational10uit4vestments10o1piranga7q1r0ish4s0maili5t0anbul7t0au2v3jaguar4va3cb2e0ep2tzt3welry6io2ll2m0p2nj2o0bs1urg4t1y2p0morgan6rs3uegos4niper7kaufen5ddi3e0rryhotels6properties14fh2g1h1i0a1ds2m1ndle4tchen5wi3m1n1oeln3matsu5sher5p0mg2n2r0d1ed3uokgroup8w1y0oto4z2la0caixa5mborghini8er3nd0rover6xess5salle5t0ino3robe5w0yer5b1c1ds2ease3clerc5frak4gal2o2xus4gbt3i0dl2fe0insurance9style7ghting6ke2lly3mited4o2ncoln4k2ve1ing5k1lc1p2oan0s3cker3us3l1ndon4tte1o3ve3pl0financial11r1s1t0d0a3u0ndbeck6xe1ury5v1y2ma0drid4if1son4keup4n0agement7go3p1rket0ing3s4riott5shalls7ttel5ba2c0kinsey7d1e0d0ia3et2lbourne7me1orial6n0u2rckmsd7g1h1iami3crosoft7l1ni1t2t0subishi9k1l0b1s2m0a2n1o0bi0le4da2e1i1m1nash3ey2ster5rmon3tgage6scow4to0rcycles9v0ie4p1q1r1s0d2t0n1r2u0seum3ic4v1w1x1y1z2na0b1goya4me2vy3ba2c1e0c1t0bank4flix4work5ustar5w0s2xt0direct7us4f0l2g0o2hk2i0co2ke1on3nja3ssan1y5l1o0kia3rton4w0ruz3tv4p1r0a1w2tt2u1yc2z2obi1server7ffice5kinawa6layan0group9lo3m0ega4ne1g1l0ine5oo2pen3racle3nge4g0anic5igins6saka4tsuka4t2vh3pa0ge2nasonic7ris2s1tners4s1y3y2ccw3e0t2f0izer5g1h0armacy6d1ilips5one2to0graphy6s4ysio5ics1tet2ures6d1n0g1k2oneer5zza4k1l0ace2y0station9umbing5s3m1n0c2ohl2ker3litie5rn2st3r0axi3ess3ime3o0d0uctions8f1gressive8mo2perties3y5tection8u0dential9s1t1ub2w0c2y2qa1pon3uebec3st5racing4dio4e0ad1lestate6tor2y4cipes5d0stone5umbrella9hab3ise0n3t2liance6n0t0als5pair3ort3ublican8st0aurant8view0s5xroth6ich0ardli6oh3l1o1p2o0cks3deo3gers4om3s0vp3u0gby3hr2n2w0e2yukyu6sa0arland6fe0ty4kura4le1on3msclub4ung5ndvik0coromant12ofi4p1rl2s1ve2xo3b0i1s2c0b1haeffler7midt4olarships8ol3ule3warz5ience5ot3d1e0arch3t2cure1ity6ek2lect4ner3rvices6ven3w1x0y3fr2g1h0angrila6rp3ell3ia1ksha5oes2p0ping5uji3w3i0lk2na1gles5te3j1k0i0n2y0pe4l0ing4m0art3ile4n0cf3o0ccer3ial4ftbank4ware6hu2lar2utions7ng1y2y2pa0ce3ort2t3r0l2s1t0ada2ples4r1tebank4farm7c0group6ockholm6rage3e3ream4udio2y3yle4u0cks3pplies3y2ort5rf1gery5zuki5v1watch4iss4x1y0dney4stems6z2tab1ipei4lk2obao4rget4tamotors6r2too4x0i3c0i2d0k2eam2ch0nology8l1masek5nnis4va3f1g1h0d1eater2re6iaa2ckets5enda4ps2res2ol4j0maxx4x2k0maxx5l1m0all4n1o0day3kyo3ols3p1ray3shiba5tal3urs3wn2yota3s3r0ade1ing4ining5vel0ers0insurance16ust3v2t1ube2i1nes3shu4v0s2w1z2ua1bank3s2g1k1nicom3versity8o2ol2ps2s1y1z2va0cations7na1guard7c1e0gas3ntures6risign5mögensberater2ung14sicherung10t2g1i0ajes4deo3g1king4llas4n1p1rgin4sa1ion4va1o3laanderen9n1odka3lvo3te1ing3o2yage5u2wales2mart4ter4ng0gou5tch0es6eather0channel12bcam3er2site5d0ding5ibo2r3f1hoswho6ien2ki2lliamhill9n0dows4e1ners6me2olterskluwer11odside6rk0s2ld3w2s1tc1f3xbox3erox4ihuan4n2xx2yz3yachts4hoo3maxun5ndex5e1odobashi7ga2kohama6u0tube6t1un3za0ppos4ra3ero3ip2m1one3uerich6w2", d = "ελ1υ2бг1ел3дети4ею2католик6ом3мкд2он1сква6онлайн5рг3рус2ф2сайт3рб3укр3қаз3հայ3ישראל5קום3ابوظبي5رامكو5لاردن4بحرين5جزائر5سعودية6عليان5مغرب5مارات5یران5بارت2زار4يتك3ھارت5تونس4سودان3رية5شبكة4عراق2ب2مان4فلسطين6قطر3كاثوليك6وم3مصر2ليسيا5وريتانيا7قع4همراه5پاکستان7ڀارت4कॉम3नेट3भारत0म्3ोत5संगठन5বাংলা5ভারত2ৰত4ਭਾਰਤ4ભારત4ଭାରତ4இந்தியா6லங்கை6சிங்கப்பூர்11భారత్5ಭಾರತ4ഭാരതം5ලංකා4คอม3ไทย3ລາວ3გე2みんな3アマゾン4クラウド4グーグル4コム2ストア3セール3ファッション6ポイント4世界2中信1国1國1文网3亚马逊3企业2佛山2信息2健康2八卦2公司1益2台湾1灣2商城1店1标2嘉里0大酒店5在线2大拿2天主教3娱乐2家電2广东2微博2慈善2我爱你3手机2招聘2政务1府2新加坡2闻2时尚2書籍2机构2淡马锡3游戏2澳門2点看2移动2组织机构4网址1店1站1络2联通2谷歌2购物2通販2集团2電訊盈科4飞利浦3食品2餐厅2香格里拉3港2닷넷1컴2삼성2한국2", f = "numeric", p = "ascii", m = "alpha", h = "asciinumeric", g = "alphanumeric", _ = "domain", ee = "emoji", te = "scheme", v = "slashscheme", y = "whitespace";
4
4
  function b(e, t) {
@@ -1,4 +1,4 @@
1
- import { FontFamily as e } from "./dist-Cp0zXPAD.js";
1
+ import { FontFamily as e } from "./dist-DxD1kSdH.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-font-family@3.22.5_@tiptap+extension-text-style@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__/node_modules/@tiptap/extension-font-family/dist/index.js
3
3
  var t = e;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { Gt as e, c as t } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, c as t } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-superscript@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__@tiptap+pm@3.22.5/node_modules/@tiptap/extension-superscript/dist/index.js
3
3
  var n = t.create({
4
4
  name: "superscript",
@@ -1,4 +1,4 @@
1
- import { Color as e } from "./dist-Cp0zXPAD.js";
1
+ import { Color as e } from "./dist-DxD1kSdH.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-color@3.22.5_@tiptap+extension-text-style@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__/node_modules/@tiptap/extension-color/dist/index.js
3
3
  var t = e;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { Gt as e, Ht as t, Ut as n, c as r } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, Ht as t, Ut as n, c as r } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-highlight@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5_/node_modules/@tiptap/extension-highlight/dist/index.js
3
3
  var i = /(?:^|\s)(==(?!\s+==)((?:[^=]+))==(?!\s+==))$/, a = /(?:^|\s)(==(?!\s+==)((?:[^=]+))==(?!\s+==))/g, o = r.create({
4
4
  name: "highlight",
@@ -1,4 +1,4 @@
1
- import { Gt as e, c as t } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, c as t } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-underline@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5_/node_modules/@tiptap/extension-underline/dist/index.js
3
3
  var n = t.create({
4
4
  name: "underline",
@@ -1,4 +1,4 @@
1
- import { Gt as e, c as t } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, c as t } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-subscript@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__@tiptap+pm@3.22.5/node_modules/@tiptap/extension-subscript/dist/index.js
3
3
  var n = t.create({
4
4
  name: "subscript",
@@ -1,4 +1,4 @@
1
- import { Gt as e, c as t, i as n } from "./dist-iLBdeBDR.js";
1
+ import { Gt as e, c as t, i as n } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-text-style@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5_/node_modules/@tiptap/extension-text-style/dist/index.js
3
3
  var r = 20, i = (e, t = 0) => {
4
4
  let n = [];
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./rolldown-runtime-CAFD8bLK.js";
2
2
  import { $ as t, D as n, E as r, H as i, J as a, K as o, O as s, Q as ee, R as te, S as c, X as l, b as u, j as d, r as f, rt as ne, x as re } from "./vue.runtime.esm-bundler-Bxqkjqhc.js";
3
- import { $ as ie, $t as ae, A as oe, At as se, B as ce, Bt as le, C as ue, Ct as de, D as fe, Dt as p, E as m, Et as h, F as g, Ft as _, G as v, Gt as y, H as b, Ht as x, I as S, It as C, J as w, Jt as T, K as E, Kt as D, L as O, Lt as k, M as A, Mt as j, N as M, Nt as N, O as P, Ot as F, P as I, Pt as L, Q as R, Qt as z, R as B, Rt as V, S as pe, St as me, T as he, Tt as ge, U as _e, Ut as ve, V as ye, Vt as be, W as xe, Wt as Se, X as Ce, Xt as we, Y as Te, Yt as Ee, Z as De, Zt as Oe, _ as ke, _t as H, a as Ae, an as je, at as Me, b as Ne, bt as Pe, c as Fe, cn as Ie, ct as Le, d as Re, dn as ze, dt as Be, en as Ve, et as He, f as U, fn as Ue, ft as We, g as Ge, gt as W, h as Ke, hn as qe, ht as Je, i as Ye, in as Xe, it as Ze, j as Qe, jt as $e, k as et, kt as tt, l as G, ln as nt, lt as rt, m as it, mn as at, mt as ot, n as st, nn as ct, nt as lt, o as ut, on as K, ot as dt, p as ft, pn as pt, pt as mt, q as ht, qt as gt, r as _t, rn as vt, rt as yt, s as bt, sn as xt, st as St, t as Ct, tn as wt, tt as Tt, u as Et, un as Dt, ut as Ot, v as kt, vt as At, w as jt, wt as Mt, x as Nt, xt as Pt, y as q, yt as Ft, z as It, zt as Lt } from "./dist-iLBdeBDR.js";
3
+ import { $ as ie, $t as ae, A as oe, At as se, B as ce, Bt as le, C as ue, Ct as de, D as fe, Dt as p, E as m, Et as h, F as g, Ft as _, G as v, Gt as y, H as b, Ht as x, I as S, It as C, J as w, Jt as T, K as E, Kt as D, L as O, Lt as k, M as A, Mt as j, N as M, Nt as N, O as P, Ot as F, P as I, Pt as L, Q as R, Qt as z, R as B, Rt as V, S as pe, St as me, T as he, Tt as ge, U as _e, Ut as ve, V as ye, Vt as be, W as xe, Wt as Se, X as Ce, Xt as we, Y as Te, Yt as Ee, Z as De, Zt as Oe, _ as ke, _t as H, a as Ae, an as je, at as Me, b as Ne, bt as Pe, c as Fe, cn as Ie, ct as Le, d as Re, dn as ze, dt as Be, en as Ve, et as He, f as U, fn as Ue, ft as We, g as Ge, gt as W, h as Ke, hn as qe, ht as Je, i as Ye, in as Xe, it as Ze, j as Qe, jt as $e, k as et, kt as tt, l as G, ln as nt, lt as rt, m as it, mn as at, mt as ot, n as st, nn as ct, nt as lt, o as ut, on as K, ot as dt, p as ft, pn as pt, pt as mt, q as ht, qt as gt, r as _t, rn as vt, rt as yt, s as bt, sn as xt, st as St, t as Ct, tn as wt, tt as Tt, u as Et, un as Dt, ut as Ot, v as kt, vt as At, w as jt, wt as Mt, x as Nt, xt as Pt, y as q, yt as Ft, z as It, zt as Lt } from "./dist-CQQS9SRN.js";
4
4
  //#region ../../node_modules/.pnpm/@tiptap+vue-3@3.22.5_@floating-ui+dom@1.7.6_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__@tip_7097ea972d8e6e4e31801879e2c3d1b4/node_modules/@tiptap/vue-3/dist/index.js
5
5
  var Rt = /* @__PURE__ */ e({
6
6
  CommandManager: () => Ct,
@@ -1,6 +1,6 @@
1
- import { Cn as e, Ct as t, Dn as n, En as r, Gt as i, Ht as a, Jt as o, Mt as s, Nt as c, Qt as l, Sn as u, St as d, Tn as f, Ut as p, Z as m, _ as ee, _n as h, at as g, bn as _, c as v, gn as te, hn as y, i as b, jt as x, nn as ne, ot as re, pn as S, rt as ie, u as C, v as ae, vn as w, wn as oe, xn as T, yn as E } from "./dist-iLBdeBDR.js";
2
- import { Link as se } from "./dist-DDJIWTRY.js";
3
- import { Underline as ce } from "./dist-KoBJjK1G.js";
1
+ import { Cn as e, Ct as t, Dn as n, En as r, Gt as i, Ht as a, Jt as o, Mt as s, Nt as c, Qt as l, Sn as u, St as d, Tn as f, Ut as p, Z as m, _ as ee, _n as h, at as g, bn as _, c as v, gn as te, hn as y, i as b, jt as x, nn as ne, ot as re, pn as S, rt as ie, u as C, v as ae, vn as w, wn as oe, xn as T, yn as E } from "./dist-CQQS9SRN.js";
2
+ import { Link as se } from "./dist-B2jcQhv8.js";
3
+ import { Underline as ce } from "./dist-DUvhjQ2-.js";
4
4
  //#region ../../node_modules/.pnpm/@tiptap+core@3.22.5_@tiptap+pm@3.22.5/node_modules/@tiptap/core/dist/jsx-runtime/jsx-runtime.js
5
5
  var D = (e, t) => {
6
6
  if (e === "slot") return 0;
@@ -1,4 +1,4 @@
1
- import { i as e } from "./dist-iLBdeBDR.js";
1
+ import { i as e } from "./dist-CQQS9SRN.js";
2
2
  //#region ../../node_modules/.pnpm/@tiptap+extension-text-align@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5_/node_modules/@tiptap/extension-text-align/dist/index.js
3
3
  var t = e.create({
4
4
  name: "textAlign",
@@ -2,8 +2,8 @@ import { A as e, B as t, E as n, M as r, S as i, X as a, a as o, at as s, b as c
2
2
  import { C as S, D as C, n as w, w as T } from "./dist-DJmnUmW9.js";
3
3
  import { t as E } from "./useI18n-PEB8ioi_.js";
4
4
  import { t as D } from "./useMergeTag-C47xwU7X.js";
5
- import { F as O, Gt as k, _n as A, bn as j, i as M, o as N, p as P, u as F, vn as ee, xn as I } from "./dist-iLBdeBDR.js";
6
- import { n as L, t as R } from "./dist-Pk4MhWMP.js";
5
+ import { F as O, Gt as k, _n as A, bn as j, i as M, o as N, p as P, u as F, vn as ee, xn as I } from "./dist-CQQS9SRN.js";
6
+ import { n as L, t as R } from "./dist-U5guDm2w.js";
7
7
  //#region src/extensions/FontSize.ts
8
8
  var te = M.create({
9
9
  name: "fontSize",
@@ -694,14 +694,16 @@ var he = M.create({
694
694
  }).run();
695
695
  },
696
696
  render: () => {
697
- let e = null, r = null, o = null, s = a([]), c = a(0), l = null, u = `tpl-merge-tag-suggestion-${++me}`, d = null, f = [];
698
- function p() {
699
- y(d?.() ?? null);
700
- }
697
+ let e = null, r = null, o = null, s = a([]), c = a(0), l = null, u = `tpl-merge-tag-suggestion-${++me}`, d = null, f = [], p = null;
701
698
  function m() {
702
- p(), requestAnimationFrame(p);
699
+ b(d?.() ?? null);
700
+ }
701
+ function g() {
702
+ m(), p !== null && cancelAnimationFrame(p), p = requestAnimationFrame(() => {
703
+ p = null, m();
704
+ });
703
705
  }
704
- function g(e) {
706
+ function _(e) {
705
707
  let t = [], n = e?.parentElement ?? null;
706
708
  for (; n && n !== document.body && n !== document.documentElement;) {
707
709
  let e = window.getComputedStyle(n), r = e.overflow + e.overflowX + e.overflowY;
@@ -709,19 +711,19 @@ var he = M.create({
709
711
  }
710
712
  return t;
711
713
  }
712
- function _(e) {
713
- f = [window, ...g(e)];
714
- for (let e of f) e.addEventListener("scroll", p, {
714
+ function v(e) {
715
+ f = [window, ..._(e)];
716
+ for (let e of f) e.addEventListener("scroll", m, {
715
717
  passive: !0,
716
718
  capture: !0
717
719
  });
718
- window.addEventListener("resize", p, { passive: !0 });
720
+ window.addEventListener("resize", m, { passive: !0 });
719
721
  }
720
- function v() {
721
- for (let e of f) e.removeEventListener("scroll", p, { capture: !0 });
722
- window.removeEventListener("resize", p), f = [];
722
+ function y() {
723
+ for (let e of f) e.removeEventListener("scroll", m, { capture: !0 });
724
+ window.removeEventListener("resize", m), f = [];
723
725
  }
724
- function y(e) {
726
+ function b(e) {
725
727
  if (!r || !e || e.bottom < 0 || e.top > window.innerHeight) return;
726
728
  r.style.position = "fixed", r.style.left = `${e.left}px`, r.style.zIndex = "9999", r.style.top = `${e.bottom}px`;
727
729
  let t = r.offsetHeight;
@@ -730,7 +732,7 @@ var he = M.create({
730
732
  r.style.top = `${n}px`;
731
733
  }
732
734
  }
733
- function b(e, t) {
735
+ function x(e, t) {
734
736
  let n = t?.closest("[data-tpl-theme]");
735
737
  if (!n) return;
736
738
  let r = n.getAttribute("data-tpl-theme");
@@ -742,10 +744,10 @@ var he = M.create({
742
744
  }
743
745
  e.style.fontFamily = i.fontFamily, e.style.fontSize = i.fontSize, e.style.lineHeight = i.lineHeight;
744
746
  }
745
- function x(e) {
747
+ function S(e) {
746
748
  o && (e ? (o.setAttribute("role", "combobox"), o.setAttribute("aria-haspopup", "listbox"), o.setAttribute("aria-expanded", "true"), o.setAttribute("aria-controls", u)) : (o.removeAttribute("aria-expanded"), o.removeAttribute("aria-controls"), o.removeAttribute("aria-activedescendant"), o.removeAttribute("aria-haspopup"), o.removeAttribute("role")));
747
749
  }
748
- function S() {
750
+ function C() {
749
751
  if (o) {
750
752
  if (s.value.length === 0) {
751
753
  o.removeAttribute("aria-activedescendant");
@@ -754,36 +756,36 @@ var he = M.create({
754
756
  o.setAttribute("aria-activedescendant", `${u}-opt-${c.value}`);
755
757
  }
756
758
  }
757
- function C(e) {
759
+ function w(e) {
758
760
  l?.(e);
759
761
  }
760
762
  return {
761
763
  onStart: (a) => {
762
764
  s.value = a.items, c.value = 0, l = (e) => a.command(e), r = document.createElement("div"), r.setAttribute("data-testid", "merge-tag-suggestion-popup");
763
765
  let f = a.editor.view?.dom;
764
- o = f ?? null, b(r, f ?? null), (n?.value ?? document.body).appendChild(r), e = h({ render() {
766
+ o = f ?? null, x(r, f ?? null), (n?.value ?? document.body).appendChild(r), e = h({ render() {
765
767
  return i(pe, {
766
768
  items: s.value,
767
769
  selectedIndex: c.value,
768
770
  emptyText: t,
769
771
  listId: u,
770
- onSelect: (e) => C(e),
772
+ onSelect: (e) => w(e),
771
773
  onHover: (e) => {
772
- c.value = e, S();
774
+ c.value = e, C();
773
775
  }
774
776
  });
775
- } }), e.mount(r), x(!0), S(), d = a.clientRect ?? null, m(), _(f ?? null);
777
+ } }), e.mount(r), S(!0), C(), d = a.clientRect ?? null, g(), v(f ?? null);
776
778
  },
777
779
  onUpdate: (e) => {
778
- s.value = e.items, c.value >= e.items.length && (c.value = 0), l = (t) => e.command(t), S(), d = e.clientRect ?? null, m();
780
+ s.value = e.items, c.value >= e.items.length && (c.value = 0), l = (t) => e.command(t), C(), d = e.clientRect ?? null, g();
779
781
  },
780
782
  onKeyDown: (e) => {
781
783
  if (e.event.key === "Escape") return !0;
782
- let t = $(e.event, s.value, c, C);
783
- return t && S(), t;
784
+ let t = $(e.event, s.value, c, w);
785
+ return t && C(), t;
784
786
  },
785
787
  onExit: () => {
786
- v(), x(!1), e?.unmount(), r?.remove(), e = null, r = null, o = null, l = null, d = null;
788
+ p !== null && (cancelAnimationFrame(p), p = null), y(), S(!1), e?.unmount(), r?.remove(), e = null, r = null, o = null, l = null, d = null;
787
789
  }
788
790
  };
789
791
  }
package/dist/index.d.ts CHANGED
@@ -311,9 +311,9 @@ declare module "@tiptap/core" {
311
311
 
312
312
  declare module "@tiptap/core" {
313
313
  interface Commands<ReturnType> {
314
- letterSpacing: {
315
- setLetterSpacing: (spacing: string) => ReturnType;
316
- unsetLetterSpacing: () => ReturnType;
314
+ lineHeight: {
315
+ setLineHeight: (lineHeight: string) => ReturnType;
316
+ unsetLineHeight: () => ReturnType;
317
317
  };
318
318
  }
319
319
  }
@@ -321,9 +321,8 @@ declare module "@tiptap/core" {
321
321
 
322
322
  declare module "@tiptap/core" {
323
323
  interface Commands<ReturnType> {
324
- lineHeight: {
325
- setLineHeight: (lineHeight: string) => ReturnType;
326
- unsetLineHeight: () => ReturnType;
324
+ mergeTagNode: {
325
+ insertMergeTag: (attrs: MergeTag) => ReturnType;
327
326
  };
328
327
  }
329
328
  }
@@ -331,8 +330,9 @@ declare module "@tiptap/core" {
331
330
 
332
331
  declare module "@tiptap/core" {
333
332
  interface Commands<ReturnType> {
334
- mergeTagNode: {
335
- insertMergeTag: (attrs: MergeTag) => ReturnType;
333
+ letterSpacing: {
334
+ setLetterSpacing: (spacing: string) => ReturnType;
335
+ unsetLetterSpacing: () => ReturnType;
336
336
  };
337
337
  }
338
338
  }