@templatical/editor 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/{AiChatSidebar-H7SIsxeC.js → AiChatSidebar-B74X6tIx.js} +3 -3
  2. package/dist/{AiFeatureMenu-CJGX-fP5.js → AiFeatureMenu-BOxQqkn9.js} +4 -4
  3. package/dist/{BlockA11yBadge-BhyoMZJ9.js → BlockIssueBadge-DJTiT1eF.js} +6 -6
  4. package/dist/{CloudEditor-BBs3p-mW.js → CloudEditor-BzCVDD6e.js} +24 -24
  5. package/dist/{CollaboratorBar-CiEo25lb.js → CollaboratorBar-Cb-c1IkS.js} +2 -2
  6. package/dist/{CommentsSidebar-DikjSLtH.js → CommentsSidebar-Qvppl-DR.js} +6 -6
  7. package/dist/{CountdownBlock-ByJ0_8Pk.js → CountdownBlock-D0IiMliC.js} +1 -1
  8. package/dist/{CountdownToolbar-Bn4RwJVN.js → CountdownToolbar-BVVT8U5C.js} +2 -2
  9. package/dist/{DesignReferenceSidebar-D08CLZPA.js → DesignReferenceSidebar-ucgtoDll.js} +6 -6
  10. package/dist/{AccessibilityPanel-BeUibrUb.js → IssuesPanel-XVdvrgmq.js} +18 -18
  11. package/dist/{ModuleBrowserModal-D6637s8j.js → ModuleBrowserModal-BotDsZ0R.js} +7 -7
  12. package/dist/{ModulePreviewCanvas-DBFmwq-y.js → ModulePreviewCanvas-CDg4MnJP.js} +2 -2
  13. package/dist/{NumberWithSuffix-CyFX9w1e.js → NumberWithSuffix-n-cr7lpU.js} +1 -1
  14. package/dist/{ParagraphEditor-DC2jOjNh.js → ParagraphEditor-CfzIUFQ_.js} +6 -6
  15. package/dist/{RichTextEditorContent-8NkjTIua.js → RichTextEditorContent-Cu9NEkKd.js} +3 -3
  16. package/dist/{SaveModuleDialog-NKT7bWWs.js → SaveModuleDialog-DuzfCamG.js} +6 -6
  17. package/dist/{SnapshotHistory-XNqYxPBj.js → SnapshotHistory-DiAPOSDM.js} +5 -5
  18. package/dist/{TemplateScoringPanel-kYOGtac3.js → TemplateScoringPanel-BzDVdfOx.js} +6 -6
  19. package/dist/{TestEmailModal-B_-Qo5Us.js → TestEmailModal-D5I5Mgtb.js} +3 -3
  20. package/dist/{TitleEditor-DnH3yyTr.js → TitleEditor-CZUkhtDo.js} +6 -6
  21. package/dist/{TplModal-BoEZFe3U.js → TplModal-CVq2BAIW.js} +3 -3
  22. package/dist/{blockTypeIcons-CUcYuWAO.js → blockTypeIcons-CJNuGjLX.js} +1 -1
  23. package/dist/bundle-stats.json +6 -6
  24. package/dist/cdn/chunks/{AiFeatureMenu-SxIqfqA0.js → AiFeatureMenu-DtrT-aFR.js} +2 -2
  25. package/dist/cdn/chunks/{AiFeatureMenu-SxIqfqA0.js.map → AiFeatureMenu-DtrT-aFR.js.map} +1 -1
  26. package/dist/cdn/chunks/{BlockA11yBadge-QqW6nomn.js → BlockIssueBadge-w7n4orSd.js} +6 -6
  27. package/dist/cdn/chunks/BlockIssueBadge-w7n4orSd.js.map +1 -0
  28. package/dist/cdn/chunks/{CloudEditor-ChWUIMw7.js → CloudEditor-CwG0DhG1.js} +101 -101
  29. package/dist/cdn/chunks/CloudEditor-CwG0DhG1.js.map +1 -0
  30. package/dist/cdn/chunks/{CollaboratorBar-73LfbzqF.js → CollaboratorBar-DYjZaz7H.js} +3 -3
  31. package/dist/cdn/chunks/{CollaboratorBar-73LfbzqF.js.map → CollaboratorBar-DYjZaz7H.js.map} +1 -1
  32. package/dist/cdn/chunks/{CountdownBlock-WYz9b9Ex.js → CountdownBlock-CFR8VJCU.js} +2 -2
  33. package/dist/cdn/chunks/{CountdownBlock-WYz9b9Ex.js.map → CountdownBlock-CFR8VJCU.js.map} +1 -1
  34. package/dist/cdn/chunks/{CountdownToolbar-B9fPfFqp.js → CountdownToolbar-DOrQCH57.js} +3 -3
  35. package/dist/cdn/chunks/{CountdownToolbar-B9fPfFqp.js.map → CountdownToolbar-DOrQCH57.js.map} +1 -1
  36. package/dist/cdn/chunks/{AccessibilityPanel-CQt87OMk.js → IssuesPanel-C54PhBgc.js} +15 -15
  37. package/dist/cdn/chunks/IssuesPanel-C54PhBgc.js.map +1 -0
  38. package/dist/cdn/chunks/{ModuleBrowserModal-DfnJQBkM.js → ModuleBrowserModal-4V6um-AC.js} +8 -8
  39. package/dist/cdn/chunks/{ModuleBrowserModal-DfnJQBkM.js.map → ModuleBrowserModal-4V6um-AC.js.map} +1 -1
  40. package/dist/cdn/chunks/{ModulePreviewCanvas-DE4_oHA_.js → ModulePreviewCanvas-DL8zI6VX.js} +24 -24
  41. package/dist/cdn/chunks/{ModulePreviewCanvas-DE4_oHA_.js.map → ModulePreviewCanvas-DL8zI6VX.js.map} +1 -1
  42. package/dist/cdn/chunks/{NumberWithSuffix-BST0yRor.js → NumberWithSuffix-avXrYDo6.js} +2 -2
  43. package/dist/cdn/chunks/{NumberWithSuffix-BST0yRor.js.map → NumberWithSuffix-avXrYDo6.js.map} +1 -1
  44. package/dist/cdn/chunks/{ParagraphEditor-CQfijF3i.js → ParagraphEditor-Cpmy85zL.js} +15 -15
  45. package/dist/cdn/chunks/{ParagraphEditor-CQfijF3i.js.map → ParagraphEditor-Cpmy85zL.js.map} +1 -1
  46. package/dist/cdn/chunks/{RichTextEditorContent-CBds8dnm.js → RichTextEditorContent-CFL-Lrv5.js} +2 -2
  47. package/dist/cdn/chunks/{RichTextEditorContent-CBds8dnm.js.map → RichTextEditorContent-CFL-Lrv5.js.map} +1 -1
  48. package/dist/cdn/chunks/{SaveModuleDialog-DXhuZgZ4.js → SaveModuleDialog-D6szy5vX.js} +4 -4
  49. package/dist/cdn/chunks/{SaveModuleDialog-DXhuZgZ4.js.map → SaveModuleDialog-D6szy5vX.js.map} +1 -1
  50. package/dist/cdn/chunks/{TitleEditor-BdQvH7eK.js → TitleEditor-CSDdss38.js} +7 -7
  51. package/dist/cdn/chunks/{TitleEditor-BdQvH7eK.js.map → TitleEditor-CSDdss38.js.map} +1 -1
  52. package/dist/cdn/chunks/{blockTypeIcons-D3CGFW5P.js → blockTypeIcons-BEdpG8xl.js} +8 -8
  53. package/dist/cdn/chunks/{blockTypeIcons-D3CGFW5P.js.map → blockTypeIcons-BEdpG8xl.js.map} +1 -1
  54. package/dist/{de-C9hp3n_a.js → cdn/chunks/de-BBEGQDsd.js} +9 -7
  55. package/dist/cdn/chunks/{de-B_6zGPbx.js.map → de-BBEGQDsd.js.map} +1 -1
  56. package/dist/{en-Dv776AqL.js → cdn/chunks/en-CHha-_ta.js} +9 -7
  57. package/dist/cdn/chunks/{en-pYt1-lm4.js.map → en-CHha-_ta.js.map} +1 -1
  58. package/dist/cdn/chunks/{extensions-BE81zpqP.js → extensions-DgvvgeJU.js} +2 -2
  59. package/dist/cdn/chunks/{extensions-BE81zpqP.js.map → extensions-DgvvgeJU.js.map} +1 -1
  60. package/dist/cdn/chunks/{features-CXF_YKlL.js → features-CgTy87Ni.js} +29 -17
  61. package/dist/cdn/chunks/features-CgTy87Ni.js.map +1 -0
  62. package/dist/cdn/chunks/{icons-KbmhF7rg.js → icons-DxJ--AJb.js} +36 -38
  63. package/dist/cdn/chunks/icons-DxJ--AJb.js.map +1 -0
  64. package/dist/cdn/chunks/{media-library-BERe10wA.js → media-library-Byh_BbTm.js} +13 -13
  65. package/dist/cdn/chunks/{media-library-BERe10wA.js.map → media-library-Byh_BbTm.js.map} +1 -1
  66. package/dist/{pt-BR-6n58Fl_q.js → cdn/chunks/pt-BR-CFomv2R8.js} +9 -7
  67. package/dist/cdn/chunks/{pt-BR-CtDwVsN-.js.map → pt-BR-CFomv2R8.js.map} +1 -1
  68. package/dist/cdn/chunks/quality-CHCEMG8e.js +1846 -0
  69. package/dist/cdn/chunks/quality-CHCEMG8e.js.map +1 -0
  70. package/dist/cdn/chunks/{renderer-OttGEYkY.js → renderer-DttVILgu.js} +3 -3
  71. package/dist/cdn/chunks/{renderer-OttGEYkY.js.map → renderer-DttVILgu.js.map} +1 -1
  72. package/dist/cdn/chunks/{src-C21GI0p6.js → src-a8fYhBi5.js} +6 -6
  73. package/dist/cdn/chunks/{src-C21GI0p6.js.map → src-a8fYhBi5.js.map} +1 -1
  74. package/dist/cdn/chunks/{styles-Cy-My9zK.js → styles-8qDRQ-6n.js} +310 -285
  75. package/dist/cdn/chunks/styles-8qDRQ-6n.js.map +1 -0
  76. package/dist/cdn/editor.css +1 -1
  77. package/dist/cdn/editor.js +11 -11
  78. package/dist/cdn/editor.js.map +1 -1
  79. package/dist/{cdn/chunks/de-B_6zGPbx.js → de-KFqjrxG3.js} +7 -9
  80. package/dist/{cdn/chunks/en-pYt1-lm4.js → en-CNm94YYD.js} +7 -9
  81. package/dist/{extensions-p6UBqXfg.js → extensions-C-FpnfJn.js} +2 -2
  82. package/dist/index.d.ts +14 -12
  83. package/dist/{keys-B5SJtPWf.js → keys-CY3tkCsv.js} +2 -2
  84. package/dist/list-checks-CMtEXe9l.js +25 -0
  85. package/dist/{cdn/chunks/pt-BR-CtDwVsN-.js → pt-BR-gxZZAUWp.js} +7 -9
  86. package/dist/style.css +1 -1
  87. package/dist/{styles-BP15QmP3.js → styles-Ci7RdimQ.js} +204 -179
  88. package/dist/templatical-editor.js +9 -9
  89. package/dist/{useCloudI18n-ByEMykjO.js → useCloudI18n-BPUHj2CZ.js} +1 -1
  90. package/dist/{useEditorCore-_rbu6iMu.js → useEditorCore-DT3VrvZS.js} +41 -29
  91. package/dist/{useI18n-PEB8ioi_.js → useI18n-CWX9ZTCW.js} +1 -1
  92. package/dist/{useMergeTag-C47xwU7X.js → useMergeTag-B3F9VR04.js} +2 -2
  93. package/dist/{usePopoverRoot-BwzFSPLy.js → usePopoverRoot-BKJcxsst.js} +1 -1
  94. package/package.json +7 -7
  95. package/dist/accessibility-BgUEA-Ai.js +0 -27
  96. package/dist/cdn/chunks/AccessibilityPanel-CQt87OMk.js.map +0 -1
  97. package/dist/cdn/chunks/BlockA11yBadge-QqW6nomn.js.map +0 -1
  98. package/dist/cdn/chunks/CloudEditor-ChWUIMw7.js.map +0 -1
  99. package/dist/cdn/chunks/features-CXF_YKlL.js.map +0 -1
  100. package/dist/cdn/chunks/icons-KbmhF7rg.js.map +0 -1
  101. package/dist/cdn/chunks/quality-CmkY1kMU.js +0 -1456
  102. package/dist/cdn/chunks/quality-CmkY1kMU.js.map +0 -1
  103. package/dist/cdn/chunks/styles-Cy-My9zK.js.map +0 -1
  104. /package/dist/{check-Bg5yrRmX.js → check-BF4bEbCU.js} +0 -0
  105. /package/dist/{chevron-down-tee-ghFi.js → chevron-down-4NzWtv6Y.js} +0 -0
  106. /package/dist/{circle-alert-C0L9pUQ4.js → circle-alert-CPIth9bC.js} +0 -0
  107. /package/dist/{clock-CDlEdqiP.js → clock-B9-ct9r_.js} +0 -0
  108. /package/dist/{image-up-1D_3XDdO.js → image-up-CubAveUO.js} +0 -0
  109. /package/dist/{info-DTGrc0Bx.js → info-DiINxXfZ.js} +0 -0
  110. /package/dist/{refresh-cw-BNAhAmtL.js → refresh-cw-C9_M6yB3.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, 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":"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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 { resolveLintOptions } from \"./utils/resolveLintOptions\";\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 lint: resolveLintOptions(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 { resolveLintOptions } from \"./utils/resolveLintOptions\";\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 lint: resolveLintOptions(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 * Template linter (`@templatical/quality`) configuration. Runs every\n * linter exported by the package (accessibility + structure).\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 lint?: import(\"@templatical/quality\").LintOptions;\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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"}
@@ -503,21 +503,19 @@ var e = {
503
503
  editorLoadFailed: "Editor konnte nicht geladen werden.",
504
504
  retry: "Erneut versuchen"
505
505
  },
506
- accessibility: {
507
- panelTitle: "Barrierefreiheit",
508
- panelTabLabel: "Barrierefreiheit",
506
+ issues: {
507
+ panelTitle: "Probleme",
508
+ panelTabLabel: "Probleme",
509
509
  groupErrors: "Fehler",
510
510
  groupWarnings: "Warnungen",
511
511
  groupInfo: "Hinweise",
512
512
  jump: "Zum Block springen",
513
513
  fix: "Beheben",
514
- emptyState: "Keine Probleme mit der Barrierefreiheit.",
515
- badgeError: "Hat Barrierefreiheitsfehler",
516
- badgeWarning: "Hat Barrierefreiheitswarnungen",
517
- issueCountTooltip: "{count} Barrierefreiheitsprobleme"
514
+ emptyState: "Keine Probleme sieht gut aus.",
515
+ badgeError: "Hat Fehler",
516
+ badgeWarning: "Hat Warnungen",
517
+ issueCountTooltip: "{count} Problem(e)"
518
518
  }
519
519
  };
520
520
  //#endregion
521
521
  export { e as default };
522
-
523
- //# sourceMappingURL=de-B_6zGPbx.js.map
@@ -503,21 +503,19 @@ var e = {
503
503
  editorLoadFailed: "Failed to load editor.",
504
504
  retry: "Retry"
505
505
  },
506
- accessibility: {
507
- panelTitle: "Accessibility",
508
- panelTabLabel: "Accessibility",
506
+ issues: {
507
+ panelTitle: "Issues",
508
+ panelTabLabel: "Issues",
509
509
  groupErrors: "Errors",
510
510
  groupWarnings: "Warnings",
511
511
  groupInfo: "Info",
512
512
  jump: "Jump to block",
513
513
  fix: "Fix",
514
- emptyState: "No accessibility issues — looking good.",
515
- badgeError: "Has accessibility errors",
516
- badgeWarning: "Has accessibility warnings",
517
- issueCountTooltip: "{count} accessibility issue(s)"
514
+ emptyState: "No issues — looking good.",
515
+ badgeError: "Has errors",
516
+ badgeWarning: "Has warnings",
517
+ issueCountTooltip: "{count} issue(s)"
518
518
  }
519
519
  };
520
520
  //#endregion
521
521
  export { e as default };
522
-
523
- //# sourceMappingURL=en-pYt1-lm4.js.map
@@ -1,7 +1,7 @@
1
1
  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, c as l, d as u, f as d, h as f, l as p, m, n as h, ot as g, p as _, rt as v, s as y, st as b, z as x } from "./vue.runtime.esm-bundler-Bxqkjqhc.js";
2
2
  import { C as S, D as C, n as w, w as T } from "./dist-DJmnUmW9.js";
3
- import { t as E } from "./useI18n-PEB8ioi_.js";
4
- import { t as D } from "./useMergeTag-C47xwU7X.js";
3
+ import { t as E } from "./useI18n-CWX9ZTCW.js";
4
+ import { t as D } from "./useMergeTag-B3F9VR04.js";
5
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
6
  import { n as L, t as R } from "./dist-U5guDm2w.js";
7
7
  //#region src/extensions/FontSize.ts
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { A11yOptions } from '../../quality/src/index.ts';
2
- import { A11yOptions as A11yOptions_2 } from '../../../quality/src/index.ts';
3
1
  import { AiConfig } from '../../../types/src/index.ts';
4
2
  import { BlockDefaults } from '../../types/src/index.ts';
5
3
  import { BlockDefaults as BlockDefaults_2 } from '../../../types/src/index.ts';
@@ -15,6 +13,8 @@ import { DisplayConditionsConfig } from '../../types/src/index.ts';
15
13
  import { DisplayConditionsConfig as DisplayConditionsConfig_2 } from '../../../types/src/index.ts';
16
14
  import { FontsConfig } from '../../types/src/index.ts';
17
15
  import { FontsConfig as FontsConfig_2 } from '../../../types/src/index.ts';
16
+ import { LintOptions } from '../../quality/src/index.ts';
17
+ import { LintOptions as LintOptions_2 } from '../../../quality/src/index.ts';
18
18
  import { McpConfig } from '../../../types/src/index.ts';
19
19
  import { MediaItem } from '../../../media-library/src/index.ts';
20
20
  import { MediaRequestContext } from '../../media-library/src/index.ts';
@@ -184,11 +184,12 @@ export declare interface TemplaticalCloudEditorConfig {
184
184
  onRequestMedia?: (context: MediaRequestContext_2) => Promise<MediaItem | null>;
185
185
  onBeforeTestEmail?: (html: string) => string | Promise<string>;
186
186
  /**
187
- * Accessibility linter (`@templatical/quality`) configuration. Cloud
187
+ * Template linter (`@templatical/quality`) configuration. Runs every
188
+ * linter exported by the package (accessibility + structure). Cloud
188
189
  * additionally merges `planConfig.accessibility` from the server (server
189
190
  * policy wins on conflict) — this option sets the consumer-supplied baseline.
190
191
  */
191
- accessibility?: A11yOptions_2;
192
+ lint?: LintOptions_2;
192
193
  }
193
194
 
194
195
  export declare interface TemplaticalEditor extends TemplaticalEditorBase {
@@ -257,14 +258,15 @@ export declare interface TemplaticalEditorConfig {
257
258
  */
258
259
  branding?: boolean;
259
260
  /**
260
- * Accessibility linter (`@templatical/quality`) configuration.
261
+ * Template linter (`@templatical/quality`) configuration. Runs every
262
+ * linter exported by the package (accessibility + structure).
261
263
  *
262
264
  * - When unset, the linter loads on demand once the user opens the panel.
263
265
  * - When `disabled: true`, the optional peer is never imported (saves the
264
266
  * chunk download) and the sidebar tab + inline badges are suppressed.
265
267
  * - `rules`/`thresholds` follow the shape exported by `@templatical/quality`.
266
268
  */
267
- accessibility?: A11yOptions;
269
+ lint?: LintOptions;
268
270
  }
269
271
 
270
272
  export { ThemeOverrides }
@@ -311,9 +313,9 @@ declare module "@tiptap/core" {
311
313
 
312
314
  declare module "@tiptap/core" {
313
315
  interface Commands<ReturnType> {
314
- lineHeight: {
315
- setLineHeight: (lineHeight: string) => ReturnType;
316
- unsetLineHeight: () => ReturnType;
316
+ letterSpacing: {
317
+ setLetterSpacing: (spacing: string) => ReturnType;
318
+ unsetLetterSpacing: () => ReturnType;
317
319
  };
318
320
  }
319
321
  }
@@ -330,9 +332,9 @@ declare module "@tiptap/core" {
330
332
 
331
333
  declare module "@tiptap/core" {
332
334
  interface Commands<ReturnType> {
333
- letterSpacing: {
334
- setLetterSpacing: (spacing: string) => ReturnType;
335
- unsetLetterSpacing: () => ReturnType;
335
+ lineHeight: {
336
+ setLineHeight: (lineHeight: string) => ReturnType;
337
+ unsetLineHeight: () => ReturnType;
336
338
  };
337
339
  }
338
340
  }
@@ -1,10 +1,10 @@
1
1
  import { w as e } from "./vue.runtime.esm-bundler-Bxqkjqhc.js";
2
2
  //#region src/keys.ts
3
- var t = Symbol("translations"), n = Symbol("editor"), r = Symbol("history"), i = Symbol("blockActions"), a = Symbol("conditionPreview"), o = Symbol("fontsManager"), s = Symbol("themeStyles"), c = Symbol("tplUiTheme"), l = Symbol("blockDefaults"), u = Symbol("blockRegistry"), d = Symbol("customBlockDefinitions"), f = Symbol("mergeTags"), p = Symbol("mergeTagSyntax"), m = Symbol("onRequestMergeTag"), h = Symbol("mergeTagAutocomplete"), g = Symbol("onRequestMedia"), _ = Symbol("displayConditions"), v = Symbol("allowCustomConditions"), y = Symbol("capabilities"), b = Symbol("keyboardReorder"), x = Symbol("accessibilityLint"), S = Symbol("editorRoot"), C = Symbol("popoverRoot"), w = Symbol("authManager"), T = Symbol("aiConfig"), E = Symbol("comments"), D = Symbol("savedModulesHeadless"), O = Symbol("scoring"), k = Symbol("cloudTranslations");
3
+ var t = Symbol("translations"), n = Symbol("editor"), r = Symbol("history"), i = Symbol("blockActions"), a = Symbol("conditionPreview"), o = Symbol("fontsManager"), s = Symbol("themeStyles"), c = Symbol("tplUiTheme"), l = Symbol("blockDefaults"), u = Symbol("blockRegistry"), d = Symbol("customBlockDefinitions"), f = Symbol("mergeTags"), p = Symbol("mergeTagSyntax"), m = Symbol("onRequestMergeTag"), h = Symbol("mergeTagAutocomplete"), g = Symbol("onRequestMedia"), _ = Symbol("displayConditions"), v = Symbol("allowCustomConditions"), y = Symbol("capabilities"), b = Symbol("keyboardReorder"), x = Symbol("templateLint"), S = Symbol("editorRoot"), C = Symbol("popoverRoot"), w = Symbol("authManager"), T = Symbol("aiConfig"), E = Symbol("comments"), D = Symbol("savedModulesHeadless"), O = Symbol("scoring"), k = Symbol("cloudTranslations");
4
4
  function A(t, n) {
5
5
  let r = e(t, null);
6
6
  if (r == null) throw Error(`${n} requires a provider for ${t.description ?? "unknown key"}. Ensure it is a descendant of Editor or CloudEditor.`);
7
7
  return r;
8
8
  }
9
9
  //#endregion
10
- export { A, m as C, s as D, O as E, t as O, g as S, D as T, r as _, i as a, h as b, y as c, a as d, d as f, o as g, S as h, w as i, c as k, k as l, n as m, T as n, l as o, _ as p, v as r, u as s, x as t, E as u, b as v, C as w, p as x, f as y };
10
+ export { A, C, s as D, x as E, t as O, m as S, O as T, b as _, l as a, p as b, k as c, d, _ as f, r as g, o as h, i, c as k, E as l, S as m, v as n, u as o, n as p, w as r, y as s, T as t, a as u, f as v, D as w, g as x, h as y };
@@ -0,0 +1,25 @@
1
+ import { t as e } from "./createLucideIcon-Di4mqmGn.js";
2
+ var t = e("list-checks", [
3
+ ["path", {
4
+ d: "M13 5h8",
5
+ key: "a7qcls"
6
+ }],
7
+ ["path", {
8
+ d: "M13 12h8",
9
+ key: "h98zly"
10
+ }],
11
+ ["path", {
12
+ d: "M13 19h8",
13
+ key: "c3s6r1"
14
+ }],
15
+ ["path", {
16
+ d: "m3 17 2 2 4-4",
17
+ key: "1jhpwq"
18
+ }],
19
+ ["path", {
20
+ d: "m3 7 2 2 4-4",
21
+ key: "1obspn"
22
+ }]
23
+ ]);
24
+ //#endregion
25
+ export { t };
@@ -503,21 +503,19 @@ var e = {
503
503
  editorLoadFailed: "Falha ao carregar o editor.",
504
504
  retry: "Tentar novamente"
505
505
  },
506
- accessibility: {
507
- panelTitle: "Acessibilidade",
508
- panelTabLabel: "Acessibilidade",
506
+ issues: {
507
+ panelTitle: "Problemas",
508
+ panelTabLabel: "Problemas",
509
509
  groupErrors: "Erros",
510
510
  groupWarnings: "Avisos",
511
511
  groupInfo: "Informações",
512
512
  jump: "Ir para o bloco",
513
513
  fix: "Corrigir",
514
- emptyState: "Nenhum problema de acessibilidade — tudo certo.",
515
- badgeError: "Possui erros de acessibilidade",
516
- badgeWarning: "Possui avisos de acessibilidade",
517
- issueCountTooltip: "{count} problema(s) de acessibilidade"
514
+ emptyState: "Nenhum problema — tudo certo.",
515
+ badgeError: "Possui erros",
516
+ badgeWarning: "Possui avisos",
517
+ issueCountTooltip: "{count} problema(s)"
518
518
  }
519
519
  };
520
520
  //#endregion
521
521
  export { e as default };
522
-
523
- //# sourceMappingURL=pt-BR-CtDwVsN-.js.map