@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.
Files changed (86) hide show
  1. package/dist/{AiFeatureMenu-zwSmx9ZW.js → AiFeatureMenu-Cr8pLXMj.js} +1 -1
  2. package/dist/{CloudEditor-5ahgKRX8.js → CloudEditor-qzoNS8K4.js} +4 -4
  3. package/dist/{CollaboratorBar-6PtX-1AS.js → CollaboratorBar-BkfR0CyD.js} +1 -1
  4. package/dist/{MergeTagSuggestion-jIpT_jmz.js → MergeTagSuggestion-DCuaSOPQ.js} +148 -162
  5. package/dist/{ModuleBrowserModal-B5IH3-1I.js → ModuleBrowserModal-XEHfEO0O.js} +4 -4
  6. package/dist/{ModulePreviewCanvas-CYOLWoZ9.js → ModulePreviewCanvas-BM8XUgbc.js} +1 -1
  7. package/dist/{ParagraphEditor-duWYA6Vv.js → ParagraphEditor-Cs47wU1V.js} +13 -13
  8. package/dist/{SaveModuleDialog-BPUly5h3.js → SaveModuleDialog-BgOS9vCQ.js} +2 -2
  9. package/dist/{SnapshotHistory-CmwU3nlN.js → SnapshotHistory-CLfYzwv9.js} +1 -1
  10. package/dist/{TestEmailModal-zfomMz9i.js → TestEmailModal-DTLfATNt.js} +2 -2
  11. package/dist/{TitleEditor-v0gtezZE.js → TitleEditor-Bq9S0C8Z.js} +5 -5
  12. package/dist/{TplModal-eGFG7k-y.js → TplModal-BW0FAtYe.js} +1 -1
  13. package/dist/{blockTypeIcons-Dd0w4T0I.js → blockTypeIcons-CvSfLeyV.js} +1 -1
  14. package/dist/bundle-stats.json +6 -6
  15. package/dist/cdn/chunks/{AiFeatureMenu-CtXsam7G.js → AiFeatureMenu-B0zH66nO.js} +2 -2
  16. package/dist/cdn/chunks/{AiFeatureMenu-CtXsam7G.js.map → AiFeatureMenu-B0zH66nO.js.map} +1 -1
  17. package/dist/cdn/chunks/{BlockIssueBadge-DR7cpDfQ.js → BlockIssueBadge-yD4pKsU9.js} +2 -2
  18. package/dist/cdn/chunks/{BlockIssueBadge-DR7cpDfQ.js.map → BlockIssueBadge-yD4pKsU9.js.map} +1 -1
  19. package/dist/cdn/chunks/{CloudEditor-YC5y3AwC.js → CloudEditor-Da-aSmw5.js} +9 -9
  20. package/dist/cdn/chunks/{CloudEditor-YC5y3AwC.js.map → CloudEditor-Da-aSmw5.js.map} +1 -1
  21. package/dist/cdn/chunks/{CollaboratorBar-BOD2j8Kl.js → CollaboratorBar-DixaFis-.js} +3 -3
  22. package/dist/cdn/chunks/{CollaboratorBar-BOD2j8Kl.js.map → CollaboratorBar-DixaFis-.js.map} +1 -1
  23. package/dist/cdn/chunks/{ColorPicker-CPMWLHpa.js → ColorPicker-O6xXCPEg.js} +2 -2
  24. package/dist/cdn/chunks/{ColorPicker-CPMWLHpa.js.map → ColorPicker-O6xXCPEg.js.map} +1 -1
  25. package/dist/cdn/chunks/{CountdownBlock-BmGqb7Zi.js → CountdownBlock-jgBPbI1p.js} +2 -2
  26. package/dist/cdn/chunks/{CountdownBlock-BmGqb7Zi.js.map → CountdownBlock-jgBPbI1p.js.map} +1 -1
  27. package/dist/cdn/chunks/{CountdownToolbar-BBwsdGbV.js → CountdownToolbar-DrmZwvIH.js} +4 -4
  28. package/dist/cdn/chunks/{CountdownToolbar-BBwsdGbV.js.map → CountdownToolbar-DrmZwvIH.js.map} +1 -1
  29. package/dist/cdn/chunks/{IssuesPanel-G0z-j3JS.js → IssuesPanel-CpyhXg9M.js} +3 -3
  30. package/dist/cdn/chunks/{IssuesPanel-G0z-j3JS.js.map → IssuesPanel-CpyhXg9M.js.map} +1 -1
  31. package/dist/cdn/chunks/{MergeTagSuggestion-rpZ6Ol-i.js → MergeTagSuggestion-BmMmdcRg.js} +2 -2
  32. package/dist/cdn/chunks/{MergeTagSuggestion-rpZ6Ol-i.js.map → MergeTagSuggestion-BmMmdcRg.js.map} +1 -1
  33. package/dist/cdn/chunks/{ModuleBrowserModal-DiLTY0ec.js → ModuleBrowserModal-DPQLFTti.js} +5 -5
  34. package/dist/cdn/chunks/{ModuleBrowserModal-DiLTY0ec.js.map → ModuleBrowserModal-DPQLFTti.js.map} +1 -1
  35. package/dist/cdn/chunks/{ModulePreviewCanvas-CdV5mEQ6.js → ModulePreviewCanvas-Csc6M0RA.js} +2 -2
  36. package/dist/cdn/chunks/{ModulePreviewCanvas-CdV5mEQ6.js.map → ModulePreviewCanvas-Csc6M0RA.js.map} +1 -1
  37. package/dist/cdn/chunks/{NumberWithSuffix-lqQlf0Uk.js → NumberWithSuffix-YmcsBmCd.js} +2 -2
  38. package/dist/cdn/chunks/{NumberWithSuffix-lqQlf0Uk.js.map → NumberWithSuffix-YmcsBmCd.js.map} +1 -1
  39. package/dist/cdn/chunks/{ParagraphEditor-BYoZfURd.js → ParagraphEditor-DR5AcHB0.js} +17 -17
  40. package/dist/cdn/chunks/{ParagraphEditor-BYoZfURd.js.map → ParagraphEditor-DR5AcHB0.js.map} +1 -1
  41. package/dist/cdn/chunks/{RichTextEditorContent-BnPRnXxy.js → RichTextEditorContent-52c2EuCT.js} +3 -3
  42. package/dist/cdn/chunks/{RichTextEditorContent-BnPRnXxy.js.map → RichTextEditorContent-52c2EuCT.js.map} +1 -1
  43. package/dist/cdn/chunks/{SaveModuleDialog-C9hUeWyG.js → SaveModuleDialog-dpRlVrrQ.js} +2 -2
  44. package/dist/cdn/chunks/{SaveModuleDialog-C9hUeWyG.js.map → SaveModuleDialog-dpRlVrrQ.js.map} +1 -1
  45. package/dist/cdn/chunks/{TitleEditor-wFvnU3Jr.js → TitleEditor-DtQTPAg3.js} +8 -8
  46. package/dist/cdn/chunks/{TitleEditor-wFvnU3Jr.js.map → TitleEditor-DtQTPAg3.js.map} +1 -1
  47. package/dist/cdn/chunks/{blockTypeIcons-DNMvDz5B.js → blockTypeIcons-B0ktHdUG.js} +3 -3
  48. package/dist/cdn/chunks/{blockTypeIcons-DNMvDz5B.js.map → blockTypeIcons-B0ktHdUG.js.map} +1 -1
  49. package/dist/cdn/chunks/{extensions-DxcxIlwT.js → extensions-CiewgCtw.js} +4 -4
  50. package/dist/cdn/chunks/{extensions-DxcxIlwT.js.map → extensions-CiewgCtw.js.map} +1 -1
  51. package/dist/cdn/chunks/{features-B3qgDBHf.js → features-DLu_D-6j.js} +6 -6
  52. package/dist/cdn/chunks/{features-B3qgDBHf.js.map → features-DLu_D-6j.js.map} +1 -1
  53. package/dist/cdn/chunks/{icons-kiMqh99T.js → icons-B03IY-pm.js} +2 -2
  54. package/dist/cdn/chunks/{icons-kiMqh99T.js.map → icons-B03IY-pm.js.map} +1 -1
  55. package/dist/cdn/chunks/{media-library-C5ogiZ4f.js → media-library-CYF18RNk.js} +3 -3
  56. package/dist/cdn/chunks/{media-library-C5ogiZ4f.js.map → media-library-CYF18RNk.js.map} +1 -1
  57. package/dist/cdn/chunks/{quality-BftAn_AJ.js → quality-LmsngN2B.js} +2 -2
  58. package/dist/cdn/chunks/{quality-BftAn_AJ.js.map → quality-LmsngN2B.js.map} +1 -1
  59. package/dist/cdn/chunks/{renderer-Cdlo4MVX.js → renderer-DAeNIQqa.js} +4 -4
  60. package/dist/cdn/chunks/renderer-DAeNIQqa.js.map +1 -0
  61. package/dist/cdn/chunks/{src-GBA8oUL2.js → src-CModhUDN.js} +4 -4
  62. package/dist/cdn/chunks/{src-GBA8oUL2.js.map → src-CModhUDN.js.map} +1 -1
  63. package/dist/cdn/chunks/{styles-B6rdq9JB.js → styles-hd_LBGaF.js} +10 -10
  64. package/dist/cdn/chunks/{styles-B6rdq9JB.js.map → styles-hd_LBGaF.js.map} +1 -1
  65. package/dist/cdn/chunks/{tiptap-zpXk5mN4.js → tiptap-C2indasq.js} +806 -811
  66. package/dist/cdn/chunks/tiptap-C2indasq.js.map +1 -0
  67. package/dist/cdn/editor.js +5 -5
  68. package/dist/{dist-BReUTadj.js → dist-8TKf-Jpg.js} +2 -2
  69. package/dist/{dist-BE9m_i60.js → dist-BDVm2MrW.js} +1 -1
  70. package/dist/{dist-D1WmF8BF.js → dist-BKKo5wPC.js} +1 -1
  71. package/dist/{dist-DhY2di4U.js → dist-COSN3oDJ.js} +1 -1
  72. package/dist/{dist-CHLoeloJ.js → dist-CcX5sLpz.js} +1 -1
  73. package/dist/{dist-D9g4CSnm.js → dist-CgTKuIUN.js} +300 -298
  74. package/dist/{dist-i_Z7t67A.js → dist-Cx7IUM7J.js} +22 -15
  75. package/dist/{dist-Bm07Z3Es.js → dist-D6M5FAY7.js} +1 -1
  76. package/dist/{dist-DGEaAAIb.js → dist-DXAZUjj3.js} +1 -1
  77. package/dist/{dist-BXR1aBJV.js → dist-DbekZDmJ.js} +1 -1
  78. package/dist/{dist-DcKlLQ8d.js → dist-Dk5AdwoQ.js} +1 -1
  79. package/dist/{dist-BPPeDw62.js → dist-YSCPD13F.js} +1 -1
  80. package/dist/{extensions-B4lu0KkE.js → extensions-DhgbhfBX.js} +3 -3
  81. package/dist/{styles-mBSbDYeX.js → styles-BWC0jc5-.js} +4 -4
  82. package/dist/templatical-editor.js +3 -3
  83. package/dist/{useEditorCore-CaaHPKjm.js → useEditorCore-FLMIhtv1.js} +2 -2
  84. package/package.json +8 -8
  85. package/dist/cdn/chunks/renderer-Cdlo4MVX.js.map +0 -1
  86. package/dist/cdn/chunks/tiptap-zpXk5mN4.js.map +0 -1
@@ -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-B3qgDBHf.js";
3
- import { B as ae, H as oe, X as se, Z as ce, b as x } from "./icons-kiMqh99T.js";
4
- import { n as S, t as C } from "./RichTextEditorContent-BnPRnXxy.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-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-zpXk5mN4.js").then((e) => e.r),
31
- import("./tiptap-zpXk5mN4.js").then((e) => e.i),
32
- import("./tiptap-zpXk5mN4.js").then((e) => e.l),
33
- import("./extensions-DxcxIlwT.js")
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-wFvnU3Jr.js.map
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-B3qgDBHf.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-kiMqh99T.js";
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-DNMvDz5B.js.map
22
+ //# sourceMappingURL=blockTypeIcons-B0ktHdUG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"blockTypeIcons-DNMvDz5B.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
+ {"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-B3qgDBHf.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-zpXk5mN4.js";
4
- import { i as j, r as M, t as N } from "./MergeTagSuggestion-rpZ6Ol-i.js";
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-DxcxIlwT.js.map
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-BftAn_AJ.js").then((e) => e.t);
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-BYoZfURd.js")), { isEditing: r, blockRef: i, toolbarPosition: a, resolvedContent: o, handleDoubleClick: s, handleEditorDone: c } = po(() => t.block.content);
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-DR7cpDfQ.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);
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-wFvnU3Jr.js")), { isEditing: r, blockRef: i, toolbarPosition: a, resolvedContent: o, handleDoubleClick: s, handleEditorDone: c } = po(() => t.block.content), l = O(() => {
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-BmGqb7Zi.js").then((e) => e.n))
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-B3qgDBHf.js.map
7467
+ //# sourceMappingURL=features-DLu_D-6j.js.map