@templatical/editor 0.5.1 → 0.6.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.
- package/dist/AccessibilityPanel-CvQGLdu6.js +108 -0
- package/dist/{AiChatSidebar-Cg206ewI.js → AiChatSidebar-Br9dhkIB.js} +17 -16
- package/dist/{AiFeatureMenu-D7Od4cI2.js → AiFeatureMenu-Ceewb3uB.js} +13 -13
- package/dist/BlockA11yBadge-CXDLqkcJ.js +34 -0
- package/dist/{CloudEditor-DG8fWnXI.js → CloudEditor-D_flODpm.js} +306 -217
- package/dist/{CollaboratorBar-BdXfCYAr.js → CollaboratorBar-BcSxXVY-.js} +4 -4
- package/dist/{CommentsSidebar-BAma2apG.js → CommentsSidebar-DceMRyIS.js} +26 -25
- package/dist/{CountdownBlock-Ce3RFNpM.js → CountdownBlock-BNSj1jvJ.js} +2 -2
- package/dist/{CountdownToolbar-Bo3Jaiky.js → CountdownToolbar-ClJr2GzL.js} +3 -3
- package/dist/{DesignReferenceSidebar-B8DDnLHQ.js → DesignReferenceSidebar-50qFipmW.js} +30 -29
- package/dist/{LoadingTrack-DhxulLPM.js → LoadingTrack-B0CWFHXQ.js} +1 -1
- package/dist/{ModuleBrowserModal-DpMInJoP.js → ModuleBrowserModal-BnSdG4DE.js} +10 -10
- package/dist/{ModulePreviewCanvas-MjIGlxVI.js → ModulePreviewCanvas-B78PuZdk.js} +2 -2
- package/dist/{NumberWithSuffix-BysfchFp.js → NumberWithSuffix-DkXUez9t.js} +3 -3
- package/dist/{ParagraphEditor-B5qZL5c2.js → ParagraphEditor-CPBYk2m3.js} +39 -38
- package/dist/{RichTextEditorContent-jxPxfbfZ.js → RichTextEditorContent-DYkIauIk.js} +4 -4
- package/dist/{SaveModuleDialog-BGFYMJby.js → SaveModuleDialog-Df__VToK.js} +7 -7
- package/dist/{SnapshotHistory-4aWE_-GM.js → SnapshotHistory-QBTbVrEK.js} +9 -9
- package/dist/{TemplateScoringPanel-BmAHdgLF.js → TemplateScoringPanel-4GTNHej5.js} +29 -27
- package/dist/{TestEmailModal-p-iH28wl.js → TestEmailModal-DaDMACHY.js} +4 -4
- package/dist/{TitleEditor-BHxJ6Oxd.js → TitleEditor-PHShl4tS.js} +10 -10
- package/dist/{TplModal-BiUCSfou.js → TplModal-DzlNkBYQ.js} +2 -2
- package/dist/_plugin-vue_export-helper-B0hnzhyu.js +8 -0
- package/dist/accessibility-e8JYu_zd.js +27 -0
- package/dist/{blockTypeIcons-8GJD96Tw.js → blockTypeIcons-D0wkSpP9.js} +2 -2
- package/dist/bundle-stats.json +8 -8
- package/dist/cdn/chunks/AccessibilityPanel-DmQIqpp1.js +97 -0
- package/dist/cdn/chunks/AccessibilityPanel-DmQIqpp1.js.map +1 -0
- package/dist/cdn/chunks/{AiFeatureMenu-DSXAYiMk.js → AiFeatureMenu-D0kJ0FMv.js} +5 -5
- package/dist/cdn/chunks/{AiFeatureMenu-DSXAYiMk.js.map → AiFeatureMenu-D0kJ0FMv.js.map} +1 -1
- package/dist/cdn/chunks/BlockA11yBadge-DWi7e6UQ.js +33 -0
- package/dist/cdn/chunks/BlockA11yBadge-DWi7e6UQ.js.map +1 -0
- package/dist/cdn/chunks/{CloudEditor-CRYF-bR3.js → CloudEditor-ulKjC0Ag.js} +253 -166
- package/dist/cdn/chunks/CloudEditor-ulKjC0Ag.js.map +1 -0
- package/dist/cdn/chunks/{CollaboratorBar-CTFgLtUt.js → CollaboratorBar-Bp9LwcWB.js} +6 -6
- package/dist/cdn/chunks/{CollaboratorBar-CTFgLtUt.js.map → CollaboratorBar-Bp9LwcWB.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownBlock-BKg-e3Uo.js → CountdownBlock-XFPbUKXw.js} +2 -2
- package/dist/cdn/chunks/{CountdownBlock-BKg-e3Uo.js.map → CountdownBlock-XFPbUKXw.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-CPAOXOwI.js → CountdownToolbar-DndLWUgz.js} +3 -3
- package/dist/cdn/chunks/{CountdownToolbar-CPAOXOwI.js.map → CountdownToolbar-DndLWUgz.js.map} +1 -1
- package/dist/cdn/chunks/{ModuleBrowserModal-B70ZM4KG.js → ModuleBrowserModal-ClY6A8bi.js} +7 -7
- package/dist/cdn/chunks/{ModuleBrowserModal-B70ZM4KG.js.map → ModuleBrowserModal-ClY6A8bi.js.map} +1 -1
- package/dist/cdn/chunks/{ModulePreviewCanvas-VHhQwuFP.js → ModulePreviewCanvas-eQExlHRD.js} +18 -18
- package/dist/cdn/chunks/{ModulePreviewCanvas-VHhQwuFP.js.map → ModulePreviewCanvas-eQExlHRD.js.map} +1 -1
- package/dist/cdn/chunks/{NumberWithSuffix-DnqYqSKs.js → NumberWithSuffix-Cf5C9rpj.js} +4 -4
- package/dist/cdn/chunks/{NumberWithSuffix-DnqYqSKs.js.map → NumberWithSuffix-Cf5C9rpj.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-oVEaeerJ.js → ParagraphEditor-B5AvuetS.js} +53 -53
- package/dist/cdn/chunks/{ParagraphEditor-oVEaeerJ.js.map → ParagraphEditor-B5AvuetS.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-CmOwLOeY.js → RichTextEditorContent-hCk4SA8U.js} +4 -4
- package/dist/cdn/chunks/{RichTextEditorContent-CmOwLOeY.js.map → RichTextEditorContent-hCk4SA8U.js.map} +1 -1
- package/dist/cdn/chunks/{SaveModuleDialog-B6kNizps.js → SaveModuleDialog-Borv00Kh.js} +3 -3
- package/dist/cdn/chunks/{SaveModuleDialog-B6kNizps.js.map → SaveModuleDialog-Borv00Kh.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-CvZe3neh.js → TitleEditor-BkzRNsEg.js} +12 -12
- package/dist/cdn/chunks/{TitleEditor-CvZe3neh.js.map → TitleEditor-BkzRNsEg.js.map} +1 -1
- package/dist/cdn/chunks/blockTypeIcons-Dlw0LR_y.js +22 -0
- package/dist/cdn/chunks/{blockTypeIcons-Bs9B-yft.js.map → blockTypeIcons-Dlw0LR_y.js.map} +1 -1
- package/dist/cdn/chunks/{de-CGu9U1iv.js → de-Ce-LbJ2J.js} +1 -1
- package/dist/cdn/chunks/{de-CGu9U1iv.js.map → de-Ce-LbJ2J.js.map} +1 -1
- package/dist/{de-DwLWZn6p.js → cdn/chunks/de-D8CnZxV9.js} +21 -1
- package/dist/cdn/chunks/de-D8CnZxV9.js.map +1 -0
- package/dist/{de-CbSb23fl.js → cdn/chunks/de-RQrZR56a.js} +8 -0
- package/dist/cdn/chunks/{de-Db6I9YdS.js.map → de-RQrZR56a.js.map} +1 -1
- package/dist/cdn/chunks/{emojiData-Vv_m5TSB.js → emojiData-EMFlj6FJ.js} +1 -1
- package/dist/cdn/chunks/{emojiData-Vv_m5TSB.js.map → emojiData-EMFlj6FJ.js.map} +1 -1
- package/dist/{en-BrlGVpmd.js → cdn/chunks/en-8FHaQv4V.js} +21 -1
- package/dist/cdn/chunks/en-8FHaQv4V.js.map +1 -0
- package/dist/{en-2fvenFu0.js → cdn/chunks/en-Bl1ecfRF.js} +8 -0
- package/dist/cdn/chunks/{en-BbqBJ2Q3.js.map → en-Bl1ecfRF.js.map} +1 -1
- package/dist/cdn/chunks/{en-C3WMCISl.js → en-DiCWK5fG.js} +1 -1
- package/dist/cdn/chunks/{en-C3WMCISl.js.map → en-DiCWK5fG.js.map} +1 -1
- package/dist/cdn/chunks/{extensions-D13MqxlW.js → extensions-Bseybosy.js} +37 -34
- package/dist/cdn/chunks/{extensions-D13MqxlW.js.map → extensions-Bseybosy.js.map} +1 -1
- package/dist/cdn/chunks/{features-BnG-dhzy.js → features-C256qERn.js} +816 -756
- package/dist/cdn/chunks/features-C256qERn.js.map +1 -0
- package/dist/cdn/chunks/{icons-B_185qj_.js → icons-BfGy6HC_.js} +99 -52
- package/dist/cdn/chunks/icons-BfGy6HC_.js.map +1 -0
- package/dist/cdn/chunks/{media-library-DxpFw4Tt.js → media-library-hjXJlR6s.js} +217 -217
- package/dist/cdn/chunks/{media-library-DxpFw4Tt.js.map → media-library-hjXJlR6s.js.map} +1 -1
- package/dist/cdn/chunks/quality-CqLOOUnE.js +1404 -0
- package/dist/cdn/chunks/quality-CqLOOUnE.js.map +1 -0
- package/dist/cdn/chunks/{readableTextColor-Cd_cgWO_.js → readableTextColor-DhoK4XiZ.js} +1 -1
- package/dist/cdn/chunks/{readableTextColor-Cd_cgWO_.js.map → readableTextColor-DhoK4XiZ.js.map} +1 -1
- package/dist/cdn/chunks/{dist-MGrg9tLo.js → renderer-Dbtez0lY.js} +178 -126
- package/dist/cdn/chunks/renderer-Dbtez0lY.js.map +1 -0
- package/dist/cdn/chunks/{src-Cw7KAyui.js → src-MOXq0pRK.js} +11 -11
- package/dist/cdn/chunks/{src-Cw7KAyui.js.map → src-MOXq0pRK.js.map} +1 -1
- package/dist/cdn/chunks/{styles-DYDpAUsJ.js → styles-COCYlkQY.js} +576 -484
- package/dist/cdn/chunks/styles-COCYlkQY.js.map +1 -0
- package/dist/cdn/chunks/{tiptap-CPAwPMop.js → tiptap-CX3vCeHQ.js} +2 -2
- package/dist/cdn/chunks/{tiptap-CPAwPMop.js.map → tiptap-CX3vCeHQ.js.map} +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +87 -86
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-Om8IxvJb.js → check-Da05j8yl.js} +1 -1
- package/dist/{chevron-down-SG7QNtDl.js → chevron-down-R2uY34iD.js} +1 -1
- package/dist/{circle-alert-De74azaX.js → circle-alert-DZuGWPX-.js} +1 -1
- package/dist/{clock-Dr7rX_PG.js → clock-CRp2sIub.js} +1 -1
- package/dist/{cloud-B1NAFg44.js → cloud-WfWdqZVK.js} +1 -1
- package/dist/{_plugin-vue_export-helper-GRmvIR5A.js → createLucideIcon-C3pa2siy.js} +2 -6
- package/dist/{cdn/chunks/de-BSWt7lYu.js → de-Brqvgr43.js} +19 -3
- package/dist/{cdn/chunks/de-Db6I9YdS.js → de-DCaaCE5s.js} +6 -2
- package/dist/{dist-CuyhAnuE.js → dist-B1IR0bpH.js} +65 -53
- package/dist/{dist-QoJxR1uP.js → dist-BFawx6IS.js} +1 -1
- package/dist/{dist-H7npgW0s.js → dist-BaQIYPsn.js} +1 -1
- package/dist/{dist-C4m7p7Wb.js → dist-Cp0zXPAD.js} +1 -1
- package/dist/{dist--8ZUsIQD.js → dist-D6uC2xhi.js} +1 -1
- package/dist/{dist-BMMiVjHs.js → dist-D90y8dvT.js} +3 -3
- package/dist/{dist--e2w6FN-.js → dist-DDJIWTRY.js} +1 -1
- package/dist/{dist-e0ylhlSV.js → dist-DJmnUmW9.js} +2 -1
- package/dist/{dist-BOSn1353.js → dist-DjviJBCi.js} +1 -1
- package/dist/{dist-BsiDC2rP.js → dist-KoBJjK1G.js} +1 -1
- package/dist/{dist-BNZS_qhu.js → dist-aRzjfSRN.js} +1 -1
- package/dist/{dist-3AzSJz2x.js → dist-us-RpCWN.js} +1 -1
- package/dist/{dist-DPsMHsAW.js → dist-wzMIGj-D.js} +1 -1
- package/dist/{cdn/chunks/en-BbqBJ2Q3.js → en-DXCyK4-X.js} +6 -2
- package/dist/{cdn/chunks/en-9-vIGlc7.js → en-WDVp87TE.js} +19 -3
- package/dist/{extensions-Cmf6XVn9.js → extensions-CUcl9Ok4.js} +26 -23
- package/dist/{image-up-10kxuulB.js → image-up-MBZKKg9p.js} +1 -1
- package/dist/index.d.ts +23 -6
- package/dist/info-CJEC7piy.js +19 -0
- package/dist/{keys-BeW4IrXP.js → keys-ciNfSSGj.js} +3 -3
- package/dist/{loader-circle-IS16T05j.js → loader-circle-DsY5Yg33.js} +1 -1
- package/dist/{message-circle-3R472hDc.js → message-circle-yElBbR2C.js} +1 -1
- package/dist/{refresh-cw-CpmOfpPK.js → refresh-cw-CE_AGtn8.js} +3 -18
- package/dist/{scan-line-J_fzG2Wo.js → scan-line-D0vcUekt.js} +1 -1
- package/dist/{send-DRkFf4hP.js → send-DH4oDQqC.js} +1 -1
- package/dist/{shield-check-DtXk7JKN.js → shield-check-CfJgs2Hd.js} +1 -1
- package/dist/{sparkles-CD7TiNiC.js → sparkles-CvRXGqFs.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{styles-C-FrkTA9.js → styles-B58wYIn4.js} +589 -476
- package/dist/templatical-editor.js +90 -89
- package/dist/{text-align-start-B8M9hruh.js → text-align-start-BT9VUDxK.js} +1 -1
- package/dist/{trash-2-cEVwp-r_.js → trash-2-DbP2Y6t2.js} +1 -1
- package/dist/{triangle-alert-8CkexIzx.js → triangle-alert-aOXceTSe.js} +1 -1
- package/dist/{useCloudI18n-D0Fi0hBU.js → useCloudI18n-BuIwR6OE.js} +1 -1
- package/dist/{useEditorCore-D0uZbPKG.js → useEditorCore-Dz-qbVXX.js} +1709 -1661
- package/dist/{useI18n-Jp3X6Q0t.js → useI18n-lb2DHDiu.js} +1 -1
- package/dist/{useMergeTag-CEE4CG-D.js → useMergeTag-CBwKnnNB.js} +3 -3
- package/dist/{x-BNc1bVzp.js → x-u2oVmjN_.js} +1 -1
- package/package.json +10 -5
- package/dist/cdn/chunks/CloudEditor-CRYF-bR3.js.map +0 -1
- package/dist/cdn/chunks/blockTypeIcons-Bs9B-yft.js +0 -22
- package/dist/cdn/chunks/de-BSWt7lYu.js.map +0 -1
- package/dist/cdn/chunks/dist-MGrg9tLo.js.map +0 -1
- package/dist/cdn/chunks/en-9-vIGlc7.js.map +0 -1
- package/dist/cdn/chunks/features-BnG-dhzy.js.map +0 -1
- package/dist/cdn/chunks/icons-B_185qj_.js.map +0 -1
- package/dist/cdn/chunks/styles-DYDpAUsJ.js.map +0 -1
- /package/dist/{dist-CmrXHeJS.js → dist-iLBdeBDR.js} +0 -0
- /package/dist/{emojiData-DcYt1YZ3.js → emojiData-PQyVa4bU.js} +0 -0
- /package/dist/{formatRelativeTime-MungD2xr.js → formatRelativeTime-WvH3Au71.js} +0 -0
- /package/dist/{liquid.browser-BohVA1YU.js → liquid.browser-CdMv1BTn.js} +0 -0
- /package/dist/{readableTextColor-Uc7ntzXo.js → readableTextColor-CY3SiRnt.js} +0 -0
- /package/dist/{styleConstants-Beu6EmBc.js → styleConstants-fWzlIIwN.js} +0 -0
package/dist/cdn/editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","names":[],"sources":["../../src/Editor.vue","../../src/Editor.vue","../../src/i18n/index.ts","../../src/utils/toMjml.ts","../../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onMounted, onUnmounted } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport 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\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n 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});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Header — absolute, full width, above everything -->\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-restore-btn-enter-active {\n transition:\n opacity 200ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-restore-btn-leave-active {\n transition:\n opacity 150ms ease-in,\n transform 150ms ease-in;\n}\n\n.tpl-restore-btn-enter-from,\n.tpl-restore-btn-leave-to {\n opacity: 0;\n transform: translateY(-8px) scale(0.9);\n}\n\n.tpl-restore-btn-enter-to,\n.tpl-restore-btn-leave-from {\n opacity: 1;\n transform: translateY(0) scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { onMounted, onUnmounted } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport 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\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n 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});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Header — absolute, full width, above everything -->\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n </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\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 locale.split(\"-\")[0].toLowerCase();\n}\n\nfunction resolveLocale(locale: string, supported: string[]): string {\n const base = getBaseLocale(locale);\n return supported.includes(base) ? base : \"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 base, e.g. en-GB → en). */\nexport function isLocaleSupported(locale: string): boolean {\n return (\n supportedLocales.includes(locale) ||\n supportedLocales.includes(getBaseLocale(locale))\n );\n}\n\n/** Check if a locale has cloud translations (matched by base). */\nexport function isCloudLocaleSupported(locale: string): boolean {\n return (\n supportedCloudLocales.includes(locale) ||\n supportedCloudLocales.includes(getBaseLocale(locale))\n );\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","import { 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\n// ---------------------------------------------------------------------------\n// OSS config + return types\n// ---------------------------------------------------------------------------\n\nexport interface TemplaticalEditorConfig {\n container: string | HTMLElement;\n content?: TemplateContent;\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/** 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// OSS init — sync\n// ---------------------------------------------------------------------------\n\nlet appInstance: App | null = null;\nconst editorRef: Ref<InstanceType<typeof Editor> | null> = ref(null);\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 // Unmount any prior app *after* awaits — checking before the await would\n // let two concurrent init() calls both pass the guard while appInstance is\n // still null and orphan the first-mounted app.\n if (appInstance) {\n unmount();\n }\n\n appInstance = createApp({\n setup() {\n return () =>\n h(Editor, {\n config,\n translations,\n fontsManager,\n ref: editorRef,\n });\n },\n });\n\n appInstance.mount(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,\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\nlet cloudAppInstance: App | null = null;\n\nconst cloudEditorRef: Ref<InstanceType<\n typeof import(\"./cloud/CloudEditor.vue\").default\n> | null> = ref(null);\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 // Unmount any prior app *after* awaits — checking before the await would\n // let two concurrent initCloud() calls both pass the guard while\n // cloudAppInstance is still null and orphan the first-mounted app.\n if (cloudAppInstance) {\n unmountCloud();\n }\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 cloudAppInstance = createApp({\n setup() {\n return () =>\n h(CloudEditor, {\n config,\n translations,\n cloudTranslations,\n fontsManager,\n ref: cloudEditorRef,\n onReady: () => {\n clearTimeout(timeout);\n resolve();\n },\n });\n },\n });\n\n cloudAppInstance.mount(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: unmountCloud,\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\nexport function unmount(): void {\n if (appInstance) {\n appInstance.unmount();\n appInstance = null;\n editorRef.value = null;\n }\n}\n\nfunction unmountCloud(): void {\n if (cloudAppInstance) {\n cloudAppInstance.unmount();\n cloudAppInstance = null;\n cloudEditorRef.value = null;\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"],"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 } 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\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Header — absolute, full width, above everything -->\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-restore-btn-enter-active {\n transition:\n opacity 200ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-restore-btn-leave-active {\n transition:\n opacity 150ms ease-in,\n transform 150ms ease-in;\n}\n\n.tpl-restore-btn-enter-from,\n.tpl-restore-btn-leave-to {\n opacity: 0;\n transform: translateY(-8px) scale(0.9);\n}\n\n.tpl-restore-btn-enter-to,\n.tpl-restore-btn-leave-from {\n opacity: 1;\n transform: translateY(0) scale(1);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { onMounted, onUnmounted } from \"vue\";\nimport type { TemplaticalEditorConfig } from \"./index\";\nimport { useEditor } from \"@templatical/core\";\nimport type { TemplateContent, UiTheme } from \"@templatical/types\";\nimport { useEditorCore } from \"./composables/useEditorCore\";\nimport { resolveAccessibilityOptions } from \"./utils/resolveAccessibilityOptions\";\nimport type { Translations } from \"./i18n\";\nimport type { UseFontsReturn } from \"./composables/useFonts\";\n\nimport { RotateCcw } from \"@lucide/vue\";\nimport Canvas from \"./components/Canvas.vue\";\nimport Sidebar from \"./components/Sidebar.vue\";\nimport RightSidebar from \"./components/RightSidebar.vue\";\nimport ViewportToggle from \"./components/ViewportToggle.vue\";\nimport PreviewToggle from \"./components/PreviewToggle.vue\";\nimport DarkModeToggle from \"./components/DarkModeToggle.vue\";\nimport EditorFooter from \"./components/EditorFooter.vue\";\nimport \"./styles/index.css\";\n\nconst props = defineProps<{\n config: TemplaticalEditorConfig;\n translations: Translations;\n fontsManager: UseFontsReturn;\n}>();\n\n// --- Core editor state ---\nconst editor = useEditor({\n content: props.config.content!,\n templateDefaults: props.config.templateDefaults,\n});\n\n// --- Shared editor core (composables, provides, plugins, keyboard) ---\nconst core = useEditorCore({\n editor,\n config: {\n uiTheme: props.config.uiTheme,\n theme: props.config.theme,\n blockDefaults: props.config.blockDefaults,\n customBlocks: props.config.customBlocks,\n mergeTags: props.config.mergeTags,\n displayConditions: props.config.displayConditions,\n onRequestMedia: props.config.onRequestMedia,\n accessibility: resolveAccessibilityOptions(props.config),\n onSave: props.config.onSave\n ? () =>\n props.config.onSave!(JSON.parse(JSON.stringify(editor.state.content)))\n : undefined,\n },\n translations: props.translations,\n fontsManager: props.fontsManager,\n autoSaveOptions: props.config.onChange\n ? {\n onChange: () =>\n props.config.onChange!(\n JSON.parse(JSON.stringify(editor.state.content)),\n ),\n }\n : null,\n});\n\n// --- Lifecycle ---\nonMounted(async () => {\n await props.fontsManager.loadCustomFonts();\n});\n\nonUnmounted(() => {\n props.fontsManager.cleanupFontLinks();\n core.destroy();\n});\n\n// --- Public API (accessed via template ref from init()) ---\ndefineExpose({\n getContent: () => editor.content.value,\n setContent: (content: TemplateContent) => editor.setContent(content),\n setTheme: (theme: UiTheme) => editor.setUiTheme(theme),\n renderCustomBlock: core.registry.renderCustomBlock,\n});\n</script>\n\n<template>\n <div\n class=\"tpl tpl:relative tpl:h-full tpl:overflow-hidden\"\n :class=\"{ 'tpl:dark': editor.state.darkMode }\"\n :data-tpl-theme=\"core.resolvedTheme.value\"\n :style=\"core.themeStyles.value\"\n >\n <!-- Header — absolute, full width, above everything -->\n <header\n class=\"tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4 tpl:shadow-[var(--tpl-shadow-md)] tpl:border-b tpl:border-[var(--tpl-border)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 80%, transparent);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n \"\n >\n <!-- Left: empty (reserved for embedder customization) -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2.5\"></div>\n\n <!-- Center: viewport + preview + dark mode -->\n <div class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-10\">\n <ViewportToggle\n :viewport=\"editor.state.viewport\"\n @change=\"editor.setViewport\"\n />\n <DarkModeToggle\n :dark-mode=\"editor.state.darkMode\"\n @change=\"editor.setDarkMode\"\n />\n <PreviewToggle\n :preview-mode=\"editor.state.previewMode\"\n @change=\"editor.setPreviewMode\"\n />\n </div>\n\n <!-- Right: empty in OSS mode -->\n <div\n class=\"tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3\"\n ></div>\n </header>\n\n <!-- Left sidebar — absolute, below header -->\n <Sidebar v-show=\"!editor.state.previewMode\" />\n\n <!-- Canvas area — absolute, fills remaining space -->\n <div\n class=\"tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto tpl:bg-[var(--tpl-canvas-bg)]\"\n style=\"transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)\"\n :class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n 'tpl:top-14',\n ]\"\n >\n <!-- Restore hidden blocks button -->\n <div class=\"tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0\">\n <Transition name=\"tpl-restore-btn\">\n <button\n v-if=\"core.conditionPreview.hasHiddenBlocks.value\"\n class=\"tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80 tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)] tpl:border-[var(--tpl-warning)]\"\n style=\"backdrop-filter: blur(8px)\"\n @click=\"core.conditionPreview.reset()\"\n >\n <RotateCcw :size=\"13\" :stroke-width=\"2\" />\n {{ core.t.blockSettings.restoreHiddenBlocks }}\n </button>\n </Transition>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:p-8\">\n <Canvas\n :viewport=\"editor.state.viewport\"\n :content=\"editor.content.value\"\n :selected-block-id=\"editor.state.selectedBlockId\"\n :dark-mode=\"editor.state.darkMode\"\n :preview-mode=\"editor.state.previewMode\"\n @select-block=\"editor.selectBlock\"\n />\n </div>\n </div>\n\n <EditorFooter\n v-if=\"config.branding !== false\"\n :position-class=\"[\n editor.state.previewMode\n ? 'tpl:left-0 tpl:right-0'\n : 'tpl:left-12 tpl:right-[320px]',\n ]\"\n />\n\n <!-- Keyboard reorder announcement region (visually hidden, screen-reader live) -->\n <div\n class=\"tpl-sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n :aria-label=\"core.t.landmarks.reorderAnnouncements\"\n >\n {{ core.keyboardReorder.announcement.value }}\n </div>\n\n <!-- Right sidebar — persisted with v-show -->\n <RightSidebar\n v-show=\"!editor.state.previewMode\"\n :selected-block=\"editor.selectedBlock.value\"\n :settings=\"editor.content.value.settings\"\n @update-block=\"\n (updates) => editor.updateBlock(editor.state.selectedBlockId!, updates)\n \"\n @delete-block=\"\n () => {\n if (editor.state.selectedBlockId) {\n core.blockActions.deleteBlock(editor.state.selectedBlockId);\n }\n }\n \"\n @duplicate-block=\"\n () => {\n if (editor.selectedBlock.value) {\n core.blockActions.duplicateBlock(editor.selectedBlock.value);\n }\n }\n \"\n @update-settings=\"(updates) => editor.updateSettings(updates)\"\n />\n </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\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 locale.split(\"-\")[0].toLowerCase();\n}\n\nfunction resolveLocale(locale: string, supported: string[]): string {\n const base = getBaseLocale(locale);\n return supported.includes(base) ? base : \"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 base, e.g. en-GB → en). */\nexport function isLocaleSupported(locale: string): boolean {\n return (\n supportedLocales.includes(locale) ||\n supportedLocales.includes(getBaseLocale(locale))\n );\n}\n\n/** Check if a locale has cloud translations (matched by base). */\nexport function isCloudLocaleSupported(locale: string): boolean {\n return (\n supportedCloudLocales.includes(locale) ||\n supportedCloudLocales.includes(getBaseLocale(locale))\n );\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","import { 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\n// ---------------------------------------------------------------------------\n// OSS config + return types\n// ---------------------------------------------------------------------------\n\nexport interface TemplaticalEditorConfig {\n container: string | HTMLElement;\n content?: TemplateContent;\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// OSS init — sync\n// ---------------------------------------------------------------------------\n\nlet appInstance: App | null = null;\nconst editorRef: Ref<InstanceType<typeof Editor> | null> = ref(null);\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 // Unmount any prior app *after* awaits — checking before the await would\n // let two concurrent init() calls both pass the guard while appInstance is\n // still null and orphan the first-mounted app.\n if (appInstance) {\n unmount();\n }\n\n appInstance = createApp({\n setup() {\n return () =>\n h(Editor, {\n config,\n translations,\n fontsManager,\n ref: editorRef,\n });\n },\n });\n\n appInstance.mount(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,\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\nlet cloudAppInstance: App | null = null;\n\nconst cloudEditorRef: Ref<InstanceType<\n typeof import(\"./cloud/CloudEditor.vue\").default\n> | null> = ref(null);\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 // Unmount any prior app *after* awaits — checking before the await would\n // let two concurrent initCloud() calls both pass the guard while\n // cloudAppInstance is still null and orphan the first-mounted app.\n if (cloudAppInstance) {\n unmountCloud();\n }\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 cloudAppInstance = createApp({\n setup() {\n return () =>\n h(CloudEditor, {\n config,\n translations,\n cloudTranslations,\n fontsManager,\n ref: cloudEditorRef,\n onReady: () => {\n clearTimeout(timeout);\n resolve();\n },\n });\n },\n });\n\n cloudAppInstance.mount(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: unmountCloud,\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\nexport function unmount(): void {\n if (appInstance) {\n appInstance.unmount();\n appInstance = null;\n editorRef.value = null;\n }\n}\n\nfunction unmountCloud(): void {\n if (cloudAppInstance) {\n cloudAppInstance.unmount();\n cloudAppInstance = null;\n cloudEditorRef.value = null;\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"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Q as e, X as t, Z as n, p as r, z as i } from "./vue.runtime.esm-bundler-DpvJL-nX.js";
|
|
2
|
-
import { r as a, u as o } from "./dist-
|
|
2
|
+
import { r as a, u as o } from "./dist-DJmnUmW9.js";
|
|
3
3
|
//#region ../core/dist/cloud/index.js
|
|
4
4
|
var s = class e {
|
|
5
5
|
static DEFAULT_BASE_URL = "https://templatical.com";
|
|
@@ -38,10 +38,6 @@ var d = ({ name: e, iconNode: o, absoluteStrokeWidth: l, "absolute-stroke-width"
|
|
|
38
38
|
...r,
|
|
39
39
|
iconNode: t,
|
|
40
40
|
name: e
|
|
41
|
-
}, i)
|
|
42
|
-
let n = e.__vccOpts || e;
|
|
43
|
-
for (let [e, r] of t) n[e] = r;
|
|
44
|
-
return n;
|
|
45
|
-
};
|
|
41
|
+
}, i);
|
|
46
42
|
//#endregion
|
|
47
|
-
export { f as
|
|
43
|
+
export { f as t };
|
|
@@ -145,7 +145,9 @@ var e = {
|
|
|
145
145
|
placeholderUrlPlaceholder: "https://... (nur zur Gestaltung)",
|
|
146
146
|
placeholderUrlTooltip: "Da die Bild-URL ein Merge-Tag verwendet, können Sie hier ein echtes Bild angeben, um das Layout während der Gestaltung in der Vorschau anzuzeigen. Dies wird nicht in die endgültige Ausgabe aufgenommen.",
|
|
147
147
|
clickToAdd: "Klicken Sie, um eine Bild-URL hinzuzufügen",
|
|
148
|
-
browseMedia: "Medien durchsuchen"
|
|
148
|
+
browseMedia: "Medien durchsuchen",
|
|
149
|
+
decorative: "Dekoratives Bild",
|
|
150
|
+
decorativeHint: "Wird von Bildschirmlesern ignoriert. Nur für Abstandshalter und visuelle Verzierungen verwenden."
|
|
149
151
|
},
|
|
150
152
|
video: {
|
|
151
153
|
videoUrl: "Video-URL",
|
|
@@ -354,6 +356,9 @@ var e = {
|
|
|
354
356
|
preheaderText: "Preheader-Text",
|
|
355
357
|
preheaderTextPlaceholder: "Vorschautext, der nach der Betreffzeile im Posteingang angezeigt wird...",
|
|
356
358
|
preheaderTextHint: "Dieser Text erscheint nach der Betreffzeile in der E-Mail-Vorschau. Unterstützt Merge-Tags.",
|
|
359
|
+
language: "Sprache",
|
|
360
|
+
contentLocale: "Inhaltssprache",
|
|
361
|
+
contentLocaleHint: "BCP-47-Code (z. B. en, de, pt-BR). Setzt das lang-Attribut der gerenderten E-Mail, damit Screenreader den Inhalt korrekt aussprechen.",
|
|
357
362
|
tips: "Tipps",
|
|
358
363
|
tip1: "600px ist die Standardbreite für E-Mail-Vorlagen",
|
|
359
364
|
tip2: "Verwenden Sie websichere Schriften für beste Kompatibilität",
|
|
@@ -497,9 +502,20 @@ var e = {
|
|
|
497
502
|
editorLoading: "Editor wird geladen...",
|
|
498
503
|
editorLoadFailed: "Editor konnte nicht geladen werden.",
|
|
499
504
|
retry: "Erneut versuchen"
|
|
505
|
+
},
|
|
506
|
+
accessibility: {
|
|
507
|
+
panelTitle: "Barrierefreiheit",
|
|
508
|
+
panelTabLabel: "Barrierefreiheit",
|
|
509
|
+
groupErrors: "Fehler",
|
|
510
|
+
groupWarnings: "Warnungen",
|
|
511
|
+
groupInfo: "Hinweise",
|
|
512
|
+
jump: "Zum Block springen",
|
|
513
|
+
fix: "Beheben",
|
|
514
|
+
emptyState: "Keine Probleme mit der Barrierefreiheit.",
|
|
515
|
+
badgeError: "Hat Barrierefreiheitsfehler",
|
|
516
|
+
badgeWarning: "Hat Barrierefreiheitswarnungen",
|
|
517
|
+
issueCountTooltip: "{count} Barrierefreiheitsprobleme"
|
|
500
518
|
}
|
|
501
519
|
};
|
|
502
520
|
//#endregion
|
|
503
521
|
export { e as default };
|
|
504
|
-
|
|
505
|
-
//# sourceMappingURL=de-BSWt7lYu.js.map
|
|
@@ -195,9 +195,13 @@ var e = {
|
|
|
195
195
|
error: "Vorlage konnte nicht aus Design generiert werden",
|
|
196
196
|
fileTooLarge: "Datei ist zu groß. Maximale Größe ist 10 MB.",
|
|
197
197
|
invalidFileType: "Dieser Dateityp wird nicht unterstützt. Laden Sie eine PNG-, JPG-, WebP- oder PDF-Datei hoch."
|
|
198
|
+
},
|
|
199
|
+
saveGate: {
|
|
200
|
+
title: "Barrierefreiheitsfehler blockieren das Speichern",
|
|
201
|
+
body: "Ihr Plan blockiert das Speichern, solange Fehler bestehen. Beheben Sie die folgenden Punkte oder speichern Sie trotzdem.",
|
|
202
|
+
cancel: "Prüfen und beheben",
|
|
203
|
+
confirm: "Trotzdem speichern"
|
|
198
204
|
}
|
|
199
205
|
};
|
|
200
206
|
//#endregion
|
|
201
207
|
export { e as default };
|
|
202
|
-
|
|
203
|
-
//# sourceMappingURL=de-Db6I9YdS.js.map
|
|
@@ -26,7 +26,8 @@ function T(e, t) {
|
|
|
26
26
|
}
|
|
27
27
|
return n;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
var E = (e) => e();
|
|
30
|
+
function D(e, t = {}) {
|
|
30
31
|
let n, r, i = S, a = (e) => {
|
|
31
32
|
clearTimeout(e), i(), i = S;
|
|
32
33
|
}, o;
|
|
@@ -41,22 +42,26 @@ function E(e, t = {}) {
|
|
|
41
42
|
});
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
|
-
function
|
|
45
|
+
function O(e) {
|
|
45
46
|
return e.endsWith("rem") ? Number.parseFloat(e) * 16 : Number.parseFloat(e);
|
|
46
47
|
}
|
|
47
|
-
function
|
|
48
|
+
function k(e) {
|
|
48
49
|
return Array.isArray(e) ? e : [e];
|
|
49
50
|
}
|
|
50
|
-
function
|
|
51
|
+
function A(e) {
|
|
51
52
|
return e || r();
|
|
52
53
|
}
|
|
53
|
-
function
|
|
54
|
-
return T(
|
|
54
|
+
function j(e, t = 200, n = {}) {
|
|
55
|
+
return T(D(t, n), e);
|
|
56
|
+
}
|
|
57
|
+
function M(e, t, n = {}) {
|
|
58
|
+
let { eventFilter: r = E, ...i } = n;
|
|
59
|
+
return m(e, T(r, t), i);
|
|
55
60
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
61
|
+
function N(e, n = !0, r) {
|
|
62
|
+
A(r) ? t(e, r) : n ? e() : o(e);
|
|
58
63
|
}
|
|
59
|
-
function
|
|
64
|
+
function P(t, n = 1e3, r = {}) {
|
|
60
65
|
let { immediate: i = !0, immediateCallback: a = !1 } = r, o = null, s = l(!1);
|
|
61
66
|
function c() {
|
|
62
67
|
o &&= (clearInterval(o), null);
|
|
@@ -76,7 +81,7 @@ function M(t, n = 1e3, r = {}) {
|
|
|
76
81
|
resume: f
|
|
77
82
|
};
|
|
78
83
|
}
|
|
79
|
-
function
|
|
84
|
+
function F(t, n, r = {}) {
|
|
80
85
|
let { immediate: i = !0, immediateCallback: a = !1 } = r, o = l(!1), s;
|
|
81
86
|
function c() {
|
|
82
87
|
s &&= (clearTimeout(s), void 0);
|
|
@@ -95,7 +100,7 @@ function N(t, n, r = {}) {
|
|
|
95
100
|
stop: u
|
|
96
101
|
};
|
|
97
102
|
}
|
|
98
|
-
function
|
|
103
|
+
function I(e = !1, t = {}) {
|
|
99
104
|
let { truthyValue: n = !0, falsyValue: r = !1 } = t, i = p(e), a = l(e);
|
|
100
105
|
function o(e) {
|
|
101
106
|
if (arguments.length) return a.value = e, a.value;
|
|
@@ -106,7 +111,14 @@ function P(e = !1, t = {}) {
|
|
|
106
111
|
}
|
|
107
112
|
return i ? o : [a, o];
|
|
108
113
|
}
|
|
109
|
-
function
|
|
114
|
+
function L(e, t, n = {}) {
|
|
115
|
+
let { debounce: r = 0, maxWait: i = void 0, ...a } = n;
|
|
116
|
+
return M(e, t, {
|
|
117
|
+
...a,
|
|
118
|
+
eventFilter: D(r, { maxWait: i })
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function R(e, t, n) {
|
|
110
122
|
return m(e, t, {
|
|
111
123
|
...n,
|
|
112
124
|
immediate: !0
|
|
@@ -114,21 +126,21 @@ function F(e, t, n) {
|
|
|
114
126
|
}
|
|
115
127
|
//#endregion
|
|
116
128
|
//#region ../../node_modules/.pnpm/@vueuse+core@14.2.1_vue@3.5.33_typescript@6.0.3_/node_modules/@vueuse/core/dist/index.js
|
|
117
|
-
var
|
|
129
|
+
var z = v ? window : void 0;
|
|
118
130
|
v && window.document, v && window.navigator, v && window.location;
|
|
119
|
-
function
|
|
131
|
+
function B(e) {
|
|
120
132
|
let t = d(e);
|
|
121
133
|
return t?.$el ?? t;
|
|
122
134
|
}
|
|
123
|
-
function
|
|
135
|
+
function V(...e) {
|
|
124
136
|
let t = (e, t, n, r) => (e.addEventListener(t, n, r), () => e.removeEventListener(t, n, r)), n = f(() => {
|
|
125
|
-
let t =
|
|
137
|
+
let t = k(d(e[0])).filter((e) => e != null);
|
|
126
138
|
return t.every((e) => typeof e != "string") ? t : void 0;
|
|
127
139
|
});
|
|
128
|
-
return
|
|
129
|
-
n.value?.map((e) =>
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
return R(() => [
|
|
141
|
+
n.value?.map((e) => B(e)) ?? [z].filter((e) => e != null),
|
|
142
|
+
k(d(n.value ? e[1] : e[0])),
|
|
143
|
+
k(u(n.value ? e[2] : e[1])),
|
|
132
144
|
d(n.value ? e[3] : e[2])
|
|
133
145
|
], ([e, n, r, i], a, o) => {
|
|
134
146
|
if (!e?.length || !n?.length || !r?.length) return;
|
|
@@ -138,23 +150,23 @@ function R(...e) {
|
|
|
138
150
|
});
|
|
139
151
|
}, { flush: "post" });
|
|
140
152
|
}
|
|
141
|
-
var
|
|
142
|
-
function
|
|
143
|
-
let { window: r =
|
|
153
|
+
var H = !1;
|
|
154
|
+
function U(e, t, n = {}) {
|
|
155
|
+
let { window: r = z, ignore: i = [], capture: a = !0, detectIframe: o = !1, controls: s = !1 } = n;
|
|
144
156
|
if (!r) return s ? {
|
|
145
157
|
stop: S,
|
|
146
158
|
cancel: S,
|
|
147
159
|
trigger: S
|
|
148
160
|
} : S;
|
|
149
|
-
if (C && !
|
|
150
|
-
|
|
161
|
+
if (C && !H) {
|
|
162
|
+
H = !0;
|
|
151
163
|
let e = { passive: !0 };
|
|
152
164
|
Array.from(r.document.body.children).forEach((t) => t.addEventListener("click", S, e)), r.document.documentElement.addEventListener("click", S, e);
|
|
153
165
|
}
|
|
154
166
|
let c = !0, l = (e) => d(i).some((t) => {
|
|
155
167
|
if (typeof t == "string") return Array.from(r.document.querySelectorAll(t)).some((t) => t === e.target || e.composedPath().includes(t));
|
|
156
168
|
{
|
|
157
|
-
let n =
|
|
169
|
+
let n = B(t);
|
|
158
170
|
return n && (e.target === n || e.composedPath().includes(n));
|
|
159
171
|
}
|
|
160
172
|
});
|
|
@@ -167,7 +179,7 @@ function B(e, t, n = {}) {
|
|
|
167
179
|
return r == null || !Array.isArray(r) ? !1 : r.some((e) => e.el === t.target || t.composedPath().includes(e.el));
|
|
168
180
|
}
|
|
169
181
|
let p = (n) => {
|
|
170
|
-
let r =
|
|
182
|
+
let r = B(e);
|
|
171
183
|
if (n.target != null && !(!(r instanceof Element) && u(e) && f(e, n)) && !(!r || r === n.target || n.composedPath().includes(r))) {
|
|
172
184
|
if ("detail" in n && n.detail === 0 && (c = !l(n)), !c) {
|
|
173
185
|
c = !0;
|
|
@@ -176,7 +188,7 @@ function B(e, t, n = {}) {
|
|
|
176
188
|
t(n);
|
|
177
189
|
}
|
|
178
190
|
}, m = !1, h = [
|
|
179
|
-
|
|
191
|
+
V(r, "click", (e) => {
|
|
180
192
|
m || (m = !0, setTimeout(() => {
|
|
181
193
|
m = !1;
|
|
182
194
|
}, 0), p(e));
|
|
@@ -184,13 +196,13 @@ function B(e, t, n = {}) {
|
|
|
184
196
|
passive: !0,
|
|
185
197
|
capture: a
|
|
186
198
|
}),
|
|
187
|
-
|
|
188
|
-
let n =
|
|
199
|
+
V(r, "pointerdown", (t) => {
|
|
200
|
+
let n = B(e);
|
|
189
201
|
c = !l(t) && !!(n && !t.composedPath().includes(n));
|
|
190
202
|
}, { passive: !0 }),
|
|
191
|
-
o &&
|
|
203
|
+
o && V(r, "blur", (n) => {
|
|
192
204
|
setTimeout(() => {
|
|
193
|
-
let i =
|
|
205
|
+
let i = B(e);
|
|
194
206
|
r.document.activeElement?.tagName === "IFRAME" && !i?.contains(r.document.activeElement) && t(n);
|
|
195
207
|
}, 0);
|
|
196
208
|
}, { passive: !0 })
|
|
@@ -205,22 +217,22 @@ function B(e, t, n = {}) {
|
|
|
205
217
|
}
|
|
206
218
|
} : g;
|
|
207
219
|
}
|
|
208
|
-
function
|
|
220
|
+
function W() {
|
|
209
221
|
let e = l(!1), n = r();
|
|
210
222
|
return n && t(() => {
|
|
211
223
|
e.value = !0;
|
|
212
224
|
}, n), e;
|
|
213
225
|
}
|
|
214
226
|
/* @__NO_SIDE_EFFECTS__ */
|
|
215
|
-
function
|
|
216
|
-
let t =
|
|
227
|
+
function G(e) {
|
|
228
|
+
let t = W();
|
|
217
229
|
return f(() => (t.value, !!e()));
|
|
218
230
|
}
|
|
219
|
-
function
|
|
220
|
-
let { window: r =
|
|
231
|
+
function K(e, t, n = {}) {
|
|
232
|
+
let { window: r = z, ...i } = n, a, o = /* @__PURE__ */ G(() => r && "MutationObserver" in r), s = () => {
|
|
221
233
|
a &&= (a.disconnect(), void 0);
|
|
222
234
|
}, c = m(f(() => {
|
|
223
|
-
let t =
|
|
235
|
+
let t = k(d(e)).map(B).filter(y);
|
|
224
236
|
return new Set(t);
|
|
225
237
|
}), (e) => {
|
|
226
238
|
s(), o.value && e.size && (a = new MutationObserver(t), e.forEach((e) => a.observe(e, i)));
|
|
@@ -236,33 +248,33 @@ function U(e, t, n = {}) {
|
|
|
236
248
|
takeRecords: l
|
|
237
249
|
};
|
|
238
250
|
}
|
|
239
|
-
var
|
|
251
|
+
var q = Symbol("vueuse-ssr-width");
|
|
240
252
|
/* @__NO_SIDE_EFFECTS__ */
|
|
241
|
-
function
|
|
242
|
-
let e = s() ? _(
|
|
253
|
+
function J() {
|
|
254
|
+
let e = s() ? _(q, null) : null;
|
|
243
255
|
return typeof e == "number" ? e : void 0;
|
|
244
256
|
}
|
|
245
|
-
function
|
|
246
|
-
let { window: r =
|
|
257
|
+
function Y(e, t = {}) {
|
|
258
|
+
let { window: r = z, ssrWidth: i = /* @__PURE__ */ J() } = t, a = /* @__PURE__ */ G(() => r && "matchMedia" in r && typeof r.matchMedia == "function"), o = l(typeof i == "number"), s = l(), c = l(!1);
|
|
247
259
|
return n(() => {
|
|
248
260
|
if (o.value) {
|
|
249
261
|
o.value = !a.value, c.value = d(e).split(",").some((e) => {
|
|
250
262
|
let t = e.includes("not all"), n = e.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/), r = e.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/), a = !!(n || r);
|
|
251
|
-
return n && a && (a = i >=
|
|
263
|
+
return n && a && (a = i >= O(n[1])), r && a && (a = i <= O(r[1])), t ? !a : a;
|
|
252
264
|
});
|
|
253
265
|
return;
|
|
254
266
|
}
|
|
255
267
|
a.value && (s.value = r.matchMedia(d(e)), c.value = s.value.matches);
|
|
256
|
-
}),
|
|
268
|
+
}), V(s, "change", (e) => {
|
|
257
269
|
c.value = e.matches;
|
|
258
270
|
}, { passive: !0 }), f(() => c.value);
|
|
259
271
|
}
|
|
260
|
-
function
|
|
261
|
-
let { window: r =
|
|
272
|
+
function X(e, t, n = {}) {
|
|
273
|
+
let { window: r = z, ...i } = n, a, o = /* @__PURE__ */ G(() => r && "ResizeObserver" in r), s = () => {
|
|
262
274
|
a &&= (a.disconnect(), void 0);
|
|
263
275
|
}, c = m(f(() => {
|
|
264
276
|
let t = d(e);
|
|
265
|
-
return Array.isArray(t) ? t.map((e) =>
|
|
277
|
+
return Array.isArray(t) ? t.map((e) => B(e)) : [B(t)];
|
|
266
278
|
}), (e) => {
|
|
267
279
|
if (s(), o.value && r) {
|
|
268
280
|
a = new ResizeObserver(t);
|
|
@@ -279,10 +291,10 @@ function q(e, t, n = {}) {
|
|
|
279
291
|
stop: l
|
|
280
292
|
};
|
|
281
293
|
}
|
|
282
|
-
function
|
|
294
|
+
function Z(e, t = {}) {
|
|
283
295
|
let { reset: n = !0, windowResize: r = !0, windowScroll: i = !0, immediate: a = !0, updateTiming: o = "sync" } = t, s = l(0), c = l(0), u = l(0), d = l(0), f = l(0), p = l(0), h = l(0), g = l(0);
|
|
284
296
|
function _() {
|
|
285
|
-
let t =
|
|
297
|
+
let t = B(e);
|
|
286
298
|
if (!t) {
|
|
287
299
|
n && (s.value = 0, c.value = 0, u.value = 0, d.value = 0, f.value = 0, p.value = 0, h.value = 0, g.value = 0);
|
|
288
300
|
return;
|
|
@@ -293,10 +305,10 @@ function J(e, t = {}) {
|
|
|
293
305
|
function v() {
|
|
294
306
|
o === "sync" ? _() : o === "next-frame" && requestAnimationFrame(() => _());
|
|
295
307
|
}
|
|
296
|
-
return
|
|
308
|
+
return X(e, v), m(() => B(e), (e) => !e && v()), K(e, v, { attributeFilter: ["style", "class"] }), i && V("scroll", v, {
|
|
297
309
|
capture: !0,
|
|
298
310
|
passive: !0
|
|
299
|
-
}), r &&
|
|
311
|
+
}), r && V("resize", v, { passive: !0 }), N(() => {
|
|
300
312
|
a && v();
|
|
301
313
|
}), {
|
|
302
314
|
height: s,
|
|
@@ -311,4 +323,4 @@ function J(e, t = {}) {
|
|
|
311
323
|
};
|
|
312
324
|
}
|
|
313
325
|
//#endregion
|
|
314
|
-
export {
|
|
326
|
+
export { j as a, I as c, Y as i, L as l, Z as n, P as o, V as r, F as s, U as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as e } from "./rolldown-runtime-BZGGJVDF.js";
|
|
2
2
|
import { A as t, B as n, C as r, J as i, N as a, O as o, Q as s, S as c, W as ee, X as te, et as ne, i as l, it as re, k as u, tt as d, w as f } from "./vue.runtime.esm-bundler-DpvJL-nX.js";
|
|
3
|
-
import { $ as ie, $t as ae, A as oe, At as se, B as ce, Bt as le, C as ue, Ct as de, D as fe, Dt as p, E as m, Et as h, F as g, Ft as _, G as v, Gt as y, H as b, Ht as x, I as S, It as C, J as w, Jt as T, K as E, Kt as D, L as O, Lt as k, M as A, Mt as j, N as M, Nt as N, O as P, Ot as F, P as I, Pt as L, Q as R, Qt as z, R as B, Rt as V, S as pe, St as me, T as he, Tt as ge, U as _e, Ut as ve, V as ye, Vt as be, W as xe, Wt as Se, X as Ce, Xt as we, Y as Te, Yt as Ee, Z as De, Zt as Oe, _ as ke, _t as H, a as Ae, an as je, at as Me, b as Ne, bt as Pe, c as Fe, cn as Ie, ct as Le, d as Re, dn as ze, dt as Be, en as Ve, et as He, f as U, fn as Ue, ft as We, g as Ge, gt as W, h as Ke, hn as qe, ht as Je, i as Ye, in as Xe, it as Ze, j as Qe, jt as $e, k as et, kt as tt, l as G, ln as nt, lt as rt, m as it, mn as at, mt as ot, n as st, nn as ct, nt as lt, o as ut, on as K, ot as dt, p as ft, pn as pt, pt as mt, q as ht, qt as gt, r as _t, rn as vt, rt as yt, s as bt, sn as xt, st as St, t as Ct, tn as wt, tt as Tt, u as Et, un as Dt, ut as Ot, v as kt, vt as At, w as jt, wt as Mt, x as Nt, xt as Pt, y as q, yt as Ft, z as It, zt as Lt } from "./dist-
|
|
3
|
+
import { $ as ie, $t as ae, A as oe, At as se, B as ce, Bt as le, C as ue, Ct as de, D as fe, Dt as p, E as m, Et as h, F as g, Ft as _, G as v, Gt as y, H as b, Ht as x, I as S, It as C, J as w, Jt as T, K as E, Kt as D, L as O, Lt as k, M as A, Mt as j, N as M, Nt as N, O as P, Ot as F, P as I, Pt as L, Q as R, Qt as z, R as B, Rt as V, S as pe, St as me, T as he, Tt as ge, U as _e, Ut as ve, V as ye, Vt as be, W as xe, Wt as Se, X as Ce, Xt as we, Y as Te, Yt as Ee, Z as De, Zt as Oe, _ as ke, _t as H, a as Ae, an as je, at as Me, b as Ne, bt as Pe, c as Fe, cn as Ie, ct as Le, d as Re, dn as ze, dt as Be, en as Ve, et as He, f as U, fn as Ue, ft as We, g as Ge, gt as W, h as Ke, hn as qe, ht as Je, i as Ye, in as Xe, it as Ze, j as Qe, jt as $e, k as et, kt as tt, l as G, ln as nt, lt as rt, m as it, mn as at, mt as ot, n as st, nn as ct, nt as lt, o as ut, on as K, ot as dt, p as ft, pn as pt, pt as mt, q as ht, qt as gt, r as _t, rn as vt, rt as yt, s as bt, sn as xt, st as St, t as Ct, tn as wt, tt as Tt, u as Et, un as Dt, ut as Ot, v as kt, vt as At, w as jt, wt as Mt, x as Nt, xt as Pt, y as q, yt as Ft, z as It, zt as Lt } from "./dist-iLBdeBDR.js";
|
|
4
4
|
//#region ../../node_modules/.pnpm/@tiptap+vue-3@3.22.5_@floating-ui+dom@1.7.6_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__@tip_7097ea972d8e6e4e31801879e2c3d1b4/node_modules/@tiptap/vue-3/dist/index.js
|
|
5
5
|
var Rt = /* @__PURE__ */ e({
|
|
6
6
|
CommandManager: () => Ct,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as e, c as t } from "./dist-
|
|
1
|
+
import { Gt as e, c as t } from "./dist-iLBdeBDR.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/@tiptap+extension-subscript@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__@tiptap+pm@3.22.5/node_modules/@tiptap/extension-subscript/dist/index.js
|
|
3
3
|
var n = t.create({
|
|
4
4
|
name: "subscript",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as e, c as t, i as n } from "./dist-
|
|
1
|
+
import { Gt as e, c as t, i as n } from "./dist-iLBdeBDR.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/@tiptap+extension-text-style@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5_/node_modules/@tiptap/extension-text-style/dist/index.js
|
|
3
3
|
var r = 20, i = (e, t = 0) => {
|
|
4
4
|
let n = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Color as e } from "./dist-
|
|
1
|
+
import { Color as e } from "./dist-Cp0zXPAD.js";
|
|
2
2
|
//#region ../../node_modules/.pnpm/@tiptap+extension-color@3.22.5_@tiptap+extension-text-style@3.22.5_@tiptap+core@3.22.5_@tiptap+pm@3.22.5__/node_modules/@tiptap/extension-color/dist/index.js
|
|
3
3
|
var t = e;
|
|
4
4
|
//#endregion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Cn as e, Ct as t, Dn as n, En as r, Gt as i, Ht as a, Jt as o, Mt as s, Nt as c, Qt as l, Sn as u, St as d, Tn as f, Ut as p, Z as m, _ as ee, _n as h, at as g, bn as _, c as v, gn as te, hn as y, i as b, jt as x, nn as ne, ot as re, pn as S, rt as ie, u as C, v as ae, vn as w, wn as oe, xn as T, yn as E } from "./dist-
|
|
2
|
-
import { Link as se } from "./dist
|
|
3
|
-
import { Underline as ce } from "./dist-
|
|
1
|
+
import { Cn as e, Ct as t, Dn as n, En as r, Gt as i, Ht as a, Jt as o, Mt as s, Nt as c, Qt as l, Sn as u, St as d, Tn as f, Ut as p, Z as m, _ as ee, _n as h, at as g, bn as _, c as v, gn as te, hn as y, i as b, jt as x, nn as ne, ot as re, pn as S, rt as ie, u as C, v as ae, vn as w, wn as oe, xn as T, yn as E } from "./dist-iLBdeBDR.js";
|
|
2
|
+
import { Link as se } from "./dist-DDJIWTRY.js";
|
|
3
|
+
import { Underline as ce } from "./dist-KoBJjK1G.js";
|
|
4
4
|
//#region ../../node_modules/.pnpm/@tiptap+core@3.22.5_@tiptap+pm@3.22.5/node_modules/@tiptap/core/dist/jsx-runtime/jsx-runtime.js
|
|
5
5
|
var D = (e, t) => {
|
|
6
6
|
if (e === "slot") return 0;
|