@templatical/editor 0.33.0 → 0.34.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 (149) hide show
  1. package/dist/{AiChatSidebar-J7WsHRDS.js → AiChatSidebar-CkHJ28sf.js} +3 -3
  2. package/dist/{AiFeatureMenu-C9mxL_7U.js → AiFeatureMenu-DWdKguDN.js} +3 -3
  3. package/dist/{BlockIssueBadge-BI6v_eol.js → BlockIssueBadge-DcyHTAqw.js} +2 -2
  4. package/dist/{BlockPreviewCanvas-mZIr9mY2.js → BlockPreviewCanvas-B621ANn9.js} +4 -4
  5. package/dist/{CloudHeaderExtras-CLtTtK12.js → CloudHeaderExtras-BJ3Cebhv.js} +2 -2
  6. package/dist/{CloudPanels-BbLAX1jH.js → CloudPanels-BGz9ygo6.js} +6 -6
  7. package/dist/{CollaboratorBar-OBbLOhLK.js → CollaboratorBar-KphmSQeZ.js} +2 -2
  8. package/dist/{ColorPicker-Cs7iK01u.js → ColorPicker-C6EOwtHC.js} +5 -5
  9. package/dist/{CommentsPanel-G_uNzi1a.js → CommentsPanel-DmWh8f08.js} +1 -1
  10. package/dist/{CommentsSidebar-DlFlpfkU.js → CommentsSidebar-CL_tffzT.js} +4 -4
  11. package/dist/{CountdownBlock-Bdt8qZi1.js → CountdownBlock-D1fsNsyS.js} +1 -1
  12. package/dist/{CountdownToolbar-BWLYViOm.js → CountdownToolbar-B8J4i5Xp.js} +2 -2
  13. package/dist/{DesignReferenceSidebar-CK_xTLh2.js → DesignReferenceSidebar-BDhEeaAh.js} +4 -4
  14. package/dist/{IssuesPanel-DIbJ_jaa.js → IssuesPanel-CiOjeRFI.js} +4 -4
  15. package/dist/MergeTagInput-RytOlh1A.js +448 -0
  16. package/dist/{MergeTagModeToggle-DeI65T5k.js → MergeTagModeToggle-Bi-FydXj.js} +2 -2
  17. package/dist/{ParagraphEditor-Dr-u72hu.js → ParagraphEditor-C87tkmtE.js} +12 -12
  18. package/dist/{RestoreVersionDialog-6sBYYt9f.js → RestoreVersionDialog-Db5rZRMA.js} +2 -2
  19. package/dist/RichTextEditorContent-BmTcV2-a.js +152 -0
  20. package/dist/{SaveBlockDialog-DABN4-Jf.js → SaveBlockDialog-CDCzx6Bo.js} +16 -16
  21. package/dist/{SavedBlocksBrowserModal-BoylzTEq.js → SavedBlocksBrowserModal-DQR6kJB1.js} +101 -101
  22. package/dist/{SavedBlocksPanels-2np4Yu1H.js → SavedBlocksPanels-DFzV0CRD.js} +1 -1
  23. package/dist/{SavedBlocksPickBar-BM7aTSK2.js → SavedBlocksPickBar-XyXmnSnO.js} +2 -2
  24. package/dist/{TemplateScoringPanel-CzWoSM1V.js → TemplateScoringPanel-CpyEKMzC.js} +4 -4
  25. package/dist/{TestEmailModal-Cb0n7Dhj.js → TestEmailModal-DIfJy9eE.js} +8 -8
  26. package/dist/{TestEmailPanel-52HjvuU7.js → TestEmailPanel-C8Wx5yPS.js} +1 -1
  27. package/dist/{TitleEditor-_taBpBfB.js → TitleEditor-iNFOisr4.js} +11 -11
  28. package/dist/{TplModal-CEYnwBBk.js → TplModal-CowM3o_F.js} +5 -5
  29. package/dist/{VersionHistoryMenu-CsGlLSO8.js → VersionHistoryMenu-CV90OhXs.js} +2 -2
  30. package/dist/{VersionHistoryPanels-DPmYk00L.js → VersionHistoryPanels-BE3pplJG.js} +1 -1
  31. package/dist/{VersionPreviewBanner-VGJbEoXj.js → VersionPreviewBanner-BwUWBtlH.js} +1 -1
  32. package/dist/{blockTypeIcons-IhdBwbxp.js → blockTypeIcons-DQsKtHik.js} +1 -1
  33. package/dist/bundle-stats.json +6 -6
  34. package/dist/cdn/chunks/{AiChatSidebar-6S5LPyRW.js → AiChatSidebar-BEnIWn6s.js} +4 -4
  35. package/dist/cdn/chunks/AiChatSidebar-BEnIWn6s.js.map +1 -0
  36. package/dist/cdn/chunks/{AiFeatureMenu-BHCtjIfV.js → AiFeatureMenu-DMl23akc.js} +4 -4
  37. package/dist/cdn/chunks/{AiFeatureMenu-BHCtjIfV.js.map → AiFeatureMenu-DMl23akc.js.map} +1 -1
  38. package/dist/cdn/chunks/{BlockIssueBadge-2-os120N.js → BlockIssueBadge-CloK4k3M.js} +3 -3
  39. package/dist/cdn/chunks/{BlockIssueBadge-2-os120N.js.map → BlockIssueBadge-CloK4k3M.js.map} +1 -1
  40. package/dist/cdn/chunks/{BlockPreviewCanvas-CYNs-Yn-.js → BlockPreviewCanvas-DSiJFJa0.js} +5 -5
  41. package/dist/cdn/chunks/{BlockPreviewCanvas-CYNs-Yn-.js.map → BlockPreviewCanvas-DSiJFJa0.js.map} +1 -1
  42. package/dist/cdn/chunks/{CloudHeaderExtras-DrbFk6Fy.js → CloudHeaderExtras-b4KpVeUN.js} +3 -3
  43. package/dist/cdn/chunks/{CloudHeaderExtras-DrbFk6Fy.js.map → CloudHeaderExtras-b4KpVeUN.js.map} +1 -1
  44. package/dist/cdn/chunks/{CloudPanels-CpzYrAPQ.js → CloudPanels-DzwOq9lp.js} +7 -7
  45. package/dist/cdn/chunks/CloudPanels-DzwOq9lp.js.map +1 -0
  46. package/dist/cdn/chunks/{CollaboratorBar-VMTCYn_z.js → CollaboratorBar-WXKvpoGK.js} +3 -3
  47. package/dist/cdn/chunks/{CollaboratorBar-VMTCYn_z.js.map → CollaboratorBar-WXKvpoGK.js.map} +1 -1
  48. package/dist/cdn/chunks/{ColorPicker-B0l_UF8G.js → ColorPicker-BubTDiS1.js} +6 -6
  49. package/dist/cdn/chunks/{ColorPicker-B0l_UF8G.js.map → ColorPicker-BubTDiS1.js.map} +1 -1
  50. package/dist/cdn/chunks/{CommentsPanel-CXMMdLrO.js → CommentsPanel-DAA2VqX4.js} +2 -2
  51. package/dist/cdn/chunks/{CommentsPanel-CXMMdLrO.js.map → CommentsPanel-DAA2VqX4.js.map} +1 -1
  52. package/dist/cdn/chunks/{CommentsSidebar-CGkhAJUW.js → CommentsSidebar-Ci0M2ePp.js} +5 -5
  53. package/dist/cdn/chunks/CommentsSidebar-Ci0M2ePp.js.map +1 -0
  54. package/dist/cdn/chunks/{CountdownBlock-CPe1ulzk.js → CountdownBlock-rK055jpy.js} +2 -2
  55. package/dist/cdn/chunks/{CountdownBlock-CPe1ulzk.js.map → CountdownBlock-rK055jpy.js.map} +1 -1
  56. package/dist/cdn/chunks/{CountdownToolbar-D5xyNd5V.js → CountdownToolbar-CzZq26Vp.js} +3 -3
  57. package/dist/cdn/chunks/{CountdownToolbar-D5xyNd5V.js.map → CountdownToolbar-CzZq26Vp.js.map} +1 -1
  58. package/dist/cdn/chunks/{DesignReferenceSidebar-D1NH1WRv.js → DesignReferenceSidebar-C_ehdX5C.js} +5 -5
  59. package/dist/cdn/chunks/DesignReferenceSidebar-C_ehdX5C.js.map +1 -0
  60. package/dist/cdn/chunks/{IssuesPanel-07SBSZVN.js → IssuesPanel-BXAN2_al.js} +5 -5
  61. package/dist/cdn/chunks/{IssuesPanel-07SBSZVN.js.map → IssuesPanel-BXAN2_al.js.map} +1 -1
  62. package/dist/cdn/chunks/MergeTagInput-1vcI9uQH.js +419 -0
  63. package/dist/cdn/chunks/MergeTagInput-1vcI9uQH.js.map +1 -0
  64. package/dist/cdn/chunks/{MergeTagModeToggle-Bg8eO5nc.js → MergeTagModeToggle-B_mUYJo4.js} +3 -3
  65. package/dist/cdn/chunks/{MergeTagModeToggle-Bg8eO5nc.js.map → MergeTagModeToggle-B_mUYJo4.js.map} +1 -1
  66. package/dist/cdn/chunks/{ParagraphEditor-x2LTi6DN.js → ParagraphEditor-DCDHMcmx.js} +10 -10
  67. package/dist/cdn/chunks/ParagraphEditor-DCDHMcmx.js.map +1 -0
  68. package/dist/cdn/chunks/{RestoreVersionDialog--SjTV0_4.js → RestoreVersionDialog-BkSSwf-Q.js} +3 -3
  69. package/dist/cdn/chunks/{RestoreVersionDialog--SjTV0_4.js.map → RestoreVersionDialog-BkSSwf-Q.js.map} +1 -1
  70. package/dist/cdn/chunks/RichTextEditorContent-S4KogjIj.js +122 -0
  71. package/dist/cdn/chunks/RichTextEditorContent-S4KogjIj.js.map +1 -0
  72. package/dist/cdn/chunks/{SaveBlockDialog-CS1lqdsQ.js → SaveBlockDialog-BH774Nt0.js} +14 -14
  73. package/dist/cdn/chunks/{SaveBlockDialog-CS1lqdsQ.js.map → SaveBlockDialog-BH774Nt0.js.map} +1 -1
  74. package/dist/cdn/chunks/{SavedBlocksBrowserModal-D6nDZ4rq.js → SavedBlocksBrowserModal-CSUqw_dD.js} +6 -6
  75. package/dist/cdn/chunks/{SavedBlocksBrowserModal-D6nDZ4rq.js.map → SavedBlocksBrowserModal-CSUqw_dD.js.map} +1 -1
  76. package/dist/cdn/chunks/{SavedBlocksPanels-DdkwvYRR.js → SavedBlocksPanels-B6ApbCjG.js} +2 -2
  77. package/dist/cdn/chunks/{SavedBlocksPanels-DdkwvYRR.js.map → SavedBlocksPanels-B6ApbCjG.js.map} +1 -1
  78. package/dist/cdn/chunks/{SavedBlocksPickBar-BLQr_6WM.js → SavedBlocksPickBar-C9MXSRcR.js} +3 -3
  79. package/dist/cdn/chunks/{SavedBlocksPickBar-BLQr_6WM.js.map → SavedBlocksPickBar-C9MXSRcR.js.map} +1 -1
  80. package/dist/cdn/chunks/{TemplateScoringPanel-CDC-JC6C.js → TemplateScoringPanel-QVOxc9TZ.js} +50 -50
  81. package/dist/cdn/chunks/TemplateScoringPanel-QVOxc9TZ.js.map +1 -0
  82. package/dist/cdn/chunks/{TestEmailModal-B2LcU11Y.js → TestEmailModal-_CZvaO58.js} +9 -9
  83. package/dist/cdn/chunks/{TestEmailModal-B2LcU11Y.js.map → TestEmailModal-_CZvaO58.js.map} +1 -1
  84. package/dist/cdn/chunks/{TestEmailPanel-ByqDSx8B.js → TestEmailPanel-DXLxvyrD.js} +2 -2
  85. package/dist/cdn/chunks/{TestEmailPanel-ByqDSx8B.js.map → TestEmailPanel-DXLxvyrD.js.map} +1 -1
  86. package/dist/cdn/chunks/{TitleEditor-DCoulgN8.js → TitleEditor-BuCOahW3.js} +9 -9
  87. package/dist/cdn/chunks/TitleEditor-BuCOahW3.js.map +1 -0
  88. package/dist/cdn/chunks/{TplModal-CxQDl-tN.js → TplModal-CqY8R3hn.js} +6 -6
  89. package/dist/cdn/chunks/TplModal-CqY8R3hn.js.map +1 -0
  90. package/dist/cdn/chunks/{VersionHistoryMenu-BOy8aMB6.js → VersionHistoryMenu-DDjIBvad.js} +3 -3
  91. package/dist/cdn/chunks/{VersionHistoryMenu-BOy8aMB6.js.map → VersionHistoryMenu-DDjIBvad.js.map} +1 -1
  92. package/dist/cdn/chunks/{VersionHistoryPanels-BPLhzW9c.js → VersionHistoryPanels-BuTw04T5.js} +2 -2
  93. package/dist/cdn/chunks/{VersionHistoryPanels-BPLhzW9c.js.map → VersionHistoryPanels-BuTw04T5.js.map} +1 -1
  94. package/dist/cdn/chunks/{VersionPreviewBanner-CYC9qP2i.js → VersionPreviewBanner-fGAv20qI.js} +2 -2
  95. package/dist/cdn/chunks/{VersionPreviewBanner-CYC9qP2i.js.map → VersionPreviewBanner-fGAv20qI.js.map} +1 -1
  96. package/dist/cdn/chunks/{createCloudRuntime-CbSqoOba.js → createCloudRuntime-JFE-sxbT.js} +2 -2
  97. package/dist/cdn/chunks/{createCloudRuntime-CbSqoOba.js.map → createCloudRuntime-JFE-sxbT.js.map} +1 -1
  98. package/dist/cdn/chunks/{extensions-CeP0iOPC.js → extensions-7z04EIGg.js} +3 -3
  99. package/dist/cdn/chunks/{extensions-CeP0iOPC.js.map → extensions-7z04EIGg.js.map} +1 -1
  100. package/dist/cdn/chunks/{keys-O8clwJ41.js → keys-DQp8x7Nx.js} +4 -4
  101. package/dist/cdn/chunks/{keys-O8clwJ41.js.map → keys-DQp8x7Nx.js.map} +1 -1
  102. package/dist/cdn/chunks/{src-C1lP5sIe.js → src-B7p3PQqm.js} +2 -2
  103. package/dist/cdn/chunks/{src-C1lP5sIe.js.map → src-B7p3PQqm.js.map} +1 -1
  104. package/dist/cdn/chunks/{useCloudI18n-ZBEJvkf4.js → useCloudI18n-MxgHWrV6.js} +2 -2
  105. package/dist/cdn/chunks/{useCloudI18n-ZBEJvkf4.js.map → useCloudI18n-MxgHWrV6.js.map} +1 -1
  106. package/dist/cdn/chunks/{useEditorCore-CZxGbZmY.js → useEditorCore-ehUSEsbw.js} +503 -500
  107. package/dist/cdn/chunks/useEditorCore-ehUSEsbw.js.map +1 -0
  108. package/dist/cdn/chunks/{useEditorRoot-DVV-EsNI.js → useEditorRoot-CteADR3y.js} +2 -2
  109. package/dist/cdn/chunks/{useEditorRoot-DVV-EsNI.js.map → useEditorRoot-CteADR3y.js.map} +1 -1
  110. package/dist/cdn/chunks/{useI18n-DCbvHv6W.js → useI18n-Ct1VIFcf.js} +2 -2
  111. package/dist/cdn/chunks/{useI18n-DCbvHv6W.js.map → useI18n-Ct1VIFcf.js.map} +1 -1
  112. package/dist/cdn/chunks/useMergeTag-TLhF2fGK.js +46 -0
  113. package/dist/cdn/chunks/useMergeTag-TLhF2fGK.js.map +1 -0
  114. package/dist/cdn/chunks/{usePopoverPosition-Ozg5iU3y.js → usePopoverPosition-BxVWE5_0.js} +2 -2
  115. package/dist/cdn/chunks/{usePopoverPosition-Ozg5iU3y.js.map → usePopoverPosition-BxVWE5_0.js.map} +1 -1
  116. package/dist/cdn/chunks/{useScrollToBlock-CrUCYsIc.js → useScrollToBlock-Cz3fpgTK.js} +2 -2
  117. package/dist/cdn/chunks/{useScrollToBlock-CrUCYsIc.js.map → useScrollToBlock-Cz3fpgTK.js.map} +1 -1
  118. package/dist/cdn/editor.css +1 -1
  119. package/dist/cdn/editor.js +1672 -2075
  120. package/dist/cdn/editor.js.map +1 -1
  121. package/dist/{createCloudRuntime-j6a9Z-vL.js → createCloudRuntime-DbTFm1ES.js} +1 -1
  122. package/dist/{extensions-5xeZR9U9.js → extensions-Mf8iHey9.js} +2 -2
  123. package/dist/{keys-BJJ4hJ4z.js → keys-C3P1yxnr.js} +3 -3
  124. package/dist/style.css +1 -1
  125. package/dist/templatical-editor.js +1835 -2239
  126. package/dist/{useCloudI18n-DaKcDMDz.js → useCloudI18n-BKoiYsWZ.js} +1 -1
  127. package/dist/{useEditorCore-BWrxHVKW.js → useEditorCore-DdkQuTHl.js} +1068 -1065
  128. package/dist/{useEditorRoot-E-oxHaEI.js → useEditorRoot-DkRGTQRs.js} +1 -1
  129. package/dist/{useI18n-DUVIAju4.js → useI18n-CL6RfPzK.js} +1 -1
  130. package/dist/useMergeTag-gSuYus-M.js +44 -0
  131. package/dist/{usePopoverPosition-APlWgXoQ.js → usePopoverPosition-CWxaIWd3.js} +1 -1
  132. package/dist/{useScrollToBlock--AzCOzlg.js → useScrollToBlock-CdI8fGqt.js} +1 -1
  133. package/package.json +7 -7
  134. package/dist/RichTextEditorContent-DKLVe6Io.js +0 -153
  135. package/dist/cdn/chunks/AiChatSidebar-6S5LPyRW.js.map +0 -1
  136. package/dist/cdn/chunks/CloudPanels-CpzYrAPQ.js.map +0 -1
  137. package/dist/cdn/chunks/CommentsSidebar-CGkhAJUW.js.map +0 -1
  138. package/dist/cdn/chunks/DesignReferenceSidebar-D1NH1WRv.js.map +0 -1
  139. package/dist/cdn/chunks/ParagraphEditor-x2LTi6DN.js.map +0 -1
  140. package/dist/cdn/chunks/RichTextEditorContent-CzdMJ1c9.js +0 -123
  141. package/dist/cdn/chunks/RichTextEditorContent-CzdMJ1c9.js.map +0 -1
  142. package/dist/cdn/chunks/TemplateScoringPanel-CDC-JC6C.js.map +0 -1
  143. package/dist/cdn/chunks/TitleEditor-DCoulgN8.js.map +0 -1
  144. package/dist/cdn/chunks/TplModal-CxQDl-tN.js.map +0 -1
  145. package/dist/cdn/chunks/useEditorCore-CZxGbZmY.js.map +0 -1
  146. package/dist/cdn/chunks/useMergeTag-CbuagYNK.js +0 -46
  147. package/dist/cdn/chunks/useMergeTag-CbuagYNK.js.map +0 -1
  148. package/dist/scan-line-E7w4yQyv.js +0 -31
  149. package/dist/useMergeTag-CuZZeiSE.js +0 -44
@@ -1 +1 @@
1
- {"version":3,"file":"MergeTagModeToggle-Bg8eO5nc.js","names":[],"sources":["../../../src/components/ViewportToggle.vue","../../../src/components/ViewportToggle.vue","../../../src/components/MergeTagModeToggle.vue","../../../src/components/MergeTagModeToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Switches the previews between the two merge-tag views: **Sample** (tags\n * replaced by their `MergeTag.sample`, rendered as ordinary text) and **Label**\n * (tags shown as their label with the usual cue).\n *\n * Deliberately the same segmented control as `ViewportToggle.vue` — the two sit\n * one click apart on the same preview surfaces, so they have to read as one\n * family rather than two similar things. Kept as a separate component rather\n * than generalising the viewport one: that would mean a generic segmented\n * control with an icon map, and two call sites don't justify the indirection.\n *\n * Renders only where a preview is showing. It must never appear over the\n * editing canvas, because substitution never happens there.\n *\n * **Renders nothing at all unless some configured tag declares a `sample`.**\n * With none, the two views are identical, so the control would be inert. The\n * check lives here rather than at each call site so the guarantee holds for\n * every surface — present and future — without three `v-if`s to keep in step.\n */\nimport { hasMergeTagSamples } from \"@templatical/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { MERGE_TAGS_KEY } from \"../keys\";\nimport { TextCursorInput, Sparkles } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\n\nconst props = defineProps<{\n /** `true` for Sample view, `false` for Label view. */\n sampleMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", sampleMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTags = inject(MERGE_TAGS_KEY, []);\nconst isAvailable = computed(() => hasMergeTagSamples(mergeTags));\n\nconst modes = computed(() => [\n { value: true, label: t.mergeTagPreview.sample },\n { value: false, label: t.mergeTagPreview.labelView },\n]);\n\nconst pillOffset = computed(() => {\n const index = modes.value.findIndex((m) => m.value === props.sampleMode);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n v-if=\"isAvailable\"\n role=\"radiogroup\"\n :aria-label=\"t.mergeTagPreview.label\"\n data-testid=\"merge-tag-mode-toggle\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${modes.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${modes.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"mode in modes\"\n :key=\"String(mode.value)\"\n role=\"radio\"\n :aria-checked=\"sampleMode === mode.value\"\n :aria-label=\"mode.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n sampleMode === mode.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"mode.label\"\n @click=\"emit('change', mode.value)\"\n >\n <Sparkles v-if=\"mode.value\" :size=\"18\" :stroke-width=\"1.5\" />\n <TextCursorInput v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ mode.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Switches the previews between the two merge-tag views: **Sample** (tags\n * replaced by their `MergeTag.sample`, rendered as ordinary text) and **Label**\n * (tags shown as their label with the usual cue).\n *\n * Deliberately the same segmented control as `ViewportToggle.vue` — the two sit\n * one click apart on the same preview surfaces, so they have to read as one\n * family rather than two similar things. Kept as a separate component rather\n * than generalising the viewport one: that would mean a generic segmented\n * control with an icon map, and two call sites don't justify the indirection.\n *\n * Renders only where a preview is showing. It must never appear over the\n * editing canvas, because substitution never happens there.\n *\n * **Renders nothing at all unless some configured tag declares a `sample`.**\n * With none, the two views are identical, so the control would be inert. The\n * check lives here rather than at each call site so the guarantee holds for\n * every surface — present and future — without three `v-if`s to keep in step.\n */\nimport { hasMergeTagSamples } from \"@templatical/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { MERGE_TAGS_KEY } from \"../keys\";\nimport { TextCursorInput, Sparkles } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\n\nconst props = defineProps<{\n /** `true` for Sample view, `false` for Label view. */\n sampleMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", sampleMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTags = inject(MERGE_TAGS_KEY, []);\nconst isAvailable = computed(() => hasMergeTagSamples(mergeTags));\n\nconst modes = computed(() => [\n { value: true, label: t.mergeTagPreview.sample },\n { value: false, label: t.mergeTagPreview.labelView },\n]);\n\nconst pillOffset = computed(() => {\n const index = modes.value.findIndex((m) => m.value === props.sampleMode);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n v-if=\"isAvailable\"\n role=\"radiogroup\"\n :aria-label=\"t.mergeTagPreview.label\"\n data-testid=\"merge-tag-mode-toggle\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${modes.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${modes.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"mode in modes\"\n :key=\"String(mode.value)\"\n role=\"radio\"\n :aria-checked=\"sampleMode === mode.value\"\n :aria-label=\"mode.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n sampleMode === mode.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"mode.label\"\n @click=\"emit('change', mode.value)\"\n >\n <Sparkles v-if=\"mode.value\" :size=\"18\" :stroke-width=\"1.5\" />\n <TextCursorInput v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ mode.label }}</span>\n </button>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"MergeTagModeToggle-B_mUYJo4.js","names":[],"sources":["../../../src/components/ViewportToggle.vue","../../../src/components/ViewportToggle.vue","../../../src/components/MergeTagModeToggle.vue","../../../src/components/MergeTagModeToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Switches the previews between the two merge-tag views: **Sample** (tags\n * replaced by their `MergeTag.sample`, rendered as ordinary text) and **Label**\n * (tags shown as their label with the usual cue).\n *\n * Deliberately the same segmented control as `ViewportToggle.vue` — the two sit\n * one click apart on the same preview surfaces, so they have to read as one\n * family rather than two similar things. Kept as a separate component rather\n * than generalising the viewport one: that would mean a generic segmented\n * control with an icon map, and two call sites don't justify the indirection.\n *\n * Renders only where a preview is showing. It must never appear over the\n * editing canvas, because substitution never happens there.\n *\n * **Renders nothing at all unless some configured tag declares a `sample`.**\n * With none, the two views are identical, so the control would be inert. The\n * check lives here rather than at each call site so the guarantee holds for\n * every surface — present and future — without three `v-if`s to keep in step.\n */\nimport { hasMergeTagSamples } from \"@templatical/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { MERGE_TAGS_KEY } from \"../keys\";\nimport { TextCursorInput, Sparkles } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\n\nconst props = defineProps<{\n /** `true` for Sample view, `false` for Label view. */\n sampleMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", sampleMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTags = inject(MERGE_TAGS_KEY, []);\nconst isAvailable = computed(() => hasMergeTagSamples(mergeTags));\n\nconst modes = computed(() => [\n { value: true, label: t.mergeTagPreview.sample },\n { value: false, label: t.mergeTagPreview.labelView },\n]);\n\nconst pillOffset = computed(() => {\n const index = modes.value.findIndex((m) => m.value === props.sampleMode);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n v-if=\"isAvailable\"\n role=\"radiogroup\"\n :aria-label=\"t.mergeTagPreview.label\"\n data-testid=\"merge-tag-mode-toggle\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${modes.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${modes.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"mode in modes\"\n :key=\"String(mode.value)\"\n role=\"radio\"\n :aria-checked=\"sampleMode === mode.value\"\n :aria-label=\"mode.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n sampleMode === mode.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"mode.label\"\n @click=\"emit('change', mode.value)\"\n >\n <Sparkles v-if=\"mode.value\" :size=\"18\" :stroke-width=\"1.5\" />\n <TextCursorInput v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ mode.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Switches the previews between the two merge-tag views: **Sample** (tags\n * replaced by their `MergeTag.sample`, rendered as ordinary text) and **Label**\n * (tags shown as their label with the usual cue).\n *\n * Deliberately the same segmented control as `ViewportToggle.vue` — the two sit\n * one click apart on the same preview surfaces, so they have to read as one\n * family rather than two similar things. Kept as a separate component rather\n * than generalising the viewport one: that would mean a generic segmented\n * control with an icon map, and two call sites don't justify the indirection.\n *\n * Renders only where a preview is showing. It must never appear over the\n * editing canvas, because substitution never happens there.\n *\n * **Renders nothing at all unless some configured tag declares a `sample`.**\n * With none, the two views are identical, so the control would be inert. The\n * check lives here rather than at each call site so the guarantee holds for\n * every surface — present and future — without three `v-if`s to keep in step.\n */\nimport { hasMergeTagSamples } from \"@templatical/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { MERGE_TAGS_KEY } from \"../keys\";\nimport { TextCursorInput, Sparkles } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\n\nconst props = defineProps<{\n /** `true` for Sample view, `false` for Label view. */\n sampleMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", sampleMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTags = inject(MERGE_TAGS_KEY, []);\nconst isAvailable = computed(() => hasMergeTagSamples(mergeTags));\n\nconst modes = computed(() => [\n { value: true, label: t.mergeTagPreview.sample },\n { value: false, label: t.mergeTagPreview.labelView },\n]);\n\nconst pillOffset = computed(() => {\n const index = modes.value.findIndex((m) => m.value === props.sampleMode);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n v-if=\"isAvailable\"\n role=\"radiogroup\"\n :aria-label=\"t.mergeTagPreview.label\"\n data-testid=\"merge-tag-mode-toggle\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${modes.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${modes.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"mode in modes\"\n :key=\"String(mode.value)\"\n role=\"radio\"\n :aria-checked=\"sampleMode === mode.value\"\n :aria-label=\"mode.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n sampleMode === mode.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"mode.label\"\n @click=\"emit('change', mode.value)\"\n >\n <Sparkles v-if=\"mode.value\" :size=\"18\" :stroke-width=\"1.5\" />\n <TextCursorInput v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ mode.label }}</span>\n </button>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  import { E as e, F as t, N as n, Q as r, R as i, S as a, at as o, b as s, c, ct as l, d as u, f as d, g as f, h as p, l as m, lt as h, m as g, p as _, q as v, u as y, ut as b, y as x } from "./draggable-kbOhCgnk.js";
2
- import { E as S, S as C, T as w, x as T } from "./useEditorCore-CZxGbZmY.js";
2
+ import { E as S, S as C, T as w, x as T } from "./useEditorCore-ehUSEsbw.js";
3
3
  import { t as E } from "./dist-CAFTQ6Y2.js";
4
- import { D, K as O, V as k, W as ee, _ as te, h as ne, w as A } from "./keys-O8clwJ41.js";
5
- import { t as j } from "./useI18n-DCbvHv6W.js";
6
- import { n as M } from "./usePopoverPosition-Ozg5iU3y.js";
4
+ import { J as D, K as O, O as k, U as ee, _ as te, h as ne, w as A } from "./keys-DQp8x7Nx.js";
5
+ import { t as j } from "./useI18n-Ct1VIFcf.js";
6
+ import { n as M } from "./usePopoverPosition-BxVWE5_0.js";
7
7
  import { Ct as N, P as re, R as ie, Rt as ae, S as oe, Vt as se, _ as ce, at as le, ct as ue, g as de, h as fe, it as pe, l as me, m as he, ot as ge, ut as _e, x as ve, y as ye } from "./icons-SP5Itd-A.js";
8
8
  import { t as P } from "./_plugin-vue_export-helper-B3ysoDQm.js";
9
9
  import { a as be, n as xe, o as Se, s as Ce } from "./styleConstants-C_YAPdQS.js";
10
- import { t as F } from "./ColorPicker-B0l_UF8G.js";
11
- import { n as I, t as L } from "./RichTextEditorContent-CzdMJ1c9.js";
10
+ import { t as F } from "./ColorPicker-BubTDiS1.js";
11
+ import { n as I, t as L } from "./RichTextEditorContent-S4KogjIj.js";
12
12
  //#region src/components/blocks/EmojiPickerDropdown.vue?vue&type=script&setup=true&lang.ts
13
13
  var R = [
14
14
  "aria-label",
@@ -157,7 +157,7 @@ var Ee = ["data-tpl-theme", "aria-label"], De = { class: "tpl:flex tpl:items-cen
157
157
  "add-logic-tag"
158
158
  ],
159
159
  setup(t, { emit: r }) {
160
- let i = t, a = r, c = e(k, null), l = e(ee, null), m = O(te, "ParagraphToolbar"), v = M(), S = e(D, null), C = e(A, null), w = e(ne, null), T = d(() => (S?.isOpen.value ?? !1) || (C?.isOpen.value ?? !1)), { t: E } = j(), N = m.fonts;
160
+ let i = t, a = r, c = e(ee, null), l = e(O, null), m = D(te, "ParagraphToolbar"), v = M(), S = e(k, null), C = e(A, null), w = e(ne, null), T = d(() => (S?.isOpen.value ?? !1) || (C?.isOpen.value ?? !1)), { t: E } = j(), N = m.fonts;
161
161
  function P(e) {
162
162
  i.editor?.chain().focus().insertContent(e).run();
163
163
  }
@@ -214,7 +214,7 @@ var Ee = ["data-tpl-theme", "aria-label"], De = { class: "tpl:flex tpl:items-cen
214
214
  "data-tpl-theme": o(l),
215
215
  role: "toolbar",
216
216
  "aria-label": o(E).paragraphEditor.toolbar,
217
- class: "tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex 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-[var(--tpl-shadow-lg)]",
217
+ class: "tpl tpl-text-toolbar tpl:absolute tpl:flex 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-[var(--tpl-shadow-lg)]",
218
218
  style: h({
219
219
  ...o(c),
220
220
  top: `${t.toolbarPosition.top}px`,
@@ -498,7 +498,7 @@ var Ee = ["data-tpl-theme", "aria-label"], De = { class: "tpl:flex tpl:items-cen
498
498
  import("./tiptap-Epe8m3Vo.js").then((e) => e.f),
499
499
  import("./tiptap-Epe8m3Vo.js").then((e) => e.d),
500
500
  import("./tiptap-Epe8m3Vo.js").then((e) => e.u),
501
- import("./extensions-CeP0iOPC.js")
501
+ import("./extensions-7z04EIGg.js")
502
502
  ]);
503
503
  return {
504
504
  TiptapEditor: o,
@@ -609,4 +609,4 @@ var Ee = ["data-tpl-theme", "aria-label"], De = { class: "tpl:flex tpl:items-cen
609
609
  //#endregion
610
610
  export { Ne as default };
611
611
 
612
- //# sourceMappingURL=ParagraphEditor-x2LTi6DN.js.map
612
+ //# sourceMappingURL=ParagraphEditor-DCDHMcmx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParagraphEditor-DCDHMcmx.js","names":[],"sources":["../../../src/components/blocks/EmojiPickerDropdown.vue","../../../src/components/blocks/EmojiPickerDropdown.vue","../../../src/components/toolbar/ToolbarIconButton.vue","../../../src/components/toolbar/ToolbarIconButton.vue","../../../src/components/toolbar/ToolbarSeparator.vue","../../../src/components/toolbar/ToolbarSelect.vue","../../../src/components/toolbar/ToolbarSelect.vue","../../../src/utils/richTextColor.ts","../../../src/components/blocks/ParagraphToolbar.vue","../../../src/components/blocks/ParagraphToolbar.vue","../../../src/components/blocks/ParagraphEditor.vue","../../../src/components/blocks/ParagraphEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useEmoji, useI18n } from \"../../composables\";\nimport { useFocusTrap } from \"../../composables/useFocusTrap\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { Smile } from \"@lucide/vue\";\nimport { computed, ref } from \"vue\";\n\nconst emit = defineEmits<{\n (e: \"insert\", emoji: string): void;\n}>();\n\nconst {\n categories: emojiCategories,\n isOpen: showEmojiPicker,\n toggle: toggleEmojiPicker,\n close: closeEmojiPicker,\n} = useEmoji();\n\nconst { t, format } = useI18n();\n\nconst pickerRef = ref<HTMLElement | null>(null);\nconst rootRef = ref<HTMLElement | null>(null);\n\nconst isOpenRef = computed(() => showEmojiPicker.value);\nuseFocusTrap(pickerRef, isOpenRef);\n\nonClickOutside(rootRef, () => {\n if (showEmojiPicker.value) {\n closeEmojiPicker();\n }\n});\n\nfunction handleInsert(emoji: string): void {\n emit(\"insert\", emoji);\n closeEmojiPicker();\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"tpl:relative\">\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': showEmojiPicker,\n }\"\n :aria-label=\"t.paragraphEditor.insertEmoji\"\n :title=\"t.paragraphEditor.insertEmoji\"\n :aria-expanded=\"showEmojiPicker\"\n aria-haspopup=\"dialog\"\n aria-controls=\"tpl-emoji-picker\"\n @click=\"toggleEmojiPicker\"\n >\n <Smile :size=\"16\" :stroke-width=\"2\" />\n </button>\n <div\n v-if=\"showEmojiPicker\"\n id=\"tpl-emoji-picker\"\n ref=\"pickerRef\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-label=\"t.paragraphEditor.insertEmoji\"\n tabindex=\"-1\"\n class=\"tpl-emoji-picker tpl:absolute tpl:top-full tpl:left-0 tpl:z-10 tpl:mt-2 tpl:w-72 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:shadow-[var(--tpl-shadow-lg)]\"\n @keydown.esc.stop.prevent=\"closeEmojiPicker\"\n >\n <div\n v-for=\"category in emojiCategories\"\n :key=\"category.key\"\n class=\"tpl:mb-2 tpl:last:mb-0\"\n >\n <div\n class=\"tpl:mb-1.5 tpl:text-[10px] tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >\n {{ t.emoji[category.key] }}\n </div>\n <div class=\"tpl:grid tpl:grid-cols-10 tpl:gap-0.5\">\n <button\n v-for=\"emoji in category.emojis\"\n :key=\"emoji\"\n type=\"button\"\n :aria-label=\"format(t.paragraphEditor.emojiItemLabel, { emoji })\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border-none tpl:bg-transparent tpl:text-base tpl:transition-all tpl:hover:scale-125 tpl:hover:bg-[var(--tpl-bg-active)]\"\n @click=\"handleInsert(emoji)\"\n >\n {{ emoji }}\n </button>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useEmoji, useI18n } from \"../../composables\";\nimport { useFocusTrap } from \"../../composables/useFocusTrap\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { Smile } from \"@lucide/vue\";\nimport { computed, ref } from \"vue\";\n\nconst emit = defineEmits<{\n (e: \"insert\", emoji: string): void;\n}>();\n\nconst {\n categories: emojiCategories,\n isOpen: showEmojiPicker,\n toggle: toggleEmojiPicker,\n close: closeEmojiPicker,\n} = useEmoji();\n\nconst { t, format } = useI18n();\n\nconst pickerRef = ref<HTMLElement | null>(null);\nconst rootRef = ref<HTMLElement | null>(null);\n\nconst isOpenRef = computed(() => showEmojiPicker.value);\nuseFocusTrap(pickerRef, isOpenRef);\n\nonClickOutside(rootRef, () => {\n if (showEmojiPicker.value) {\n closeEmojiPicker();\n }\n});\n\nfunction handleInsert(emoji: string): void {\n emit(\"insert\", emoji);\n closeEmojiPicker();\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"tpl:relative\">\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': showEmojiPicker,\n }\"\n :aria-label=\"t.paragraphEditor.insertEmoji\"\n :title=\"t.paragraphEditor.insertEmoji\"\n :aria-expanded=\"showEmojiPicker\"\n aria-haspopup=\"dialog\"\n aria-controls=\"tpl-emoji-picker\"\n @click=\"toggleEmojiPicker\"\n >\n <Smile :size=\"16\" :stroke-width=\"2\" />\n </button>\n <div\n v-if=\"showEmojiPicker\"\n id=\"tpl-emoji-picker\"\n ref=\"pickerRef\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-label=\"t.paragraphEditor.insertEmoji\"\n tabindex=\"-1\"\n class=\"tpl-emoji-picker tpl:absolute tpl:top-full tpl:left-0 tpl:z-10 tpl:mt-2 tpl:w-72 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:shadow-[var(--tpl-shadow-lg)]\"\n @keydown.esc.stop.prevent=\"closeEmojiPicker\"\n >\n <div\n v-for=\"category in emojiCategories\"\n :key=\"category.key\"\n class=\"tpl:mb-2 tpl:last:mb-0\"\n >\n <div\n class=\"tpl:mb-1.5 tpl:text-[10px] tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >\n {{ t.emoji[category.key] }}\n </div>\n <div class=\"tpl:grid tpl:grid-cols-10 tpl:gap-0.5\">\n <button\n v-for=\"emoji in category.emojis\"\n :key=\"emoji\"\n type=\"button\"\n :aria-label=\"format(t.paragraphEditor.emojiItemLabel, { emoji })\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border-none tpl:bg-transparent tpl:text-base tpl:transition-all tpl:hover:scale-125 tpl:hover:bg-[var(--tpl-bg-active)]\"\n @click=\"handleInsert(emoji)\"\n >\n {{ emoji }}\n </button>\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\n\ndefineProps<{\n icon: Component;\n label: string;\n active?: boolean;\n strokeWidth?: number;\n size?: number;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{ 'tpl-text-toolbar-btn--active': active }\"\n :aria-label=\"label\"\n :title=\"label\"\n :aria-pressed=\"active ? 'true' : 'false'\"\n >\n <component :is=\"icon\" :size=\"size ?? 16\" :stroke-width=\"strokeWidth ?? 2\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\n\ndefineProps<{\n icon: Component;\n label: string;\n active?: boolean;\n strokeWidth?: number;\n size?: number;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{ 'tpl-text-toolbar-btn--active': active }\"\n :aria-label=\"label\"\n :title=\"label\"\n :aria-pressed=\"active ? 'true' : 'false'\"\n >\n <component :is=\"icon\" :size=\"size ?? 16\" :stroke-width=\"strokeWidth ?? 2\" />\n </button>\n</template>\n","<template>\n <span\n class=\"tpl:mx-1 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n</template>\n","<script setup lang=\"ts\">\ninterface OptionItem {\n value: string;\n label: string;\n}\n\ndefineProps<{\n modelValue: string;\n options: readonly (string | OptionItem)[];\n label: string;\n placeholder?: string;\n widthClass?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nfunction onChange(e: Event): void {\n emit(\"update:modelValue\", (e.target as HTMLSelectElement).value);\n}\n\nfunction optionValue(opt: string | OptionItem): string {\n return typeof opt === \"string\" ? opt : opt.value;\n}\n\nfunction optionLabel(opt: string | OptionItem): string {\n return typeof opt === \"string\" ? opt : opt.label;\n}\n</script>\n\n<template>\n <select\n :class=\"[\n 'tpl:h-8 tpl:cursor-pointer tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text)] tpl:outline-none',\n widthClass ?? 'tpl:w-20',\n ]\"\n :value=\"modelValue\"\n :aria-label=\"label\"\n :title=\"label\"\n @change=\"onChange\"\n >\n <option value=\"\">{{ placeholder ?? \"\" }}</option>\n <option\n v-for=\"opt in options\"\n :key=\"optionValue(opt)\"\n :value=\"optionValue(opt)\"\n >\n {{ optionLabel(opt) }}\n </option>\n </select>\n</template>\n","<script setup lang=\"ts\">\ninterface OptionItem {\n value: string;\n label: string;\n}\n\ndefineProps<{\n modelValue: string;\n options: readonly (string | OptionItem)[];\n label: string;\n placeholder?: string;\n widthClass?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nfunction onChange(e: Event): void {\n emit(\"update:modelValue\", (e.target as HTMLSelectElement).value);\n}\n\nfunction optionValue(opt: string | OptionItem): string {\n return typeof opt === \"string\" ? opt : opt.value;\n}\n\nfunction optionLabel(opt: string | OptionItem): string {\n return typeof opt === \"string\" ? opt : opt.label;\n}\n</script>\n\n<template>\n <select\n :class=\"[\n 'tpl:h-8 tpl:cursor-pointer tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text)] tpl:outline-none',\n widthClass ?? 'tpl:w-20',\n ]\"\n :value=\"modelValue\"\n :aria-label=\"label\"\n :title=\"label\"\n @change=\"onChange\"\n >\n <option value=\"\">{{ placeholder ?? \"\" }}</option>\n <option\n v-for=\"opt in options\"\n :key=\"optionValue(opt)\"\n :value=\"optionValue(opt)\"\n >\n {{ optionLabel(opt) }}\n </option>\n </select>\n</template>\n","import { DEFAULT_TEXT_COLOR } from \"../constants/styleConstants\";\n\n/**\n * The color a rich-text selection actually renders as, for display in the\n * text-color control (issue #373).\n *\n * Priority: an explicit inline text-color mark on the selection wins; otherwise\n * the text inherits the document-level `textColor`; if that too is unset (older\n * content predating document text color), fall back to the built-in default.\n *\n * A native `<input type=\"color\">` can't represent \"no color\", so the control\n * must be handed a concrete value. Resolving the effective color is what keeps\n * that value truthful — the swatch shows the color the text actually renders in,\n * whatever level it comes from. Defaulting to a hard-coded `#000000` instead\n * would both read as an explicit choice and not even match the real inherited\n * color (`#1a1a1a`).\n */\nexport function resolveEffectiveTextColor(\n explicitColor: string,\n documentTextColor: string | undefined,\n): string {\n return explicitColor || documentTextColor || DEFAULT_TEXT_COLOR;\n}\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport EmojiPickerDropdown from \"./EmojiPickerDropdown.vue\";\nimport ToolbarIconButton from \"../toolbar/ToolbarIconButton.vue\";\nimport ToolbarSeparator from \"../toolbar/ToolbarSeparator.vue\";\nimport ToolbarSelect from \"../toolbar/ToolbarSelect.vue\";\nimport { useI18n } from \"../../composables\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { Editor } from \"@tiptap/core\";\nimport {\n AlignCenter,\n AlignJustify,\n AlignLeft,\n AlignRight,\n Bold,\n Italic,\n Braces,\n Link,\n List,\n ListOrdered,\n LoaderCircle,\n RemoveFormatting,\n ScanLine,\n Strikethrough,\n Subscript,\n Superscript,\n Underline,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n EDITOR_KEY,\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n FONTS_MANAGER_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n requireInject,\n} from \"../../keys\";\nimport {\n DEFAULT_HIGHLIGHT_COLOR,\n FONT_SIZE_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n LETTER_SPACING_OPTIONS,\n} from \"../../constants/styleConstants\";\nimport { resolveEffectiveTextColor } from \"../../utils/richTextColor\";\n\nconst props = defineProps<{\n editor: Editor | null;\n toolbarPosition: { top: number; left: number };\n isLoading: boolean;\n canRequestMergeTag: boolean;\n canInsertLogicTag: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"open-link-dialog\"): void;\n (e: \"add-merge-tag\"): void;\n (e: \"add-logic-tag\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"ParagraphToolbar\");\nconst popoverRoot = usePopoverRoot();\n// Picker may be null in non-editor contexts (e.g. isolated component tests).\n// When it's provided AND open, hide the floating toolbar — leaving it\n// visible behind the modal is visually noisy and the toolbar's Tailwind\n// z-index utility doesn't compile reliably, so it can sometimes paint\n// over the modal backdrop.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\n// Editor may be absent in isolated tests; used only to resolve the inherited\n// document text color for the text-color swatch.\nconst editorReturn = inject(EDITOR_KEY, null);\n// Hide the floating toolbar while either built-in picker modal is open, so it\n// doesn't overlap the dialog.\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst fontFamilies = fontsManager.fonts;\n\nfunction insertEmoji(emoji: string): void {\n props.editor?.chain().focus().insertContent(emoji).run();\n}\n\nfunction textStyleAttr(attr: string): string {\n return (props.editor?.getAttributes(\"textStyle\")[attr] as string) || \"\";\n}\n\nfunction setFontFamily(family: string): void {\n const chain = props.editor?.chain().focus();\n if (family) chain?.setFontFamily(family).run();\n else chain?.unsetFontFamily().run();\n}\n\nfunction setFontSize(size: string): void {\n const chain = props.editor?.chain().focus();\n if (size) chain?.setFontSize(size).run();\n else chain?.unsetFontSize().run();\n}\n\n// When the selection is a link, the color control operates on the link itself\n// (its `color` attribute → inline on the `<a>`) so the link's text and underline\n// stay in sync; otherwise it applies a plain inline text-color mark.\nfunction isLinkSelection(): boolean {\n return props.editor?.isActive(\"link\") ?? false;\n}\n\nfunction linkColorAttr(): string {\n return (props.editor?.getAttributes(\"link\").color as string) || \"\";\n}\n\n// Set (or clear, when empty) the color of the current selection. On a link the\n// whole link is recolored (extendMarkRange) so it doesn't split into segments.\nfunction setColor(color: string): void {\n const chain = props.editor?.chain().focus();\n if (!chain) return;\n if (isLinkSelection()) {\n chain.extendMarkRange(\"link\").updateAttributes(\"link\", {\n color: color || null,\n });\n // A per-link color takes absolute priority over any inner text-color: strip\n // the inline text color on the link range so the `<a>` color alone drives\n // the glyphs and the underline (mirrors useRichTextLinkDialog.insertLink).\n // Only when setting a color — clearing leaves the text color untouched.\n if (color) chain.unsetColor();\n chain.run();\n } else if (color) {\n // A text color applied over a selection that includes a link unifies the\n // link with it: update the link's own color so the `<a>` (which paints the\n // underline and is what the dialog/toolbar read) matches the recolored\n // glyphs. A no-op on a selection with no link. Mirror of the link branch\n // above — either way a link never ends up with a text color fighting its\n // own color.\n chain.setColor(color).updateAttributes(\"link\", { color }).run();\n } else {\n chain.unsetColor().run();\n }\n}\n\n// The color the current selection actually renders in — a link's own color (else\n// the effective document link color: link color, then text color), or for plain\n// text the inline mark else the document text color. Keeps the swatch truthful\n// rather than showing a hard-coded default (issue #373 / per-link color).\nfunction effectiveTextColor(): string {\n const settings = editorReturn?.content.value.settings;\n if (isLinkSelection()) {\n return resolveEffectiveTextColor(\n linkColorAttr(),\n settings?.linkColor || settings?.textColor,\n );\n }\n return resolveEffectiveTextColor(textStyleAttr(\"color\"), settings?.textColor);\n}\n\n// The selection's own explicit color: a link's color when on a link, else the\n// inline text-color mark. May be \"\" when the selection only inherits its color\n// — that drives the swatch's set/unset state, while `effectiveTextColor()`\n// seeds the wheel so it still opens on the real rendered color.\nfunction explicitTextColor(): string {\n return isLinkSelection() ? linkColorAttr() : textStyleAttr(\"color\");\n}\n\nfunction getCurrentLineHeight(): string {\n return (props.editor?.getAttributes(\"paragraph\").lineHeight as string) || \"\";\n}\n\nfunction setLineHeight(value: string): void {\n const chain = props.editor?.chain().focus();\n if (value) chain?.setLineHeight(value).run();\n else chain?.unsetLineHeight().run();\n}\n\nfunction setLetterSpacing(value: string): void {\n const chain = props.editor?.chain().focus();\n if (value && value !== \"normal\") chain?.setLetterSpacing(value).run();\n else chain?.unsetLetterSpacing().run();\n}\n\nfunction getCurrentHighlight(): string {\n return (props.editor?.getAttributes(\"highlight\").color as string) || \"\";\n}\n\nfunction setHighlight(color: string): void {\n const chain = props.editor?.chain().focus();\n if (color) chain?.setHighlight({ color }).run();\n else chain?.unsetHighlight().run();\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.paragraphEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:flex 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-[var(--tpl-shadow-lg)]\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n flexDirection: 'column',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Row 1: Font family, Font size, Text color, Bold/Italic/Underline/Strikethrough -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <ToolbarSelect\n :model-value=\"textStyleAttr('fontFamily')\"\n :options=\"fontFamilies\"\n :label=\"t.paragraphEditor.fontFamily\"\n :placeholder=\"t.paragraphEditor.defaultFont\"\n width-class=\"tpl:w-32\"\n @update:model-value=\"setFontFamily\"\n />\n <ToolbarSelect\n :model-value=\"textStyleAttr('fontSize')\"\n :options=\"FONT_SIZE_OPTIONS\"\n :label=\"t.paragraphEditor.fontSize\"\n :placeholder=\"t.paragraphEditor.defaultSize\"\n width-class=\"tpl:w-20\"\n @update:model-value=\"setFontSize\"\n />\n <ToolbarSeparator />\n <ColorPicker\n swatch-only\n size=\"sm\"\n data-testid=\"text-color-picker\"\n :model-value=\"explicitTextColor()\"\n :seed-color=\"effectiveTextColor()\"\n :aria-label=\"t.paragraphEditor.textColor\"\n @update:model-value=\"setColor\"\n />\n <ColorPicker\n swatch-only\n size=\"sm\"\n data-testid=\"highlight-color-picker\"\n :model-value=\"getCurrentHighlight()\"\n :seed-color=\"DEFAULT_HIGHLIGHT_COLOR\"\n :aria-label=\"t.paragraphEditor.highlightColor\"\n @update:model-value=\"setHighlight\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Bold\"\n :label=\"t.paragraphEditor.bold\"\n :active=\"editor.isActive('bold')\"\n :stroke-width=\"2.5\"\n @click=\"editor.chain().focus().toggleBold().run()\"\n />\n <ToolbarIconButton\n :icon=\"Italic\"\n :label=\"t.paragraphEditor.italic\"\n :active=\"editor.isActive('italic')\"\n @click=\"editor.chain().focus().toggleItalic().run()\"\n />\n <ToolbarIconButton\n :icon=\"Underline\"\n :label=\"t.paragraphEditor.underline\"\n :active=\"editor.isActive('underline')\"\n @click=\"editor.chain().focus().toggleUnderline().run()\"\n />\n <ToolbarIconButton\n :icon=\"Strikethrough\"\n :label=\"t.paragraphEditor.strikethrough\"\n :active=\"editor.isActive('strike')\"\n @click=\"editor.chain().focus().toggleStrike().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Subscript\"\n :label=\"t.paragraphEditor.subscript\"\n :active=\"editor.isActive('subscript')\"\n @click=\"editor.chain().focus().toggleSubscript().run()\"\n />\n <ToolbarIconButton\n :icon=\"Superscript\"\n :label=\"t.paragraphEditor.superscript\"\n :active=\"editor.isActive('superscript')\"\n @click=\"editor.chain().focus().toggleSuperscript().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Link\"\n :label=\"t.paragraphEditor.addLink\"\n :active=\"editor.isActive('link')\"\n @click=\"emit('open-link-dialog')\"\n />\n </div>\n <!-- Row 2: Lists, Alignment, LH, LS, Clear, Emoji, Merge tags -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <ToolbarIconButton\n :icon=\"List\"\n :label=\"t.paragraphEditor.bulletList\"\n :active=\"editor.isActive('bulletList')\"\n @click=\"editor.chain().focus().toggleBulletList().run()\"\n />\n <ToolbarIconButton\n :icon=\"ListOrdered\"\n :label=\"t.paragraphEditor.numberedList\"\n :active=\"editor.isActive('orderedList')\"\n @click=\"editor.chain().focus().toggleOrderedList().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"AlignLeft\"\n :label=\"t.paragraphEditor.alignLeft\"\n :active=\"editor.isActive({ textAlign: 'left' })\"\n @click=\"editor.chain().focus().setTextAlign('left').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignCenter\"\n :label=\"t.paragraphEditor.alignCenter\"\n :active=\"editor.isActive({ textAlign: 'center' })\"\n @click=\"editor.chain().focus().setTextAlign('center').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignRight\"\n :label=\"t.paragraphEditor.alignRight\"\n :active=\"editor.isActive({ textAlign: 'right' })\"\n @click=\"editor.chain().focus().setTextAlign('right').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignJustify\"\n :label=\"t.paragraphEditor.alignJustify\"\n :active=\"editor.isActive({ textAlign: 'justify' })\"\n @click=\"editor.chain().focus().setTextAlign('justify').run()\"\n />\n <ToolbarSeparator />\n <ToolbarSelect\n :model-value=\"getCurrentLineHeight()\"\n :options=\"LINE_HEIGHT_OPTIONS\"\n :label=\"t.paragraphEditor.lineHeight\"\n placeholder=\"LH\"\n width-class=\"tpl:w-16\"\n @update:model-value=\"setLineHeight\"\n />\n <ToolbarSelect\n :model-value=\"textStyleAttr('letterSpacing')\"\n :options=\"LETTER_SPACING_OPTIONS\"\n :label=\"t.paragraphEditor.letterSpacing\"\n placeholder=\"LS\"\n width-class=\"tpl:w-20\"\n @update:model-value=\"setLetterSpacing\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"RemoveFormatting\"\n :label=\"t.paragraphEditor.clearFormatting\"\n @click=\"editor.chain().focus().clearNodes().unsetAllMarks().run()\"\n />\n <ToolbarSeparator />\n <EmojiPickerDropdown @insert=\"insertEmoji\" />\n <template v-if=\"canRequestMergeTag\">\n <ToolbarSeparator />\n <button\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:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"emit('add-merge-tag')\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n </template>\n <template v-if=\"canInsertLogicTag\">\n <ToolbarSeparator />\n <button\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: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=\"emit('add-logic-tag')\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n </div>\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</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport EmojiPickerDropdown from \"./EmojiPickerDropdown.vue\";\nimport ToolbarIconButton from \"../toolbar/ToolbarIconButton.vue\";\nimport ToolbarSeparator from \"../toolbar/ToolbarSeparator.vue\";\nimport ToolbarSelect from \"../toolbar/ToolbarSelect.vue\";\nimport { useI18n } from \"../../composables\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { Editor } from \"@tiptap/core\";\nimport {\n AlignCenter,\n AlignJustify,\n AlignLeft,\n AlignRight,\n Bold,\n Italic,\n Braces,\n Link,\n List,\n ListOrdered,\n LoaderCircle,\n RemoveFormatting,\n ScanLine,\n Strikethrough,\n Subscript,\n Superscript,\n Underline,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n EDITOR_KEY,\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n FONTS_MANAGER_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n requireInject,\n} from \"../../keys\";\nimport {\n DEFAULT_HIGHLIGHT_COLOR,\n FONT_SIZE_OPTIONS,\n LINE_HEIGHT_OPTIONS,\n LETTER_SPACING_OPTIONS,\n} from \"../../constants/styleConstants\";\nimport { resolveEffectiveTextColor } from \"../../utils/richTextColor\";\n\nconst props = defineProps<{\n editor: Editor | null;\n toolbarPosition: { top: number; left: number };\n isLoading: boolean;\n canRequestMergeTag: boolean;\n canInsertLogicTag: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"open-link-dialog\"): void;\n (e: \"add-merge-tag\"): void;\n (e: \"add-logic-tag\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"ParagraphToolbar\");\nconst popoverRoot = usePopoverRoot();\n// Picker may be null in non-editor contexts (e.g. isolated component tests).\n// When it's provided AND open, hide the floating toolbar — leaving it\n// visible behind the modal is visually noisy and the toolbar's Tailwind\n// z-index utility doesn't compile reliably, so it can sometimes paint\n// over the modal backdrop.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\n// Editor may be absent in isolated tests; used only to resolve the inherited\n// document text color for the text-color swatch.\nconst editorReturn = inject(EDITOR_KEY, null);\n// Hide the floating toolbar while either built-in picker modal is open, so it\n// doesn't overlap the dialog.\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst fontFamilies = fontsManager.fonts;\n\nfunction insertEmoji(emoji: string): void {\n props.editor?.chain().focus().insertContent(emoji).run();\n}\n\nfunction textStyleAttr(attr: string): string {\n return (props.editor?.getAttributes(\"textStyle\")[attr] as string) || \"\";\n}\n\nfunction setFontFamily(family: string): void {\n const chain = props.editor?.chain().focus();\n if (family) chain?.setFontFamily(family).run();\n else chain?.unsetFontFamily().run();\n}\n\nfunction setFontSize(size: string): void {\n const chain = props.editor?.chain().focus();\n if (size) chain?.setFontSize(size).run();\n else chain?.unsetFontSize().run();\n}\n\n// When the selection is a link, the color control operates on the link itself\n// (its `color` attribute → inline on the `<a>`) so the link's text and underline\n// stay in sync; otherwise it applies a plain inline text-color mark.\nfunction isLinkSelection(): boolean {\n return props.editor?.isActive(\"link\") ?? false;\n}\n\nfunction linkColorAttr(): string {\n return (props.editor?.getAttributes(\"link\").color as string) || \"\";\n}\n\n// Set (or clear, when empty) the color of the current selection. On a link the\n// whole link is recolored (extendMarkRange) so it doesn't split into segments.\nfunction setColor(color: string): void {\n const chain = props.editor?.chain().focus();\n if (!chain) return;\n if (isLinkSelection()) {\n chain.extendMarkRange(\"link\").updateAttributes(\"link\", {\n color: color || null,\n });\n // A per-link color takes absolute priority over any inner text-color: strip\n // the inline text color on the link range so the `<a>` color alone drives\n // the glyphs and the underline (mirrors useRichTextLinkDialog.insertLink).\n // Only when setting a color — clearing leaves the text color untouched.\n if (color) chain.unsetColor();\n chain.run();\n } else if (color) {\n // A text color applied over a selection that includes a link unifies the\n // link with it: update the link's own color so the `<a>` (which paints the\n // underline and is what the dialog/toolbar read) matches the recolored\n // glyphs. A no-op on a selection with no link. Mirror of the link branch\n // above — either way a link never ends up with a text color fighting its\n // own color.\n chain.setColor(color).updateAttributes(\"link\", { color }).run();\n } else {\n chain.unsetColor().run();\n }\n}\n\n// The color the current selection actually renders in — a link's own color (else\n// the effective document link color: link color, then text color), or for plain\n// text the inline mark else the document text color. Keeps the swatch truthful\n// rather than showing a hard-coded default (issue #373 / per-link color).\nfunction effectiveTextColor(): string {\n const settings = editorReturn?.content.value.settings;\n if (isLinkSelection()) {\n return resolveEffectiveTextColor(\n linkColorAttr(),\n settings?.linkColor || settings?.textColor,\n );\n }\n return resolveEffectiveTextColor(textStyleAttr(\"color\"), settings?.textColor);\n}\n\n// The selection's own explicit color: a link's color when on a link, else the\n// inline text-color mark. May be \"\" when the selection only inherits its color\n// — that drives the swatch's set/unset state, while `effectiveTextColor()`\n// seeds the wheel so it still opens on the real rendered color.\nfunction explicitTextColor(): string {\n return isLinkSelection() ? linkColorAttr() : textStyleAttr(\"color\");\n}\n\nfunction getCurrentLineHeight(): string {\n return (props.editor?.getAttributes(\"paragraph\").lineHeight as string) || \"\";\n}\n\nfunction setLineHeight(value: string): void {\n const chain = props.editor?.chain().focus();\n if (value) chain?.setLineHeight(value).run();\n else chain?.unsetLineHeight().run();\n}\n\nfunction setLetterSpacing(value: string): void {\n const chain = props.editor?.chain().focus();\n if (value && value !== \"normal\") chain?.setLetterSpacing(value).run();\n else chain?.unsetLetterSpacing().run();\n}\n\nfunction getCurrentHighlight(): string {\n return (props.editor?.getAttributes(\"highlight\").color as string) || \"\";\n}\n\nfunction setHighlight(color: string): void {\n const chain = props.editor?.chain().focus();\n if (color) chain?.setHighlight({ color }).run();\n else chain?.unsetHighlight().run();\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.paragraphEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:flex 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-[var(--tpl-shadow-lg)]\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n flexDirection: 'column',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Row 1: Font family, Font size, Text color, Bold/Italic/Underline/Strikethrough -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <ToolbarSelect\n :model-value=\"textStyleAttr('fontFamily')\"\n :options=\"fontFamilies\"\n :label=\"t.paragraphEditor.fontFamily\"\n :placeholder=\"t.paragraphEditor.defaultFont\"\n width-class=\"tpl:w-32\"\n @update:model-value=\"setFontFamily\"\n />\n <ToolbarSelect\n :model-value=\"textStyleAttr('fontSize')\"\n :options=\"FONT_SIZE_OPTIONS\"\n :label=\"t.paragraphEditor.fontSize\"\n :placeholder=\"t.paragraphEditor.defaultSize\"\n width-class=\"tpl:w-20\"\n @update:model-value=\"setFontSize\"\n />\n <ToolbarSeparator />\n <ColorPicker\n swatch-only\n size=\"sm\"\n data-testid=\"text-color-picker\"\n :model-value=\"explicitTextColor()\"\n :seed-color=\"effectiveTextColor()\"\n :aria-label=\"t.paragraphEditor.textColor\"\n @update:model-value=\"setColor\"\n />\n <ColorPicker\n swatch-only\n size=\"sm\"\n data-testid=\"highlight-color-picker\"\n :model-value=\"getCurrentHighlight()\"\n :seed-color=\"DEFAULT_HIGHLIGHT_COLOR\"\n :aria-label=\"t.paragraphEditor.highlightColor\"\n @update:model-value=\"setHighlight\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Bold\"\n :label=\"t.paragraphEditor.bold\"\n :active=\"editor.isActive('bold')\"\n :stroke-width=\"2.5\"\n @click=\"editor.chain().focus().toggleBold().run()\"\n />\n <ToolbarIconButton\n :icon=\"Italic\"\n :label=\"t.paragraphEditor.italic\"\n :active=\"editor.isActive('italic')\"\n @click=\"editor.chain().focus().toggleItalic().run()\"\n />\n <ToolbarIconButton\n :icon=\"Underline\"\n :label=\"t.paragraphEditor.underline\"\n :active=\"editor.isActive('underline')\"\n @click=\"editor.chain().focus().toggleUnderline().run()\"\n />\n <ToolbarIconButton\n :icon=\"Strikethrough\"\n :label=\"t.paragraphEditor.strikethrough\"\n :active=\"editor.isActive('strike')\"\n @click=\"editor.chain().focus().toggleStrike().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Subscript\"\n :label=\"t.paragraphEditor.subscript\"\n :active=\"editor.isActive('subscript')\"\n @click=\"editor.chain().focus().toggleSubscript().run()\"\n />\n <ToolbarIconButton\n :icon=\"Superscript\"\n :label=\"t.paragraphEditor.superscript\"\n :active=\"editor.isActive('superscript')\"\n @click=\"editor.chain().focus().toggleSuperscript().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"Link\"\n :label=\"t.paragraphEditor.addLink\"\n :active=\"editor.isActive('link')\"\n @click=\"emit('open-link-dialog')\"\n />\n </div>\n <!-- Row 2: Lists, Alignment, LH, LS, Clear, Emoji, Merge tags -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <ToolbarIconButton\n :icon=\"List\"\n :label=\"t.paragraphEditor.bulletList\"\n :active=\"editor.isActive('bulletList')\"\n @click=\"editor.chain().focus().toggleBulletList().run()\"\n />\n <ToolbarIconButton\n :icon=\"ListOrdered\"\n :label=\"t.paragraphEditor.numberedList\"\n :active=\"editor.isActive('orderedList')\"\n @click=\"editor.chain().focus().toggleOrderedList().run()\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"AlignLeft\"\n :label=\"t.paragraphEditor.alignLeft\"\n :active=\"editor.isActive({ textAlign: 'left' })\"\n @click=\"editor.chain().focus().setTextAlign('left').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignCenter\"\n :label=\"t.paragraphEditor.alignCenter\"\n :active=\"editor.isActive({ textAlign: 'center' })\"\n @click=\"editor.chain().focus().setTextAlign('center').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignRight\"\n :label=\"t.paragraphEditor.alignRight\"\n :active=\"editor.isActive({ textAlign: 'right' })\"\n @click=\"editor.chain().focus().setTextAlign('right').run()\"\n />\n <ToolbarIconButton\n :icon=\"AlignJustify\"\n :label=\"t.paragraphEditor.alignJustify\"\n :active=\"editor.isActive({ textAlign: 'justify' })\"\n @click=\"editor.chain().focus().setTextAlign('justify').run()\"\n />\n <ToolbarSeparator />\n <ToolbarSelect\n :model-value=\"getCurrentLineHeight()\"\n :options=\"LINE_HEIGHT_OPTIONS\"\n :label=\"t.paragraphEditor.lineHeight\"\n placeholder=\"LH\"\n width-class=\"tpl:w-16\"\n @update:model-value=\"setLineHeight\"\n />\n <ToolbarSelect\n :model-value=\"textStyleAttr('letterSpacing')\"\n :options=\"LETTER_SPACING_OPTIONS\"\n :label=\"t.paragraphEditor.letterSpacing\"\n placeholder=\"LS\"\n width-class=\"tpl:w-20\"\n @update:model-value=\"setLetterSpacing\"\n />\n <ToolbarSeparator />\n <ToolbarIconButton\n :icon=\"RemoveFormatting\"\n :label=\"t.paragraphEditor.clearFormatting\"\n @click=\"editor.chain().focus().clearNodes().unsetAllMarks().run()\"\n />\n <ToolbarSeparator />\n <EmojiPickerDropdown @insert=\"insertEmoji\" />\n <template v-if=\"canRequestMergeTag\">\n <ToolbarSeparator />\n <button\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:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"emit('add-merge-tag')\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n </template>\n <template v-if=\"canInsertLogicTag\">\n <ToolbarSeparator />\n <button\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: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=\"emit('add-logic-tag')\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n </div>\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</template>\n","<script setup lang=\"ts\">\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { ParagraphBlock as ParagraphBlockType } from \"@templatical/types\";\nimport ParagraphToolbar from \"./ParagraphToolbar.vue\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: ParagraphBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst popoverRoot = usePopoverRoot();\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: \"ParagraphEditor\",\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 { default: UnderlineExt },\n { default: SubscriptExt },\n { default: SuperscriptExt },\n { default: TextAlign },\n { TextStyle },\n { default: Color },\n { default: FontFamily },\n { default: Highlight },\n {\n MergeTagNode,\n MergeTagSuggestion,\n LogicMergeTagNode,\n FontSize,\n LineHeight,\n LetterSpacing,\n LineBoundaryKeys,\n },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"@tiptap/extension-underline\"),\n import(\"@tiptap/extension-subscript\"),\n import(\"@tiptap/extension-superscript\"),\n import(\"@tiptap/extension-text-align\"),\n import(\"@tiptap/extension-text-style\"),\n import(\"@tiptap/extension-color\"),\n import(\"@tiptap/extension-font-family\"),\n import(\"@tiptap/extension-highlight\"),\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 // 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 // StarterKit also bundles @tiptap/extension-underline; the editor\n // registers its own UnderlineExt below, so disable StarterKit's copy\n // to avoid a duplicate `underline` extension (and its console warning).\n underline: false,\n }),\n UnderlineExt,\n SubscriptExt,\n SuperscriptExt,\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n TextAlign.configure({ types: [\"paragraph\"] }),\n TextStyle,\n Color,\n FontFamily,\n Highlight.configure({ multicolor: true }),\n FontSize,\n LineHeight,\n LetterSpacing,\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n LineBoundaryKeys,\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 <ParagraphToolbar\n :editor=\"editor\"\n :toolbar-position=\"toolbarPosition\"\n :is-loading=\"isLoading\"\n :can-request-merge-tag=\"canRequestMergeTag\"\n :can-insert-logic-tag=\"canInsertLogicTag\"\n @open-link-dialog=\"openLinkDialog\"\n @add-merge-tag=\"handleAddMergeTag\"\n @add-logic-tag=\"handleAddLogicTag\"\n />\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 { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { ParagraphBlock as ParagraphBlockType } from \"@templatical/types\";\nimport ParagraphToolbar from \"./ParagraphToolbar.vue\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: ParagraphBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst popoverRoot = usePopoverRoot();\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: \"ParagraphEditor\",\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 { default: UnderlineExt },\n { default: SubscriptExt },\n { default: SuperscriptExt },\n { default: TextAlign },\n { TextStyle },\n { default: Color },\n { default: FontFamily },\n { default: Highlight },\n {\n MergeTagNode,\n MergeTagSuggestion,\n LogicMergeTagNode,\n FontSize,\n LineHeight,\n LetterSpacing,\n LineBoundaryKeys,\n },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"@tiptap/extension-underline\"),\n import(\"@tiptap/extension-subscript\"),\n import(\"@tiptap/extension-superscript\"),\n import(\"@tiptap/extension-text-align\"),\n import(\"@tiptap/extension-text-style\"),\n import(\"@tiptap/extension-color\"),\n import(\"@tiptap/extension-font-family\"),\n import(\"@tiptap/extension-highlight\"),\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 // 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 // StarterKit also bundles @tiptap/extension-underline; the editor\n // registers its own UnderlineExt below, so disable StarterKit's copy\n // to avoid a duplicate `underline` extension (and its console warning).\n underline: false,\n }),\n UnderlineExt,\n SubscriptExt,\n SuperscriptExt,\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n TextAlign.configure({ types: [\"paragraph\"] }),\n TextStyle,\n Color,\n FontFamily,\n Highlight.configure({ multicolor: true }),\n FontSize,\n LineHeight,\n LetterSpacing,\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n LineBoundaryKeys,\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 <ParagraphToolbar\n :editor=\"editor\"\n :toolbar-position=\"toolbarPosition\"\n :is-loading=\"isLoading\"\n :can-request-merge-tag=\"canRequestMergeTag\"\n :can-insert-logic-tag=\"canInsertLogicTag\"\n @open-link-dialog=\"openLinkDialog\"\n @add-merge-tag=\"handleAddMergeTag\"\n @add-logic-tag=\"handleAddLogicTag\"\n />\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,8 +1,8 @@
1
1
  import { H as e, N as t, S as n, at as r, b as i, ct as a, g as o, h as s, m as c, p as l, ut as u, y as d } from "./draggable-kbOhCgnk.js";
2
- import { t as f } from "./useI18n-DCbvHv6W.js";
2
+ import { t as f } from "./useI18n-Ct1VIFcf.js";
3
3
  import { it as p, u as m } from "./icons-SP5Itd-A.js";
4
4
  import { d as h, v as g, x as _ } from "./styleConstants-C_YAPdQS.js";
5
- import { t as v } from "./TplModal-CxQDl-tN.js";
5
+ import { t as v } from "./TplModal-CqY8R3hn.js";
6
6
  //#region src/components/RestoreVersionDialog.vue?vue&type=script&setup=true&lang.ts
7
7
  var y = ["aria-busy"], b = { class: "tpl:flex tpl:items-center tpl:gap-2" }, x = {
8
8
  id: "tpl-restore-version-title",
@@ -84,4 +84,4 @@ var y = ["aria-busy"], b = { class: "tpl:flex tpl:items-center tpl:gap-2" }, x =
84
84
  //#endregion
85
85
  export { D as default };
86
86
 
87
- //# sourceMappingURL=RestoreVersionDialog--SjTV0_4.js.map
87
+ //# sourceMappingURL=RestoreVersionDialog-BkSSwf-Q.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RestoreVersionDialog--SjTV0_4.js","names":[],"sources":["../../../src/components/RestoreVersionDialog.vue","../../../src/components/RestoreVersionDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n dangerBtnClass,\n primaryBtnClass,\n secondaryBtnClass,\n} from \"../constants/styleConstants\";\n/**\n * Confirms a restore that would discard unsaved work.\n *\n * It exists because `confirmRestore()` throws away the pre-preview backup: once\n * a restore lands, anything the user had not saved exists nowhere. The editor\n * does not record a version to cover that — it never authors versions — so the\n * protection is a question asked at the moment of loss, with the option to put\n * the work somewhere real first.\n *\n * `canSave` decides which of the two shapes renders: an offer to save first, or\n * a plain warning when the templates provider withheld `save` (or there is no\n * templates provider at all), because there is then nowhere to put the work.\n */\nimport { AlertTriangle, LoaderCircle } from \"@lucide/vue\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n /** Whether \"save first\" can be offered rather than only a warning. */\n canSave: boolean;\n /** True while either action is in flight — both buttons go inert. */\n isBusy: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"save-and-restore\"): void;\n (e: \"discard-and-restore\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n :aria-busy=\"isBusy\"\n aria-labelledby=\"tpl-restore-version-title\"\n aria-describedby=\"tpl-restore-version-body\"\n data-testid=\"restore-version-dialog\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto 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 <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h3\n id=\"tpl-restore-version-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.versionPreview.restoreConfirm.title }}\n </h3>\n </div>\n\n <p\n id=\"tpl-restore-version-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n canSave\n ? t.versionPreview.restoreConfirm.unsavedWithSave\n : t.versionPreview.restoreConfirm.unsavedNoSave\n }}\n </p>\n\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"restore-version-cancel\"\n :class=\"secondaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('cancel')\"\n >\n {{ t.versionPreview.restoreConfirm.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"restore-version-discard\"\n :class=\"dangerBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('discard-and-restore')\"\n >\n {{ t.versionPreview.restoreConfirm.restoreAnyway }}\n </button>\n <!-- Only rendered when there is somewhere to save to: an action the\n editor cannot perform should not look like one you could take. -->\n <button\n v-if=\"canSave\"\n type=\"button\"\n data-testid=\"restore-version-save-first\"\n :class=\"primaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('save-and-restore')\"\n >\n <LoaderCircle\n v-if=\"isBusy\"\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.versionPreview.restoreConfirm.saveAndRestore }}\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport {\n dangerBtnClass,\n primaryBtnClass,\n secondaryBtnClass,\n} from \"../constants/styleConstants\";\n/**\n * Confirms a restore that would discard unsaved work.\n *\n * It exists because `confirmRestore()` throws away the pre-preview backup: once\n * a restore lands, anything the user had not saved exists nowhere. The editor\n * does not record a version to cover that — it never authors versions — so the\n * protection is a question asked at the moment of loss, with the option to put\n * the work somewhere real first.\n *\n * `canSave` decides which of the two shapes renders: an offer to save first, or\n * a plain warning when the templates provider withheld `save` (or there is no\n * templates provider at all), because there is then nowhere to put the work.\n */\nimport { AlertTriangle, LoaderCircle } from \"@lucide/vue\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n /** Whether \"save first\" can be offered rather than only a warning. */\n canSave: boolean;\n /** True while either action is in flight — both buttons go inert. */\n isBusy: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"save-and-restore\"): void;\n (e: \"discard-and-restore\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n :aria-busy=\"isBusy\"\n aria-labelledby=\"tpl-restore-version-title\"\n aria-describedby=\"tpl-restore-version-body\"\n data-testid=\"restore-version-dialog\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto 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 <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h3\n id=\"tpl-restore-version-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.versionPreview.restoreConfirm.title }}\n </h3>\n </div>\n\n <p\n id=\"tpl-restore-version-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n canSave\n ? t.versionPreview.restoreConfirm.unsavedWithSave\n : t.versionPreview.restoreConfirm.unsavedNoSave\n }}\n </p>\n\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"restore-version-cancel\"\n :class=\"secondaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('cancel')\"\n >\n {{ t.versionPreview.restoreConfirm.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"restore-version-discard\"\n :class=\"dangerBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('discard-and-restore')\"\n >\n {{ t.versionPreview.restoreConfirm.restoreAnyway }}\n </button>\n <!-- Only rendered when there is somewhere to save to: an action the\n editor cannot perform should not look like one you could take. -->\n <button\n v-if=\"canSave\"\n type=\"button\"\n data-testid=\"restore-version-save-first\"\n :class=\"primaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('save-and-restore')\"\n >\n <LoaderCircle\n v-if=\"isBusy\"\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.versionPreview.restoreConfirm.saveAndRestore }}\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA+BA,IAAM,IAAO,GAMP,EAAE,SAAM,EAAQ;oBAIpB,EAAA,GAAA,EA8EW,GAAA;GA9EA,SAAS,EAAA;GAAU,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,QAAA;;GACvC,SAAA,QA4EM,CA5EN,EA4EM,OAAA;IA3EJ,MAAK;IACL,cAAW;IACV,aAAW,EAAA;IACZ,mBAAgB;IAChB,oBAAiB;IACjB,eAAY;IACZ,OAAM;IACN,OAAA;KAAA,oBAAA;KAAA,cAAA;IAAA;;IAKA,EAYM,OAZN,GAYM,CAXJ,EAIE,EAAA,CAAA,GAAA;KAHC,MAAM;KACN,gBAAc;KACf,OAAM;IAER,CAAA,GAAA,EAKK,MALL,GAKK,EADA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,KAAK,GAAA,CAAA,CAAA,CAAA;IAI5C,EASI,KATJ,GASI,EAJA,EAAA,UAAsB,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,kBAA8B,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,aAAa,GAAA,CAAA;IAMrI,EAqCM,OArCN,GAqCM;KApCJ,EAQS,UAAA;MAPP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAiB;MACxB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,QAAA;KAET,GAAA,EAAA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,MAAM,GAAA,IAAA,CAAA;KAE3C,EAQS,UAAA;MAPP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAc;MACrB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,qBAAA;KAET,GAAA,EAAA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,aAAa,GAAA,IAAA,CAAA;KAK1C,EAAA,WADR,EAAA,GAAA,EAeS,UAAA;;MAbP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAe;MACtB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,kBAAA;KAGJ,GAAA,CAAA,EAAA,UADR,EAAA,GAAA,EAKE,EAAA,CAAA,GAAA;;MAHA,OAAM;MACL,MAAM;MACN,gBAAc;KACf,CAAA,KAAA,EAAA,IAAA,EAAA,GAAA,EAAA,MACF,EAAG,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,cAAc,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA,KAAA,EAAA,IAAA,EAAA"}
1
+ {"version":3,"file":"RestoreVersionDialog-BkSSwf-Q.js","names":[],"sources":["../../../src/components/RestoreVersionDialog.vue","../../../src/components/RestoreVersionDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n dangerBtnClass,\n primaryBtnClass,\n secondaryBtnClass,\n} from \"../constants/styleConstants\";\n/**\n * Confirms a restore that would discard unsaved work.\n *\n * It exists because `confirmRestore()` throws away the pre-preview backup: once\n * a restore lands, anything the user had not saved exists nowhere. The editor\n * does not record a version to cover that — it never authors versions — so the\n * protection is a question asked at the moment of loss, with the option to put\n * the work somewhere real first.\n *\n * `canSave` decides which of the two shapes renders: an offer to save first, or\n * a plain warning when the templates provider withheld `save` (or there is no\n * templates provider at all), because there is then nowhere to put the work.\n */\nimport { AlertTriangle, LoaderCircle } from \"@lucide/vue\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n /** Whether \"save first\" can be offered rather than only a warning. */\n canSave: boolean;\n /** True while either action is in flight — both buttons go inert. */\n isBusy: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"save-and-restore\"): void;\n (e: \"discard-and-restore\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n :aria-busy=\"isBusy\"\n aria-labelledby=\"tpl-restore-version-title\"\n aria-describedby=\"tpl-restore-version-body\"\n data-testid=\"restore-version-dialog\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto 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 <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h3\n id=\"tpl-restore-version-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.versionPreview.restoreConfirm.title }}\n </h3>\n </div>\n\n <p\n id=\"tpl-restore-version-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n canSave\n ? t.versionPreview.restoreConfirm.unsavedWithSave\n : t.versionPreview.restoreConfirm.unsavedNoSave\n }}\n </p>\n\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"restore-version-cancel\"\n :class=\"secondaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('cancel')\"\n >\n {{ t.versionPreview.restoreConfirm.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"restore-version-discard\"\n :class=\"dangerBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('discard-and-restore')\"\n >\n {{ t.versionPreview.restoreConfirm.restoreAnyway }}\n </button>\n <!-- Only rendered when there is somewhere to save to: an action the\n editor cannot perform should not look like one you could take. -->\n <button\n v-if=\"canSave\"\n type=\"button\"\n data-testid=\"restore-version-save-first\"\n :class=\"primaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('save-and-restore')\"\n >\n <LoaderCircle\n v-if=\"isBusy\"\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.versionPreview.restoreConfirm.saveAndRestore }}\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport {\n dangerBtnClass,\n primaryBtnClass,\n secondaryBtnClass,\n} from \"../constants/styleConstants\";\n/**\n * Confirms a restore that would discard unsaved work.\n *\n * It exists because `confirmRestore()` throws away the pre-preview backup: once\n * a restore lands, anything the user had not saved exists nowhere. The editor\n * does not record a version to cover that — it never authors versions — so the\n * protection is a question asked at the moment of loss, with the option to put\n * the work somewhere real first.\n *\n * `canSave` decides which of the two shapes renders: an offer to save first, or\n * a plain warning when the templates provider withheld `save` (or there is no\n * templates provider at all), because there is then nowhere to put the work.\n */\nimport { AlertTriangle, LoaderCircle } from \"@lucide/vue\";\nimport TplModal from \"./TplModal.vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n /** Whether \"save first\" can be offered rather than only a warning. */\n canSave: boolean;\n /** True while either action is in flight — both buttons go inert. */\n isBusy: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"save-and-restore\"): void;\n (e: \"discard-and-restore\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n :aria-busy=\"isBusy\"\n aria-labelledby=\"tpl-restore-version-title\"\n aria-describedby=\"tpl-restore-version-body\"\n data-testid=\"restore-version-dialog\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto 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 <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h3\n id=\"tpl-restore-version-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.versionPreview.restoreConfirm.title }}\n </h3>\n </div>\n\n <p\n id=\"tpl-restore-version-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n canSave\n ? t.versionPreview.restoreConfirm.unsavedWithSave\n : t.versionPreview.restoreConfirm.unsavedNoSave\n }}\n </p>\n\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"restore-version-cancel\"\n :class=\"secondaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('cancel')\"\n >\n {{ t.versionPreview.restoreConfirm.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"restore-version-discard\"\n :class=\"dangerBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('discard-and-restore')\"\n >\n {{ t.versionPreview.restoreConfirm.restoreAnyway }}\n </button>\n <!-- Only rendered when there is somewhere to save to: an action the\n editor cannot perform should not look like one you could take. -->\n <button\n v-if=\"canSave\"\n type=\"button\"\n data-testid=\"restore-version-save-first\"\n :class=\"primaryBtnClass\"\n :disabled=\"isBusy\"\n @click=\"emit('save-and-restore')\"\n >\n <LoaderCircle\n v-if=\"isBusy\"\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.versionPreview.restoreConfirm.saveAndRestore }}\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA+BA,IAAM,IAAO,GAMP,EAAE,SAAM,EAAQ;oBAIpB,EAAA,GAAA,EA8EW,GAAA;GA9EA,SAAS,EAAA;GAAU,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,QAAA;;GACvC,SAAA,QA4EM,CA5EN,EA4EM,OAAA;IA3EJ,MAAK;IACL,cAAW;IACV,aAAW,EAAA;IACZ,mBAAgB;IAChB,oBAAiB;IACjB,eAAY;IACZ,OAAM;IACN,OAAA;KAAA,oBAAA;KAAA,cAAA;IAAA;;IAKA,EAYM,OAZN,GAYM,CAXJ,EAIE,EAAA,CAAA,GAAA;KAHC,MAAM;KACN,gBAAc;KACf,OAAM;IAER,CAAA,GAAA,EAKK,MALL,GAKK,EADA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,KAAK,GAAA,CAAA,CAAA,CAAA;IAI5C,EASI,KATJ,GASI,EAJA,EAAA,UAAsB,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,kBAA8B,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,aAAa,GAAA,CAAA;IAMrI,EAqCM,OArCN,GAqCM;KApCJ,EAQS,UAAA;MAPP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAiB;MACxB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,QAAA;KAET,GAAA,EAAA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,MAAM,GAAA,IAAA,CAAA;KAE3C,EAQS,UAAA;MAPP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAc;MACrB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,qBAAA;KAET,GAAA,EAAA,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,aAAa,GAAA,IAAA,CAAA;KAK1C,EAAA,WADR,EAAA,GAAA,EAeS,UAAA;;MAbP,MAAK;MACL,eAAY;MACX,OAAK,EAAE,EAAA,CAAA,CAAe;MACtB,UAAU,EAAA;MACV,SAAK,AAAA,EAAA,QAAA,MAAE,EAAI,kBAAA;KAGJ,GAAA,CAAA,EAAA,UADR,EAAA,GAAA,EAKE,EAAA,CAAA,GAAA;;MAHA,OAAM;MACL,MAAM;MACN,gBAAc;KACf,CAAA,KAAA,EAAA,IAAA,EAAA,GAAA,EAAA,MACF,EAAG,EAAA,CAAA,CAAC,CAAC,eAAe,eAAe,cAAc,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA,KAAA,EAAA,IAAA,EAAA"}
@@ -0,0 +1,122 @@
1
+ import { D as e, E as t, N as n, R as r, S as i, at as a, b as o, d as s, f as c, g as l, h as u, l as d, lt as f, m as p, p as m, ut as h, y as g, z as _ } from "./draggable-kbOhCgnk.js";
2
+ import { K as v, U as y, h as b } from "./keys-DQp8x7Nx.js";
3
+ import { t as x } from "./useI18n-Ct1VIFcf.js";
4
+ import { n as S } from "./usePopoverPosition-BxVWE5_0.js";
5
+ import { n as C } from "./icons-SP5Itd-A.js";
6
+ import { t as w } from "./ColorPicker-BubTDiS1.js";
7
+ import { t as T } from "./MergeTagInput-1vcI9uQH.js";
8
+ //#region src/components/blocks/RichTextLinkDialog.vue?vue&type=script&setup=true&lang.ts
9
+ var E = ["data-tpl-theme"], D = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-5 tpl:py-4" }, O = {
10
+ id: "tpl-link-dialog-title",
11
+ class: "tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]"
12
+ }, k = ["aria-label"], A = { class: "tpl:p-5" }, j = { class: "tpl:mb-4 tpl:last:mb-0" }, M = { class: "tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase" }, N = { class: "tpl:mb-4 tpl:last:mb-0" }, P = { class: "tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase" }, F = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-5 tpl:py-4" }, I = { class: "tpl:ml-auto tpl:flex tpl:gap-2" }, L = /* @__PURE__ */ i({
13
+ __name: "RichTextLinkDialog",
14
+ props: /*@__PURE__*/ e({
15
+ visible: { type: Boolean },
16
+ isEditingLink: { type: Boolean }
17
+ }, {
18
+ linkUrl: { required: !0 },
19
+ linkUrlModifiers: {},
20
+ linkColor: { default: "" },
21
+ linkColorModifiers: {},
22
+ dialogRef: { required: !0 },
23
+ dialogRefModifiers: {}
24
+ }),
25
+ emits: /*@__PURE__*/ e([
26
+ "close",
27
+ "insert",
28
+ "remove",
29
+ "keydown"
30
+ ], [
31
+ "update:linkUrl",
32
+ "update:linkColor",
33
+ "update:dialogRef"
34
+ ]),
35
+ setup(e, { emit: r }) {
36
+ let i = _(e, "linkUrl"), g = _(e, "linkColor"), L = _(e, "dialogRef"), R = r, z = t(y, null), B = t(v, null), V = S(), H = t(b, null), U = c(() => H?.content.value.settings.linkColor || "#2c85de"), { t: W } = x();
37
+ return (t, r) => a(V) ? (n(), p(s, {
38
+ key: 0,
39
+ to: a(V)
40
+ }, [e.visible ? (n(), l("div", {
41
+ key: 0,
42
+ "data-tpl-theme": a(B),
43
+ class: "tpl tpl-link-dialog tpl:fixed tpl:inset-0 tpl:flex tpl:items-center tpl:justify-center",
44
+ style: f(a(z)),
45
+ onClick: r[7] ||= d((e) => R("close"), ["self"])
46
+ }, [m("div", {
47
+ ref: (e) => L.value = e,
48
+ role: "dialog",
49
+ "aria-modal": "true",
50
+ "aria-labelledby": "tpl-link-dialog-title",
51
+ "data-testid": "link-dialog",
52
+ class: "tpl:w-[400px] tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-lg)]"
53
+ }, [
54
+ m("div", D, [m("h4", O, h(e.isEditingLink ? a(W).linkDialog.editLink : a(W).linkDialog.insertLink), 1), m("button", {
55
+ type: "button",
56
+ "aria-label": a(W).linkDialog.cancel,
57
+ class: "tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]",
58
+ onClick: r[0] ||= (e) => R("close")
59
+ }, [o(a(C), {
60
+ size: 16,
61
+ "stroke-width": 2
62
+ })], 8, k)]),
63
+ m("div", A, [m("div", j, [m("label", M, h(a(W).linkDialog.urlLabel), 1), o(T, {
64
+ "model-value": i.value,
65
+ type: "url",
66
+ "data-testid": "link-dialog-url",
67
+ placeholder: a(W).linkDialog.urlPlaceholder,
68
+ "onUpdate:modelValue": r[1] ||= (e) => i.value = e,
69
+ onKeydown: r[2] ||= (e) => R("keydown", e)
70
+ }, null, 8, ["model-value", "placeholder"])]), m("div", N, [m("label", P, h(a(W).linkDialog.colorLabel), 1), o(w, {
71
+ "model-value": g.value,
72
+ "seed-color": U.value,
73
+ "onUpdate:modelValue": r[3] ||= (e) => g.value = e
74
+ }, null, 8, ["model-value", "seed-color"])])]),
75
+ m("div", F, [e.isEditingLink ? (n(), l("button", {
76
+ key: 0,
77
+ type: "button",
78
+ class: "tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-danger)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-danger)] tpl:transition-all tpl:hover:bg-[var(--tpl-danger-light)]",
79
+ onClick: r[4] ||= (e) => R("remove")
80
+ }, h(a(W).linkDialog.removeLink), 1)) : u("", !0), m("div", I, [m("button", {
81
+ type: "button",
82
+ class: "tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]",
83
+ onClick: r[5] ||= (e) => R("close")
84
+ }, h(a(W).linkDialog.cancel), 1), m("button", {
85
+ type: "button",
86
+ class: "tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:transition-all tpl:hover:bg-[var(--tpl-primary-hover)] tpl:text-[var(--tpl-on-primary)]",
87
+ "data-testid": "link-dialog-submit",
88
+ onClick: r[6] ||= (e) => R("insert")
89
+ }, h(e.isEditingLink ? a(W).linkDialog.updateLink : a(W).linkDialog.insertLink), 1)])])
90
+ ], 512)], 12, E)) : u("", !0)], 8, ["to"])) : u("", !0);
91
+ }
92
+ }), R = {
93
+ key: 0,
94
+ class: "tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2"
95
+ }, z = { class: "tpl:animate-pulse tpl:text-[var(--tpl-text-dim)]" }, B = {
96
+ key: 1,
97
+ class: "tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:p-2 tpl:text-center tpl:text-xs tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-text-muted)]"
98
+ }, V = /* @__PURE__ */ i({
99
+ __name: "RichTextEditorContent",
100
+ props: {
101
+ editor: {},
102
+ editorContent: {},
103
+ isLoading: { type: Boolean },
104
+ initError: {}
105
+ },
106
+ emits: ["retry"],
107
+ setup(e, { emit: t }) {
108
+ let i = t, { t: o } = x();
109
+ return (t, s) => e.isLoading ? (n(), l("div", R, [m("div", z, h(a(o).errors.editorLoading), 1)])) : e.initError ? (n(), l("div", B, [g(h(a(o).errors.editorLoadFailed) + " ", 1), m("button", {
110
+ class: "tpl:ml-1 tpl:cursor-pointer tpl:border-none tpl:bg-transparent tpl:p-0 tpl:underline tpl:text-[var(--tpl-primary)]",
111
+ onClick: s[0] ||= (e) => i("retry")
112
+ }, h(a(o).errors.retry), 1)])) : e.editorContent && e.editor ? (n(), p(r(e.editorContent), {
113
+ key: 2,
114
+ editor: e.editor,
115
+ class: "tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2"
116
+ }, null, 8, ["editor"])) : u("", !0);
117
+ }
118
+ });
119
+ //#endregion
120
+ export { L as n, V as t };
121
+
122
+ //# sourceMappingURL=RichTextEditorContent-S4KogjIj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RichTextEditorContent-S4KogjIj.js","names":[],"sources":["../../../src/components/blocks/RichTextLinkDialog.vue","../../../src/components/blocks/RichTextLinkDialog.vue","../../../src/components/blocks/RichTextEditorContent.vue","../../../src/components/blocks/RichTextEditorContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport { X } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { EDITOR_KEY, THEME_STYLES_KEY, UI_THEME_KEY } from \"../../keys\";\n\ndefineProps<{\n visible: boolean;\n isEditingLink: boolean;\n}>();\n\nconst linkUrl = defineModel<string>(\"linkUrl\", { required: true });\nconst linkColor = defineModel<string>(\"linkColor\", { default: \"\" });\nconst dialogRef = defineModel<HTMLElement | null>(\"dialogRef\", {\n required: true,\n});\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\"): void;\n (e: \"remove\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\nconst editorReturn = inject(EDITOR_KEY, null);\n// Seed the picker at the document link color so opening it starts from the\n// color an unset link would inherit; an explicit value paints the swatch.\nconst documentLinkColor = computed(\n () => editorReturn?.content.value.settings.linkColor || \"#2c85de\",\n);\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl-link-dialog tpl:fixed tpl:inset-0 tpl:flex tpl:items-center tpl:justify-center\"\n :style=\"themeStyles\"\n @click.self=\"emit('close')\"\n >\n <div\n :ref=\"(el) => (dialogRef = el as HTMLElement | null)\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-link-dialog-title\"\n data-testid=\"link-dialog\"\n class=\"tpl:w-[400px] tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-5 tpl:py-4\"\n >\n <h4\n id=\"tpl-link-dialog-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n isEditingLink ? t.linkDialog.editLink : t.linkDialog.insertLink\n }}\n </h4>\n <button\n type=\"button\"\n :aria-label=\"t.linkDialog.cancel\"\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n <div class=\"tpl:p-5\">\n <div class=\"tpl:mb-4 tpl:last:mb-0\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >{{ t.linkDialog.urlLabel }}</label\n >\n <!-- Many links are composed from a merge tag (a per-recipient or\n per-event URL), so this field carries the same affordances as\n every other URL field in the editor. `@keydown` only fires for\n keys the autocomplete popup declined, so Enter still submits\n the dialog without hijacking the press that picks a tag. -->\n <MergeTagInput\n :model-value=\"linkUrl\"\n type=\"url\"\n data-testid=\"link-dialog-url\"\n :placeholder=\"t.linkDialog.urlPlaceholder\"\n @update:model-value=\"linkUrl = $event\"\n @keydown=\"emit('keydown', $event)\"\n />\n </div>\n <div class=\"tpl:mb-4 tpl:last:mb-0\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >{{ t.linkDialog.colorLabel }}</label\n >\n <ColorPicker\n :model-value=\"linkColor\"\n :seed-color=\"documentLinkColor\"\n @update:model-value=\"linkColor = $event\"\n />\n </div>\n </div>\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-5 tpl:py-4\"\n >\n <button\n v-if=\"isEditingLink\"\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-danger)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-danger)] tpl:transition-all tpl:hover:bg-[var(--tpl-danger-light)]\"\n @click=\"emit('remove')\"\n >\n {{ t.linkDialog.removeLink }}\n </button>\n <div class=\"tpl:ml-auto tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"emit('close')\"\n >\n {{ t.linkDialog.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:transition-all tpl:hover:bg-[var(--tpl-primary-hover)] tpl:text-[var(--tpl-on-primary)]\"\n data-testid=\"link-dialog-submit\"\n @click=\"emit('insert')\"\n >\n {{\n isEditingLink\n ? t.linkDialog.updateLink\n : t.linkDialog.insertLink\n }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport { X } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { EDITOR_KEY, THEME_STYLES_KEY, UI_THEME_KEY } from \"../../keys\";\n\ndefineProps<{\n visible: boolean;\n isEditingLink: boolean;\n}>();\n\nconst linkUrl = defineModel<string>(\"linkUrl\", { required: true });\nconst linkColor = defineModel<string>(\"linkColor\", { default: \"\" });\nconst dialogRef = defineModel<HTMLElement | null>(\"dialogRef\", {\n required: true,\n});\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\"): void;\n (e: \"remove\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\nconst editorReturn = inject(EDITOR_KEY, null);\n// Seed the picker at the document link color so opening it starts from the\n// color an unset link would inherit; an explicit value paints the swatch.\nconst documentLinkColor = computed(\n () => editorReturn?.content.value.settings.linkColor || \"#2c85de\",\n);\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl-link-dialog tpl:fixed tpl:inset-0 tpl:flex tpl:items-center tpl:justify-center\"\n :style=\"themeStyles\"\n @click.self=\"emit('close')\"\n >\n <div\n :ref=\"(el) => (dialogRef = el as HTMLElement | null)\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-link-dialog-title\"\n data-testid=\"link-dialog\"\n class=\"tpl:w-[400px] tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-5 tpl:py-4\"\n >\n <h4\n id=\"tpl-link-dialog-title\"\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n isEditingLink ? t.linkDialog.editLink : t.linkDialog.insertLink\n }}\n </h4>\n <button\n type=\"button\"\n :aria-label=\"t.linkDialog.cancel\"\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n <div class=\"tpl:p-5\">\n <div class=\"tpl:mb-4 tpl:last:mb-0\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >{{ t.linkDialog.urlLabel }}</label\n >\n <!-- Many links are composed from a merge tag (a per-recipient or\n per-event URL), so this field carries the same affordances as\n every other URL field in the editor. `@keydown` only fires for\n keys the autocomplete popup declined, so Enter still submits\n the dialog without hijacking the press that picks a tag. -->\n <MergeTagInput\n :model-value=\"linkUrl\"\n type=\"url\"\n data-testid=\"link-dialog-url\"\n :placeholder=\"t.linkDialog.urlPlaceholder\"\n @update:model-value=\"linkUrl = $event\"\n @keydown=\"emit('keydown', $event)\"\n />\n </div>\n <div class=\"tpl:mb-4 tpl:last:mb-0\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-xs tpl:font-medium tpl:tracking-wide tpl:text-[var(--tpl-text-muted)] tpl:uppercase\"\n >{{ t.linkDialog.colorLabel }}</label\n >\n <ColorPicker\n :model-value=\"linkColor\"\n :seed-color=\"documentLinkColor\"\n @update:model-value=\"linkColor = $event\"\n />\n </div>\n </div>\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-5 tpl:py-4\"\n >\n <button\n v-if=\"isEditingLink\"\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-danger)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-danger)] tpl:transition-all tpl:hover:bg-[var(--tpl-danger-light)]\"\n @click=\"emit('remove')\"\n >\n {{ t.linkDialog.removeLink }}\n </button>\n <div class=\"tpl:ml-auto tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"emit('close')\"\n >\n {{ t.linkDialog.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-4 tpl:py-2 tpl:text-[13px] tpl:font-medium tpl:transition-all tpl:hover:bg-[var(--tpl-primary-hover)] tpl:text-[var(--tpl-on-primary)]\"\n data-testid=\"link-dialog-submit\"\n @click=\"emit('insert')\"\n >\n {{\n isEditingLink\n ? t.linkDialog.updateLink\n : t.linkDialog.insertLink\n }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport type { Editor } from \"@tiptap/vue-3\";\nimport type { Component } from \"vue\";\n\ndefineProps<{\n editor: Editor | null;\n editorContent: Component | null;\n isLoading: boolean;\n initError: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"isLoading\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2\"\n >\n <div class=\"tpl:animate-pulse tpl:text-[var(--tpl-text-dim)]\">\n {{ t.errors.editorLoading }}\n </div>\n </div>\n <div\n v-else-if=\"initError\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:p-2 tpl:text-center tpl:text-xs tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.errors.editorLoadFailed }}\n <button\n class=\"tpl:ml-1 tpl:cursor-pointer tpl:border-none tpl:bg-transparent tpl:p-0 tpl:underline tpl:text-[var(--tpl-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ t.errors.retry }}\n </button>\n </div>\n <component\n :is=\"editorContent\"\n v-else-if=\"editorContent && editor\"\n :editor=\"editor as Editor\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport type { Editor } from \"@tiptap/vue-3\";\nimport type { Component } from \"vue\";\n\ndefineProps<{\n editor: Editor | null;\n editorContent: Component | null;\n isLoading: boolean;\n initError: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"isLoading\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2\"\n >\n <div class=\"tpl:animate-pulse tpl:text-[var(--tpl-text-dim)]\">\n {{ t.errors.editorLoading }}\n </div>\n </div>\n <div\n v-else-if=\"initError\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:p-2 tpl:text-center tpl:text-xs tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.errors.editorLoadFailed }}\n <button\n class=\"tpl:ml-1 tpl:cursor-pointer tpl:border-none tpl:bg-transparent tpl:p-0 tpl:underline tpl:text-[var(--tpl-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ t.errors.retry }}\n </button>\n </div>\n <component\n :is=\"editorContent\"\n v-else-if=\"editorContent && editor\"\n :editor=\"editor as Editor\"\n class=\"tpl-text-editable tpl:min-h-[1.5em] tpl:rounded tpl:border tpl:border-dashed tpl:border-[var(--tpl-primary)] tpl:p-2\"\n />\n</template>\n"],"mappings":"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"}