@templatical/editor 0.17.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiFeatureMenu-zwSmx9ZW.js → AiFeatureMenu-Cr8pLXMj.js} +1 -1
- package/dist/{CloudEditor-5ahgKRX8.js → CloudEditor-qzoNS8K4.js} +4 -4
- package/dist/{CollaboratorBar-6PtX-1AS.js → CollaboratorBar-BkfR0CyD.js} +1 -1
- package/dist/{MergeTagSuggestion-jIpT_jmz.js → MergeTagSuggestion-DCuaSOPQ.js} +148 -162
- package/dist/{ModuleBrowserModal-B5IH3-1I.js → ModuleBrowserModal-XEHfEO0O.js} +4 -4
- package/dist/{ModulePreviewCanvas-CYOLWoZ9.js → ModulePreviewCanvas-BM8XUgbc.js} +1 -1
- package/dist/{ParagraphEditor-duWYA6Vv.js → ParagraphEditor-Cs47wU1V.js} +13 -13
- package/dist/{SaveModuleDialog-BPUly5h3.js → SaveModuleDialog-BgOS9vCQ.js} +2 -2
- package/dist/{SnapshotHistory-CmwU3nlN.js → SnapshotHistory-CLfYzwv9.js} +1 -1
- package/dist/{TestEmailModal-zfomMz9i.js → TestEmailModal-DTLfATNt.js} +2 -2
- package/dist/{TitleEditor-v0gtezZE.js → TitleEditor-Bq9S0C8Z.js} +5 -5
- package/dist/{TplModal-eGFG7k-y.js → TplModal-BW0FAtYe.js} +1 -1
- package/dist/{blockTypeIcons-Dd0w4T0I.js → blockTypeIcons-CvSfLeyV.js} +1 -1
- package/dist/bundle-stats.json +6 -6
- package/dist/cdn/chunks/{AiFeatureMenu-CtXsam7G.js → AiFeatureMenu-B0zH66nO.js} +2 -2
- package/dist/cdn/chunks/{AiFeatureMenu-CtXsam7G.js.map → AiFeatureMenu-B0zH66nO.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-DR7cpDfQ.js → BlockIssueBadge-yD4pKsU9.js} +2 -2
- package/dist/cdn/chunks/{BlockIssueBadge-DR7cpDfQ.js.map → BlockIssueBadge-yD4pKsU9.js.map} +1 -1
- package/dist/cdn/chunks/{CloudEditor-YC5y3AwC.js → CloudEditor-Da-aSmw5.js} +9 -9
- package/dist/cdn/chunks/{CloudEditor-YC5y3AwC.js.map → CloudEditor-Da-aSmw5.js.map} +1 -1
- package/dist/cdn/chunks/{CollaboratorBar-BOD2j8Kl.js → CollaboratorBar-DixaFis-.js} +3 -3
- package/dist/cdn/chunks/{CollaboratorBar-BOD2j8Kl.js.map → CollaboratorBar-DixaFis-.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-CPMWLHpa.js → ColorPicker-O6xXCPEg.js} +2 -2
- package/dist/cdn/chunks/{ColorPicker-CPMWLHpa.js.map → ColorPicker-O6xXCPEg.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownBlock-BmGqb7Zi.js → CountdownBlock-jgBPbI1p.js} +2 -2
- package/dist/cdn/chunks/{CountdownBlock-BmGqb7Zi.js.map → CountdownBlock-jgBPbI1p.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-BBwsdGbV.js → CountdownToolbar-DrmZwvIH.js} +4 -4
- package/dist/cdn/chunks/{CountdownToolbar-BBwsdGbV.js.map → CountdownToolbar-DrmZwvIH.js.map} +1 -1
- package/dist/cdn/chunks/{IssuesPanel-G0z-j3JS.js → IssuesPanel-CpyhXg9M.js} +3 -3
- package/dist/cdn/chunks/{IssuesPanel-G0z-j3JS.js.map → IssuesPanel-CpyhXg9M.js.map} +1 -1
- package/dist/cdn/chunks/{MergeTagSuggestion-rpZ6Ol-i.js → MergeTagSuggestion-BmMmdcRg.js} +2 -2
- package/dist/cdn/chunks/{MergeTagSuggestion-rpZ6Ol-i.js.map → MergeTagSuggestion-BmMmdcRg.js.map} +1 -1
- package/dist/cdn/chunks/{ModuleBrowserModal-DiLTY0ec.js → ModuleBrowserModal-DPQLFTti.js} +5 -5
- package/dist/cdn/chunks/{ModuleBrowserModal-DiLTY0ec.js.map → ModuleBrowserModal-DPQLFTti.js.map} +1 -1
- package/dist/cdn/chunks/{ModulePreviewCanvas-CdV5mEQ6.js → ModulePreviewCanvas-Csc6M0RA.js} +2 -2
- package/dist/cdn/chunks/{ModulePreviewCanvas-CdV5mEQ6.js.map → ModulePreviewCanvas-Csc6M0RA.js.map} +1 -1
- package/dist/cdn/chunks/{NumberWithSuffix-lqQlf0Uk.js → NumberWithSuffix-YmcsBmCd.js} +2 -2
- package/dist/cdn/chunks/{NumberWithSuffix-lqQlf0Uk.js.map → NumberWithSuffix-YmcsBmCd.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-BYoZfURd.js → ParagraphEditor-DR5AcHB0.js} +17 -17
- package/dist/cdn/chunks/{ParagraphEditor-BYoZfURd.js.map → ParagraphEditor-DR5AcHB0.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-BnPRnXxy.js → RichTextEditorContent-52c2EuCT.js} +3 -3
- package/dist/cdn/chunks/{RichTextEditorContent-BnPRnXxy.js.map → RichTextEditorContent-52c2EuCT.js.map} +1 -1
- package/dist/cdn/chunks/{SaveModuleDialog-C9hUeWyG.js → SaveModuleDialog-dpRlVrrQ.js} +2 -2
- package/dist/cdn/chunks/{SaveModuleDialog-C9hUeWyG.js.map → SaveModuleDialog-dpRlVrrQ.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-wFvnU3Jr.js → TitleEditor-DtQTPAg3.js} +8 -8
- package/dist/cdn/chunks/{TitleEditor-wFvnU3Jr.js.map → TitleEditor-DtQTPAg3.js.map} +1 -1
- package/dist/cdn/chunks/{blockTypeIcons-DNMvDz5B.js → blockTypeIcons-B0ktHdUG.js} +3 -3
- package/dist/cdn/chunks/{blockTypeIcons-DNMvDz5B.js.map → blockTypeIcons-B0ktHdUG.js.map} +1 -1
- package/dist/cdn/chunks/{extensions-DxcxIlwT.js → extensions-CiewgCtw.js} +4 -4
- package/dist/cdn/chunks/{extensions-DxcxIlwT.js.map → extensions-CiewgCtw.js.map} +1 -1
- package/dist/cdn/chunks/{features-B3qgDBHf.js → features-DLu_D-6j.js} +6 -6
- package/dist/cdn/chunks/{features-B3qgDBHf.js.map → features-DLu_D-6j.js.map} +1 -1
- package/dist/cdn/chunks/{icons-kiMqh99T.js → icons-B03IY-pm.js} +2 -2
- package/dist/cdn/chunks/{icons-kiMqh99T.js.map → icons-B03IY-pm.js.map} +1 -1
- package/dist/cdn/chunks/{media-library-C5ogiZ4f.js → media-library-CYF18RNk.js} +3 -3
- package/dist/cdn/chunks/{media-library-C5ogiZ4f.js.map → media-library-CYF18RNk.js.map} +1 -1
- package/dist/cdn/chunks/{quality-BftAn_AJ.js → quality-LmsngN2B.js} +2 -2
- package/dist/cdn/chunks/{quality-BftAn_AJ.js.map → quality-LmsngN2B.js.map} +1 -1
- package/dist/cdn/chunks/{renderer-Cdlo4MVX.js → renderer-DAeNIQqa.js} +4 -4
- package/dist/cdn/chunks/renderer-DAeNIQqa.js.map +1 -0
- package/dist/cdn/chunks/{src-GBA8oUL2.js → src-CModhUDN.js} +4 -4
- package/dist/cdn/chunks/{src-GBA8oUL2.js.map → src-CModhUDN.js.map} +1 -1
- package/dist/cdn/chunks/{styles-B6rdq9JB.js → styles-hd_LBGaF.js} +10 -10
- package/dist/cdn/chunks/{styles-B6rdq9JB.js.map → styles-hd_LBGaF.js.map} +1 -1
- package/dist/cdn/chunks/{tiptap-zpXk5mN4.js → tiptap-C2indasq.js} +806 -811
- package/dist/cdn/chunks/tiptap-C2indasq.js.map +1 -0
- package/dist/cdn/editor.js +5 -5
- package/dist/{dist-BReUTadj.js → dist-8TKf-Jpg.js} +2 -2
- package/dist/{dist-BE9m_i60.js → dist-BDVm2MrW.js} +1 -1
- package/dist/{dist-D1WmF8BF.js → dist-BKKo5wPC.js} +1 -1
- package/dist/{dist-DhY2di4U.js → dist-COSN3oDJ.js} +1 -1
- package/dist/{dist-CHLoeloJ.js → dist-CcX5sLpz.js} +1 -1
- package/dist/{dist-D9g4CSnm.js → dist-CgTKuIUN.js} +300 -298
- package/dist/{dist-i_Z7t67A.js → dist-Cx7IUM7J.js} +22 -15
- package/dist/{dist-Bm07Z3Es.js → dist-D6M5FAY7.js} +1 -1
- package/dist/{dist-DGEaAAIb.js → dist-DXAZUjj3.js} +1 -1
- package/dist/{dist-BXR1aBJV.js → dist-DbekZDmJ.js} +1 -1
- package/dist/{dist-DcKlLQ8d.js → dist-Dk5AdwoQ.js} +1 -1
- package/dist/{dist-BPPeDw62.js → dist-YSCPD13F.js} +1 -1
- package/dist/{extensions-B4lu0KkE.js → extensions-DhgbhfBX.js} +3 -3
- package/dist/{styles-mBSbDYeX.js → styles-BWC0jc5-.js} +4 -4
- package/dist/templatical-editor.js +3 -3
- package/dist/{useEditorCore-CaaHPKjm.js → useEditorCore-FLMIhtv1.js} +2 -2
- package/package.json +8 -8
- package/dist/cdn/chunks/renderer-Cdlo4MVX.js.map +0 -1
- package/dist/cdn/chunks/tiptap-zpXk5mN4.js.map +0 -1
package/dist/cdn/chunks/{SaveModuleDialog-C9hUeWyG.js.map → SaveModuleDialog-dpRlVrrQ.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SaveModuleDialog-C9hUeWyG.js","names":[],"sources":["../../../src/cloud/components/SaveModuleDialog.vue","../../../src/cloud/components/SaveModuleDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SaveModuleDialog-dpRlVrrQ.js","names":[],"sources":["../../../src/cloud/components/SaveModuleDialog.vue","../../../src/cloud/components/SaveModuleDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { K as e, M as t, T as n, ct as r, d as i, f as a, g as o, h as s, it as c, m as l, ot as u, p as d, st as ee, u as f, v as p, x as m, y as h } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { Bt as te, Ct as ne, H as re, Q as g, Z as _, dn as v, ln as y, nn as b, tn as ie } from "./features-
|
|
3
|
-
import { B as ae, H as oe, X as se, Z as ce, b as x } from "./icons-
|
|
4
|
-
import { n as S, t as C } from "./RichTextEditorContent-
|
|
2
|
+
import { Bt as te, Ct as ne, H as re, Q as g, Z as _, dn as v, ln as y, nn as b, tn as ie } from "./features-DLu_D-6j.js";
|
|
3
|
+
import { B as ae, H as oe, X as se, Z as ce, b as x } from "./icons-B03IY-pm.js";
|
|
4
|
+
import { n as S, t as C } from "./RichTextEditorContent-52c2EuCT.js";
|
|
5
5
|
//#region src/components/blocks/TitleEditor.vue?vue&type=script&setup=true&lang.ts
|
|
6
6
|
var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme", "aria-label"], E = ["aria-label", "title"], D = ["aria-label", "title"], O = ["aria-label", "title"], k = {
|
|
7
7
|
key: 0,
|
|
@@ -27,10 +27,10 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
|
|
|
27
27
|
editorName: "TitleEditor",
|
|
28
28
|
async loadExtensions({ mergeTags: e, syntax: t, triggerChar: n, autocompleteEnabled: r, suggestionEmptyText: i }) {
|
|
29
29
|
let [{ Editor: a, EditorContent: o }, { default: s }, { default: c }, { MergeTagNode: l, MergeTagSuggestion: u, LogicMergeTagNode: d }] = await Promise.all([
|
|
30
|
-
import("./tiptap-
|
|
31
|
-
import("./tiptap-
|
|
32
|
-
import("./tiptap-
|
|
33
|
-
import("./extensions-
|
|
30
|
+
import("./tiptap-C2indasq.js").then((e) => e.r),
|
|
31
|
+
import("./tiptap-C2indasq.js").then((e) => e.i),
|
|
32
|
+
import("./tiptap-C2indasq.js").then((e) => e.l),
|
|
33
|
+
import("./extensions-CiewgCtw.js")
|
|
34
34
|
]);
|
|
35
35
|
return {
|
|
36
36
|
TiptapEditor: a,
|
|
@@ -192,4 +192,4 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
|
|
|
192
192
|
//#endregion
|
|
193
193
|
export { P as default };
|
|
194
194
|
|
|
195
|
-
//# sourceMappingURL=TitleEditor-
|
|
195
|
+
//# sourceMappingURL=TitleEditor-DtQTPAg3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TitleEditor-wFvnU3Jr.js","names":[],"sources":["../../../src/components/blocks/TitleEditor.vue","../../../src/components/blocks/TitleEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TitleEditor-DtQTPAg3.js","names":[],"sources":["../../../src/components/blocks/TitleEditor.vue","../../../src/components/blocks/TitleEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { At as e, Tt as t, _t as n, mt as r, xt as i } from "./features-
|
|
2
|
-
import { D as a, E as o, J as s, P as c, U as l, a as u, j as d, w as f } from "./icons-
|
|
1
|
+
import { At as e, Tt as t, _t as n, mt as r, xt as i } from "./features-DLu_D-6j.js";
|
|
2
|
+
import { D as a, E as o, J as s, P as c, U as l, a as u, j as d, w as f } from "./icons-B03IY-pm.js";
|
|
3
3
|
//#region src/utils/blockTypeIcons.ts
|
|
4
4
|
var p = {
|
|
5
5
|
section: s,
|
|
@@ -19,4 +19,4 @@ var p = {
|
|
|
19
19
|
//#endregion
|
|
20
20
|
export { p as t };
|
|
21
21
|
|
|
22
|
-
//# sourceMappingURL=blockTypeIcons-
|
|
22
|
+
//# sourceMappingURL=blockTypeIcons-B0ktHdUG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blockTypeIcons-
|
|
1
|
+
{"version":3,"file":"blockTypeIcons-B0ktHdUG.js","names":[],"sources":["../../../src/utils/blockTypeIcons.ts"],"sourcesContent":["import {\n Code,\n Columns3,\n Heading,\n Image,\n Minus,\n MoveVertical,\n Navigation,\n Pilcrow,\n Play,\n RectangleHorizontal,\n Share2,\n Table,\n Timer,\n} from \"@lucide/vue\";\nimport type { Component } from \"vue\";\n\nexport const blockTypeIcons: Record<string, Component> = {\n section: Columns3,\n title: Heading,\n paragraph: Pilcrow,\n image: Image,\n button: RectangleHorizontal,\n divider: Minus,\n video: Play,\n social: Share2,\n menu: Navigation,\n table: Table,\n spacer: MoveVertical,\n countdown: Timer,\n html: Code,\n};\n"],"mappings":";;;AAiBA,IAAa,IAA4C;CACvD,SAAS;CACT,OAAO;CACP,WAAW;CACX,OAAO;CACP,QAAQ;CACR,SAAS;CACT,OAAO;CACP,QAAQ;CACR,MAAM;CACN,OAAO;CACP,QAAQ;CACR,WAAW;CACX,MAAM;AACR"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { H as e, M as t, O as n, V as r, Z as i, c as a, ct as o, f as s, g as c, h as l, it as u, l as d, m as f, o as p, ot as m, p as h, st as g, x as _ } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { Bt as v, Dn as y, On as b, Tn as x, it as S, jn as C } from "./features-
|
|
3
|
-
import { _ as w, g as T, h as E, n as D, t as O, v as k, y as A } from "./tiptap-
|
|
4
|
-
import { i as j, r as M, t as N } from "./MergeTagSuggestion-
|
|
2
|
+
import { Bt as v, Dn as y, On as b, Tn as x, it as S, jn as C } from "./features-DLu_D-6j.js";
|
|
3
|
+
import { _ as w, g as T, h as E, n as D, t as O, v as k, y as A } from "./tiptap-C2indasq.js";
|
|
4
|
+
import { i as j, r as M, t as N } from "./MergeTagSuggestion-BmMmdcRg.js";
|
|
5
5
|
//#region src/extensions/FontSize.ts
|
|
6
6
|
var P = E.create({
|
|
7
7
|
name: "fontSize",
|
|
@@ -418,4 +418,4 @@ var U = w.create({
|
|
|
418
418
|
//#endregion
|
|
419
419
|
export { P as FontSize, F as LetterSpacing, I as LineHeight, U as LogicMergeTagNode, q as MergeTagNode, N as MergeTagSuggestion, M as filterMergeTags, j as handleSuggestionKeyDown };
|
|
420
420
|
|
|
421
|
-
//# sourceMappingURL=extensions-
|
|
421
|
+
//# sourceMappingURL=extensions-CiewgCtw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions-DxcxIlwT.js","names":[],"sources":["../../../src/extensions/FontSize.ts","../../../src/extensions/LetterSpacing.ts","../../../src/extensions/LineHeight.ts","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/isNodeSelected.ts","../../../src/extensions/renderVueNodeView.ts","../../../src/extensions/LogicMergeTagNode.ts","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNode.ts"],"sourcesContent":["import { Extension } from \"@tiptap/core\";\n\nexport interface FontSizeOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n fontSize: {\n setFontSize: (size: string) => ReturnType;\n unsetFontSize: () => ReturnType;\n };\n }\n}\n\nexport const FontSize = Extension.create<FontSizeOptions>({\n name: \"fontSize\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n fontSize: {\n default: null,\n parseHTML: (element) =>\n element.style.fontSize?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.fontSize) {\n return {};\n }\n return {\n style: `font-size: ${attributes.fontSize}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setFontSize:\n (size: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { fontSize: size }).run();\n },\n unsetFontSize:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { fontSize: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LetterSpacingOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n letterSpacing: {\n setLetterSpacing: (spacing: string) => ReturnType;\n unsetLetterSpacing: () => ReturnType;\n };\n }\n}\n\nexport const LetterSpacing = Extension.create<LetterSpacingOptions>({\n name: \"letterSpacing\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n letterSpacing: {\n default: null,\n parseHTML: (element) =>\n element.style.letterSpacing?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.letterSpacing) {\n return {};\n }\n return {\n style: `letter-spacing: ${attributes.letterSpacing}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLetterSpacing:\n (spacing: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { letterSpacing: spacing }).run();\n },\n unsetLetterSpacing:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { letterSpacing: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LineHeightOptions {\n types: string[];\n defaultLineHeight: string;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n lineHeight: {\n setLineHeight: (lineHeight: string) => ReturnType;\n unsetLineHeight: () => ReturnType;\n };\n }\n}\n\nexport const LineHeight = Extension.create<LineHeightOptions>({\n name: \"lineHeight\",\n\n addOptions() {\n return {\n types: [\"paragraph\"],\n defaultLineHeight: \"1.5\",\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n lineHeight: {\n default: null,\n parseHTML: (element) => element.style.lineHeight || null,\n renderHTML: (attributes) => {\n if (!attributes.lineHeight) {\n return {};\n }\n return {\n style: `line-height: ${attributes.lineHeight}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLineHeight:\n (lineHeight: string) =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.updateAttributes(type, { lineHeight }),\n );\n },\n unsetLineHeight:\n () =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.resetAttributes(type, \"lineHeight\"),\n );\n },\n };\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import type { Editor } from \"@tiptap/core\";\n\n/**\n * Decides whether MergeTagNode / LogicMergeTagNode should swallow a\n * Backspace/Delete keystroke (returning `true` from a TipTap keymap\n * suppresses the default behavior).\n *\n * Only suppresses in the cursor-adjacent case so the \"first press\n * selects the atom, second press deletes it\" UX works. Range selections\n * (Cmd+A, drag-select, double-click-word) explicitly fall through —\n * default `replaceSelection` then deletes the entire range including\n * any merge-tag atoms inside it.\n *\n * Earlier revisions also returned `true` whenever `nodesBetween($from,\n * $to)` found a merge tag anywhere in the selection range — which\n * silently broke Cmd+A + Backspace for any paragraph containing a\n * merge tag (entire deletion was cancelled, nothing happened). The\n * cursor-adjacent protection is the only piece that should survive.\n */\nexport function isNodeSelected(editor: Editor, nodeTypeName: string): boolean {\n const { $from, $to } = editor.state.selection;\n\n // Range selection: let TipTap's default range-delete run. Even when\n // the range contains atom nodes, ProseMirror's replaceSelection\n // handles atomic deletion correctly.\n if ($from.pos !== $to.pos) return false;\n\n // Cursor-adjacent atom: protect from single-keystroke deletion.\n // (`$from.pos > 0` guards against reading `nodeBefore` at doc start\n // where ProseMirror's index would be undefined for nodeBefore.)\n if ($from.pos > 0 && $from.nodeBefore?.type.name === nodeTypeName) {\n return true;\n }\n if ($from.nodeAfter?.type.name === nodeTypeName) {\n return true;\n }\n\n return false;\n}\n","import type { Component } from \"vue\";\nimport { VueNodeViewRenderer } from \"@tiptap/vue-3\";\n\n/**\n * Typed wrapper for VueNodeViewRenderer that handles the known type mismatch\n * between Vue SFC default exports and TipTap's expected component type.\n */\nexport function renderVueNodeView(component: Component) {\n return VueNodeViewRenderer(\n component as Parameters<typeof VueNodeViewRenderer>[0],\n );\n}\n","import LogicMergeTagNodeView from \"./LogicMergeTagNodeView.vue\";\nimport type { SyntaxPreset } from \"@templatical/types\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n SYNTAX_PRESETS,\n} from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface LogicMergeTagNodeOptions {\n syntax: SyntaxPreset;\n}\n\nexport const LogicMergeTagNode = Node.create<LogicMergeTagNodeOptions>({\n name: \"logicMergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n value: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-logic-merge-tag\") || \"\",\n },\n keyword: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-keyword\") || element.textContent || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-logic-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n if (!isLogicMergeTagValue(node.attrs.value, this.options.syntax)) {\n return [\"span\", {}, node.attrs.value];\n }\n\n const keyword = getLogicMergeTagKeyword(\n node.attrs.value,\n this.options.syntax,\n );\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-logic-merge-tag\": node.attrs.value,\n \"data-keyword\": keyword,\n }),\n keyword,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(LogicMergeTagNodeView);\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.logic.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.logic.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { getMergeTagLabel } = useMergeTag();\nconst { t } = useI18n();\n\nconst displayLabel = computed(() => getMergeTagLabel(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n if (newValue && newValue !== props.node.attrs.value) {\n // Update with new value and derive label from it\n props.updateAttributes({\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-32 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { getMergeTagLabel } = useMergeTag();\nconst { t } = useI18n();\n\nconst displayLabel = computed(() => getMergeTagLabel(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n if (newValue && newValue !== props.node.attrs.value) {\n // Update with new value and derive label from it\n props.updateAttributes({\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-32 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import MergeTagNodeView from \"./MergeTagNodeView.vue\";\nimport type { MergeTag, SyntaxPreset } from \"@templatical/types\";\nimport { getMergeTagLabel, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface MergeTagNodeOptions {\n mergeTags: MergeTag[];\n syntax: SyntaxPreset;\n}\n\n/**\n * Inserted-node attrs are intentionally limited to `label` + `value`. The\n * `MergeTag` interface also carries optional `group` / `description` for\n * the built-in picker, but those are display-only and must NOT leak into\n * the document JSON — they would bloat templates and pollute serialized\n * state. `Pick<>` makes the constraint explicit at the type level.\n */\nexport type InsertMergeTagAttrs = Pick<MergeTag, \"label\" | \"value\">;\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n mergeTagNode: {\n insertMergeTag: (attrs: InsertMergeTagAttrs) => ReturnType;\n };\n }\n}\n\nexport const MergeTagNode = Node.create<MergeTagNodeOptions>({\n name: \"mergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n mergeTags: [],\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n label: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-label\") || element.textContent || \"\",\n },\n value: {\n default: \"\",\n parseHTML: (element) => element.getAttribute(\"data-merge-tag\") || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = getMergeTagLabel(node.attrs.value, this.options.mergeTags);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-merge-tag\": node.attrs.value,\n \"data-label\": label,\n }),\n label,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(MergeTagNodeView);\n },\n\n addCommands() {\n return {\n insertMergeTag:\n (attrs: InsertMergeTagAttrs) =>\n ({ commands }) => {\n // Whitelist explicitly — even if a caller smuggles extra keys via\n // a wider type assertion, only label/value reach the node attrs.\n return commands.insertContent({\n type: this.name,\n attrs: { label: attrs.label, value: attrs.value },\n });\n },\n };\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.value.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags);\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.value.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags);\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extensions-CiewgCtw.js","names":[],"sources":["../../../src/extensions/FontSize.ts","../../../src/extensions/LetterSpacing.ts","../../../src/extensions/LineHeight.ts","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/LogicMergeTagNodeView.vue","../../../src/extensions/isNodeSelected.ts","../../../src/extensions/renderVueNodeView.ts","../../../src/extensions/LogicMergeTagNode.ts","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNodeView.vue","../../../src/extensions/MergeTagNode.ts"],"sourcesContent":["import { Extension } from \"@tiptap/core\";\n\nexport interface FontSizeOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n fontSize: {\n setFontSize: (size: string) => ReturnType;\n unsetFontSize: () => ReturnType;\n };\n }\n}\n\nexport const FontSize = Extension.create<FontSizeOptions>({\n name: \"fontSize\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n fontSize: {\n default: null,\n parseHTML: (element) =>\n element.style.fontSize?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.fontSize) {\n return {};\n }\n return {\n style: `font-size: ${attributes.fontSize}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setFontSize:\n (size: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { fontSize: size }).run();\n },\n unsetFontSize:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { fontSize: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LetterSpacingOptions {\n types: string[];\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n letterSpacing: {\n setLetterSpacing: (spacing: string) => ReturnType;\n unsetLetterSpacing: () => ReturnType;\n };\n }\n}\n\nexport const LetterSpacing = Extension.create<LetterSpacingOptions>({\n name: \"letterSpacing\",\n\n addOptions() {\n return {\n types: [\"textStyle\"],\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n letterSpacing: {\n default: null,\n parseHTML: (element) =>\n element.style.letterSpacing?.replace(/['\"]+/g, \"\") || null,\n renderHTML: (attributes) => {\n if (!attributes.letterSpacing) {\n return {};\n }\n return {\n style: `letter-spacing: ${attributes.letterSpacing}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLetterSpacing:\n (spacing: string) =>\n ({ chain }) => {\n return chain().setMark(\"textStyle\", { letterSpacing: spacing }).run();\n },\n unsetLetterSpacing:\n () =>\n ({ chain }) => {\n return chain()\n .setMark(\"textStyle\", { letterSpacing: null })\n .removeEmptyTextStyle()\n .run();\n },\n };\n },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport interface LineHeightOptions {\n types: string[];\n defaultLineHeight: string;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n lineHeight: {\n setLineHeight: (lineHeight: string) => ReturnType;\n unsetLineHeight: () => ReturnType;\n };\n }\n}\n\nexport const LineHeight = Extension.create<LineHeightOptions>({\n name: \"lineHeight\",\n\n addOptions() {\n return {\n types: [\"paragraph\"],\n defaultLineHeight: \"1.5\",\n };\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n lineHeight: {\n default: null,\n parseHTML: (element) => element.style.lineHeight || null,\n renderHTML: (attributes) => {\n if (!attributes.lineHeight) {\n return {};\n }\n return {\n style: `line-height: ${attributes.lineHeight}`,\n };\n },\n },\n },\n },\n ];\n },\n\n addCommands() {\n return {\n setLineHeight:\n (lineHeight: string) =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.updateAttributes(type, { lineHeight }),\n );\n },\n unsetLineHeight:\n () =>\n ({ commands }) => {\n return this.options.types.every((type) =>\n commands.resetAttributes(type, \"lineHeight\"),\n );\n },\n };\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport type { Editor } from \"@tiptap/core\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n value: string;\n keyword: string;\n };\n };\n editor: Editor;\n getPos: () => number;\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { syntax } = useMergeTag();\nconst { t } = useI18n();\n\nconst isValid = computed(() =>\n isLogicMergeTagValue(props.node.attrs.value, syntax),\n);\nconst displayKeyword = computed(() =>\n getLogicMergeTagKeyword(props.node.attrs.value, syntax),\n);\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\nlet handled = false;\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n handled = false;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n if (handled) {\n return;\n }\n handled = true;\n\n const newValue = editValue.value.trim();\n if (!newValue) {\n isEditing.value = false;\n return;\n }\n\n if (newValue !== props.node.attrs.value) {\n props.updateAttributes({\n value: newValue,\n keyword: isLogicMergeTagValue(newValue, syntax)\n ? getLogicMergeTagKeyword(newValue, syntax)\n : \"\",\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n :class=\"\n isValid\n ? 'tpl-logic-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase tpl:select-none'\n : ''\n \"\n :style=\"\n isValid\n ? 'background-color: transparent; border: 1.5px solid color-mix(in srgb, var(--tpl-primary) 50%, transparent); color: var(--tpl-primary);'\n : ''\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-40 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:normal-case tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode: valid merge tag -->\n <span\n v-else-if=\"isValid\"\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayKeyword }}\n </span>\n <!-- Display mode: invalid (plain text) -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ node.attrs.value }}\n </span>\n <button\n v-if=\"isValid\"\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import type { Editor } from \"@tiptap/core\";\n\n/**\n * Decides whether MergeTagNode / LogicMergeTagNode should swallow a\n * Backspace/Delete keystroke (returning `true` from a TipTap keymap\n * suppresses the default behavior).\n *\n * Only suppresses in the cursor-adjacent case so the \"first press\n * selects the atom, second press deletes it\" UX works. Range selections\n * (Cmd+A, drag-select, double-click-word) explicitly fall through —\n * default `replaceSelection` then deletes the entire range including\n * any merge-tag atoms inside it.\n *\n * Earlier revisions also returned `true` whenever `nodesBetween($from,\n * $to)` found a merge tag anywhere in the selection range — which\n * silently broke Cmd+A + Backspace for any paragraph containing a\n * merge tag (entire deletion was cancelled, nothing happened). The\n * cursor-adjacent protection is the only piece that should survive.\n */\nexport function isNodeSelected(editor: Editor, nodeTypeName: string): boolean {\n const { $from, $to } = editor.state.selection;\n\n // Range selection: let TipTap's default range-delete run. Even when\n // the range contains atom nodes, ProseMirror's replaceSelection\n // handles atomic deletion correctly.\n if ($from.pos !== $to.pos) return false;\n\n // Cursor-adjacent atom: protect from single-keystroke deletion.\n // (`$from.pos > 0` guards against reading `nodeBefore` at doc start\n // where ProseMirror's index would be undefined for nodeBefore.)\n if ($from.pos > 0 && $from.nodeBefore?.type.name === nodeTypeName) {\n return true;\n }\n if ($from.nodeAfter?.type.name === nodeTypeName) {\n return true;\n }\n\n return false;\n}\n","import type { Component } from \"vue\";\nimport { VueNodeViewRenderer } from \"@tiptap/vue-3\";\n\n/**\n * Typed wrapper for VueNodeViewRenderer that handles the known type mismatch\n * between Vue SFC default exports and TipTap's expected component type.\n */\nexport function renderVueNodeView(component: Component) {\n return VueNodeViewRenderer(\n component as Parameters<typeof VueNodeViewRenderer>[0],\n );\n}\n","import LogicMergeTagNodeView from \"./LogicMergeTagNodeView.vue\";\nimport type { SyntaxPreset } from \"@templatical/types\";\nimport {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n SYNTAX_PRESETS,\n} from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface LogicMergeTagNodeOptions {\n syntax: SyntaxPreset;\n}\n\nexport const LogicMergeTagNode = Node.create<LogicMergeTagNodeOptions>({\n name: \"logicMergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n value: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-logic-merge-tag\") || \"\",\n },\n keyword: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-keyword\") || element.textContent || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-logic-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n if (!isLogicMergeTagValue(node.attrs.value, this.options.syntax)) {\n return [\"span\", {}, node.attrs.value];\n }\n\n const keyword = getLogicMergeTagKeyword(\n node.attrs.value,\n this.options.syntax,\n );\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-logic-merge-tag\": node.attrs.value,\n \"data-keyword\": keyword,\n }),\n keyword,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(LogicMergeTagNodeView);\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.logic.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.logic.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n if (!isLogicMergeTagValue(fullValue, this.options.syntax)) {\n return;\n }\n\n const keyword = getLogicMergeTagKeyword(\n fullValue,\n this.options.syntax,\n );\n\n const node = this.type.create({\n value: fullValue,\n keyword,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { getMergeTagLabel } = useMergeTag();\nconst { t } = useI18n();\n\nconst displayLabel = computed(() => getMergeTagLabel(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n if (newValue && newValue !== props.node.attrs.value) {\n // Update with new value and derive label from it\n props.updateAttributes({\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-32 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport { NodeViewWrapper } from \"@tiptap/vue-3\";\nimport { computed, nextTick, ref } from \"vue\";\n\nconst props = defineProps<{\n node: {\n attrs: {\n label: string;\n value: string;\n };\n };\n deleteNode: () => void;\n updateAttributes: (attrs: Record<string, unknown>) => void;\n}>();\n\nconst { getMergeTagLabel } = useMergeTag();\nconst { t } = useI18n();\n\nconst displayLabel = computed(() => getMergeTagLabel(props.node.attrs.value));\n\nconst isEditing = ref(false);\nconst editValue = ref(\"\");\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nfunction startEditing(): void {\n editValue.value = props.node.attrs.value;\n isEditing.value = true;\n nextTick(() => {\n inputRef.value?.focus();\n inputRef.value?.select();\n });\n}\n\nfunction finishEditing(): void {\n const newValue = editValue.value.trim();\n if (newValue && newValue !== props.node.attrs.value) {\n // Update with new value and derive label from it\n props.updateAttributes({\n value: newValue,\n label: getMergeTagLabel(newValue),\n });\n }\n isEditing.value = false;\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finishEditing();\n } else if (event.key === \"Escape\") {\n isEditing.value = false;\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"span\"\n class=\"tpl-merge-tag-node tpl:group tpl:mx-0.5 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium tpl:select-none tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-primary) 20%, transparent);\n \"\n contenteditable=\"false\"\n >\n <!-- Edit mode -->\n <input\n v-if=\"isEditing\"\n ref=\"inputRef\"\n v-model=\"editValue\"\n type=\"text\"\n class=\"tpl:w-32 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-0.5 tpl:py-0 tpl:text-[1em] tpl:font-medium tpl:outline-none tpl:text-[var(--tpl-primary)]\"\n @blur=\"finishEditing\"\n @keydown=\"handleKeydown\"\n />\n <!-- Display mode -->\n <span\n v-else\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.editValue\"\n class=\"tpl-tooltip tpl:cursor-pointer\"\n :data-tooltip=\"node.attrs.value\"\n @click.stop=\"startEditing\"\n @keydown.enter.stop=\"startEditing\"\n @keydown.space.prevent.stop=\"startEditing\"\n >\n {{ displayLabel }}\n </span>\n <button\n type=\"button\"\n :aria-label=\"t.mergeTag.deleteMergeTag\"\n class=\"tpl-merge-tag-delete tpl:flex tpl:size-5 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:opacity-60 tpl:transition-all hover:tpl:opacity-100 tpl:text-[var(--tpl-primary)]\"\n contenteditable=\"false\"\n @click.stop.prevent=\"deleteNode\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n aria-hidden=\"true\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </NodeViewWrapper>\n</template>\n","import MergeTagNodeView from \"./MergeTagNodeView.vue\";\nimport type { MergeTag, SyntaxPreset } from \"@templatical/types\";\nimport { getMergeTagLabel, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { InputRule, mergeAttributes, Node, PasteRule } from \"@tiptap/core\";\nimport { isNodeSelected } from \"./isNodeSelected\";\nimport { renderVueNodeView } from \"./renderVueNodeView\";\n\nexport interface MergeTagNodeOptions {\n mergeTags: MergeTag[];\n syntax: SyntaxPreset;\n}\n\n/**\n * Inserted-node attrs are intentionally limited to `label` + `value`. The\n * `MergeTag` interface also carries optional `group` / `description` for\n * the built-in picker, but those are display-only and must NOT leak into\n * the document JSON — they would bloat templates and pollute serialized\n * state. `Pick<>` makes the constraint explicit at the type level.\n */\nexport type InsertMergeTagAttrs = Pick<MergeTag, \"label\" | \"value\">;\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n mergeTagNode: {\n insertMergeTag: (attrs: InsertMergeTagAttrs) => ReturnType;\n };\n }\n}\n\nexport const MergeTagNode = Node.create<MergeTagNodeOptions>({\n name: \"mergeTagNode\",\n\n group: \"inline\",\n\n inline: true,\n\n atom: true,\n\n addOptions() {\n return {\n mergeTags: [],\n syntax: SYNTAX_PRESETS.liquid,\n };\n },\n\n addAttributes() {\n return {\n label: {\n default: \"\",\n parseHTML: (element) =>\n element.getAttribute(\"data-label\") || element.textContent || \"\",\n },\n value: {\n default: \"\",\n parseHTML: (element) => element.getAttribute(\"data-merge-tag\") || \"\",\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: \"span[data-merge-tag]\",\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = getMergeTagLabel(node.attrs.value, this.options.mergeTags);\n\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n \"data-merge-tag\": node.attrs.value,\n \"data-label\": label,\n }),\n label,\n ];\n },\n\n addNodeView() {\n return renderVueNodeView(MergeTagNodeView);\n },\n\n addCommands() {\n return {\n insertMergeTag:\n (attrs: InsertMergeTagAttrs) =>\n ({ commands }) => {\n // Whitelist explicitly — even if a caller smuggles extra keys via\n // a wider type assertion, only label/value reach the node attrs.\n return commands.insertContent({\n type: this.name,\n attrs: { label: attrs.label, value: attrs.value },\n });\n },\n };\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => isNodeSelected(this.editor, this.name),\n Delete: () => isNodeSelected(this.editor, this.name),\n };\n },\n\n addInputRules() {\n const inputRegex = new RegExp(this.options.syntax.value.source + \"$\", \"\");\n\n return [\n new InputRule({\n find: inputRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags);\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n\n addPasteRules() {\n const pasteRegex = new RegExp(this.options.syntax.value.source, \"g\");\n\n return [\n new PasteRule({\n find: pasteRegex,\n handler: ({ state, range, match }) => {\n const fullValue = match[0];\n const label = getMergeTagLabel(fullValue, this.options.mergeTags);\n\n const node = this.type.create({\n label,\n value: fullValue,\n });\n\n state.tr.replaceWith(range.from, range.to, node);\n },\n }),\n ];\n },\n});\n"],"mappings":"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"}
|
|
@@ -1564,7 +1564,7 @@ function wr(e) {
|
|
|
1564
1564
|
o || l();
|
|
1565
1565
|
async function l() {
|
|
1566
1566
|
try {
|
|
1567
|
-
let t = await import("./quality-
|
|
1567
|
+
let t = await import("./quality-LmsngN2B.js").then((e) => e.t);
|
|
1568
1568
|
if (c) return;
|
|
1569
1569
|
a.value = { lintTemplate: t.lintTemplate }, r.value = !0, u(), s = fn(e.content, u, {
|
|
1570
1570
|
debounce: e.debounce ?? 500,
|
|
@@ -3570,7 +3570,7 @@ var mo = ["innerHTML"], ho = /* @__PURE__ */ B({
|
|
|
3570
3570
|
viewport: {}
|
|
3571
3571
|
},
|
|
3572
3572
|
setup(e) {
|
|
3573
|
-
let t = e, n = T(() => import("./ParagraphEditor-
|
|
3573
|
+
let t = e, n = T(() => import("./ParagraphEditor-DR5AcHB0.js")), { isEditing: r, blockRef: i, toolbarPosition: a, resolvedContent: o, handleDoubleClick: s, handleEditorDone: c } = po(() => t.block.content);
|
|
3574
3574
|
return (t, l) => (f(), k("div", {
|
|
3575
3575
|
ref_key: "blockRef",
|
|
3576
3576
|
ref: i,
|
|
@@ -3638,7 +3638,7 @@ var yo = ["data-block-id", "data-block-type"], bo = ["aria-label"], xo = [
|
|
|
3638
3638
|
},
|
|
3639
3639
|
emits: ["select"],
|
|
3640
3640
|
setup(e, { emit: t }) {
|
|
3641
|
-
let n = T(() => import("./BlockIssueBadge-
|
|
3641
|
+
let n = T(() => import("./BlockIssueBadge-yD4pKsU9.js")), r = e, i = t, { t: a, format: s } = Y(), c = v(dr, null), l = C(null), u = O(() => c?.liftedBlockId.value === r.block.id), d = O(() => u.value ? s(a.blockActions.dragLifted, { block: r.block.type }) : a.blockActions.drag);
|
|
3642
3642
|
async function p() {
|
|
3643
3643
|
await m(), l.value?.focus();
|
|
3644
3644
|
}
|
|
@@ -3797,7 +3797,7 @@ var No = /* @__PURE__ */ B({
|
|
|
3797
3797
|
viewport: {}
|
|
3798
3798
|
},
|
|
3799
3799
|
setup(e) {
|
|
3800
|
-
let t = e, n = T(() => import("./TitleEditor-
|
|
3800
|
+
let t = e, n = T(() => import("./TitleEditor-DtQTPAg3.js")), { isEditing: r, blockRef: i, toolbarPosition: a, resolvedContent: o, handleDoubleClick: s, handleEditorDone: c } = po(() => t.block.content), l = O(() => {
|
|
3801
3801
|
let e = {
|
|
3802
3802
|
fontSize: `${pe[t.block.level]}px`,
|
|
3803
3803
|
textAlign: t.block.textAlign
|
|
@@ -4296,7 +4296,7 @@ var os = ["src", "alt"], ss = {
|
|
|
4296
4296
|
table: ns,
|
|
4297
4297
|
spacer: Xo,
|
|
4298
4298
|
html: Ra,
|
|
4299
|
-
countdown: T(() => import("./CountdownBlock-
|
|
4299
|
+
countdown: T(() => import("./CountdownBlock-jgBPbI1p.js").then((e) => e.n))
|
|
4300
4300
|
};
|
|
4301
4301
|
function ms(e) {
|
|
4302
4302
|
let { editor: t, config: n, translations: r, fontsManager: i } = e, { t: a, format: o } = Y(r);
|
|
@@ -7464,4 +7464,4 @@ var Iu = {
|
|
|
7464
7464
|
//#endregion
|
|
7465
7465
|
export { ha as $, fe as $n, zn as $t, fs as A, mt as An, fi as At, ho as B, be as Bn, Y as Bt, ws as C, Lt as Cn, Ci as Ct, vs as D, Ct as Dn, hi as Dt, _s as E, xt as En, bi as Et, Lo as F, st as Fn, ii as Ft, Va as G, he as Gn, qn as Gt, io as H, ve as Hn, lr as Ht, No as I, W as In, X as It, Ma as J, Ee as Jn, _r as Jt, za as K, Se as Kn, ur as Kt, jo as L, We as Ln, Kr as Lt, Xo as M, ut as Mn, li as Mt, qo as N, lt as Nn, si as Nt, gs as O, yt as On, mi as Ot, Bo as P, ct as Pn, ai as Pt, _a as Q, de as Qn, cr as Qt, _o as R, ye as Rn, Mr as Rt, Ts as S, un as Sn, wi as St, Z as T, dt as Tn, xi as Tt, ro as U, Te as Un, hr as Ut, ao as V, we as Vn, gr as Vt, $a as W, _e as Wn, Kn as Wt, xa as X, xe as Xn, Jn as Xt, Aa as Y, ge as Yn, Hn as Yt, ya as Z, pe as Zn, Zn as Zt, Ls as _, Nn as _n, Mi as _t, Bl as a, Yn as an, na as at, Ns as b, an as bn, Ei as bt, ec as c, fr as cn, qi as ct, qs as d, Gn as dn, Bi as dt, Un as en, ue as er, da as et, Ks as f, J as fn, Ri as ft, Hs as g, q as gn, Ni as gt, Us as h, Dn as hn, Fi as ht, hu as i, sr as in, ia as it, ns as j, St as jn, di as jt, ms as k, ht as kn, pi as kt, Ys as l, Wn as ln, Ki as lt, Ws as m, En as mn, Ii as mt, pd as n, nr as nn, oa as nt, Ec as o, vr as on, ra as ot, Gs as p, vn as pn, Li as pt, Ra as q, Ce as qn, br as qt, Au as r, $n as rn, aa as rt, tc as s, yr as sn, ea as st, kd as t, ar as tn, ua as tt, Js as u, Rn as un, Hi as ut, Is as v, An as vn, ji as vt, $ as w, Ot as wn, Si as wt, ks as x, ln as xn, Ti as xt, Ps as y, on as yn, ki as yt, go as z, De as zn, Tr as zt };
|
|
7466
7466
|
|
|
7467
|
-
//# sourceMappingURL=features-
|
|
7467
|
+
//# sourceMappingURL=features-DLu_D-6j.js.map
|