@templatical/editor 0.41.0 → 0.42.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 (322) hide show
  1. package/dist/{AiChatSidebar-a8TPBAl2.js → AiChatSidebar-DOCvzGBe.js} +8 -8
  2. package/dist/{AiFeatureMenu-CroDMqRG.js → AiFeatureMenu-CfijAx4G.js} +4 -4
  3. package/dist/{BlockIssueBadge-D8KsGWY6.js → BlockIssueBadge-BJ3A6mqh.js} +2 -2
  4. package/dist/{BlockPreviewCanvas-Z9SZIgfA.js → BlockPreviewCanvas-DggfBaoJ.js} +33 -33
  5. package/dist/{CloudHeaderExtras-CVqHeqc6.js → CloudHeaderExtras-dFbdqBr5.js} +2 -2
  6. package/dist/{CloudPanels-CmEpQUJB.js → CloudPanels-DmMxhzzZ.js} +4 -4
  7. package/dist/{CollaboratorBar-AIQC9fEF.js → CollaboratorBar-XXk1tOC5.js} +2 -2
  8. package/dist/{CommentsPanel-9k1q-79y.js → CommentsPanel-CGEhbjx0.js} +1 -1
  9. package/dist/{CommentsSidebar-Bc7nORmD.js → CommentsSidebar-CUpst9ou.js} +10 -10
  10. package/dist/{CountdownToolbar-DkC-qSgq.js → CountdownToolbar-B1a_UIuB.js} +3 -3
  11. package/dist/{DesignReferenceSidebar-OoiYJAKS.js → DesignReferenceSidebar-DkAmsBgM.js} +6 -6
  12. package/dist/{IssuesPanel-Dnns3O9z.js → IssuesPanel-lTpsCV4a.js} +5 -5
  13. package/dist/{LogicTagInsertButton-BimKobaG.js → LogicTagInsertButton-B5YymR4U.js} +243 -219
  14. package/dist/{MediaEditModal-C-InB-H2.js → MediaEditModal-B1X8qtJb.js} +1 -1
  15. package/dist/{MediaPanels-CTosUpeo.js → MediaPanels-DO30L1KS.js} +1 -1
  16. package/dist/{MergeTagInput-DqpVzhy7.js → MergeTagInput-B0M_-X0K.js} +1 -1
  17. package/dist/{MergeTagModeToggle-BkIiAgpQ.js → MergeTagModeToggle-B-8W1tK7.js} +4 -4
  18. package/dist/{ParagraphEditor-DaZ03d1U.js → ParagraphEditor-BhhpXgoO.js} +35 -35
  19. package/dist/{RestoreVersionDialog-DHb1mPoF.js → RestoreVersionDialog-DE8aqmdi.js} +3 -3
  20. package/dist/{RichTextEditorContent-AEtfKvLz.js → RichTextEditorContent-AaLAArA0.js} +5 -5
  21. package/dist/{SaveBlockDialog-hGwLDWfE.js → SaveBlockDialog-BJ-hEwLv.js} +8 -8
  22. package/dist/{SavedBlocksBrowserModal-C0lYVBFi.js → SavedBlocksBrowserModal-nLI8aAQ5.js} +9 -9
  23. package/dist/{SavedBlocksPanels-CaTbeVDv.js → SavedBlocksPanels-RnzUMUJI.js} +1 -1
  24. package/dist/{SavedBlocksPickBar-DR2afid2.js → SavedBlocksPickBar-D3QA3Tgy.js} +2 -2
  25. package/dist/{TemplateScoringPanel-Cs0TGtRF.js → TemplateScoringPanel-C6ellAcs.js} +11 -11
  26. package/dist/{TemplateSettings-BD1WNP4C.js → TemplateSettings-C_0qK-zs.js} +6 -6
  27. package/dist/{TestEmailModal-DqoosG1u.js → TestEmailModal-CnyxLhWJ.js} +7 -7
  28. package/dist/{TestEmailPanel-DpozqyHF.js → TestEmailPanel-BRisIL4G.js} +1 -1
  29. package/dist/{TitleEditor-BCBdZLt_.js → TitleEditor-lPITPzw6.js} +12 -12
  30. package/dist/{ToggleSwitch-CJfQ8u57.js → ToggleSwitch-CtZ6adlX.js} +1 -1
  31. package/dist/{Toolbar-C7SjilzH.js → Toolbar-B6dG5_IP.js} +847 -554
  32. package/dist/{TplModal-CVO-2KrU.js → TplModal-BiAf7tWe.js} +2 -2
  33. package/dist/{VersionHistoryMenu-b0A8mS2_.js → VersionHistoryMenu-DdspCRSA.js} +5 -5
  34. package/dist/{VersionHistoryPanels-BzF7dmwa.js → VersionHistoryPanels-C589Jm_B.js} +1 -1
  35. package/dist/{VersionPreviewBanner-xB9r83r0.js → VersionPreviewBanner-XUuLe36h.js} +1 -1
  36. package/dist/{WrapperBlock-BDjOn3fY.js → WrapperBlock-CVQ4t1tY.js} +4 -4
  37. package/dist/{blockTypeIcons-B0Bq1WT1.js → blockTypeIcons-Sg8SY1NQ.js} +2 -2
  38. package/dist/bundle-stats.json +6 -6
  39. package/dist/{cdn/chunks/ca-Ckgw9yd0.js → ca--zGWn9AU.js} +14 -2
  40. package/dist/cdn/chunks/{AiChatSidebar-mKImUXi6.js → AiChatSidebar-DiG-EQ-E.js} +8 -8
  41. package/dist/cdn/chunks/AiChatSidebar-DiG-EQ-E.js.map +1 -0
  42. package/dist/cdn/chunks/{AiFeatureMenu-Cx3qdStQ.js → AiFeatureMenu-BXSPgOzA.js} +5 -5
  43. package/dist/cdn/chunks/AiFeatureMenu-BXSPgOzA.js.map +1 -0
  44. package/dist/cdn/chunks/{BlockIssueBadge-DS1pUf5g.js → BlockIssueBadge-ChQ0mPf5.js} +2 -2
  45. package/dist/cdn/chunks/BlockIssueBadge-ChQ0mPf5.js.map +1 -0
  46. package/dist/cdn/chunks/{BlockPreviewCanvas-D31TNib3.js → BlockPreviewCanvas-CMmv1FTA.js} +28 -28
  47. package/dist/cdn/chunks/BlockPreviewCanvas-CMmv1FTA.js.map +1 -0
  48. package/dist/cdn/chunks/{CloudHeaderExtras-0zdlKiq7.js → CloudHeaderExtras-yvFz2_e-.js} +3 -3
  49. package/dist/cdn/chunks/CloudHeaderExtras-yvFz2_e-.js.map +1 -0
  50. package/dist/cdn/chunks/{CloudPanels-DPh71yOO.js → CloudPanels-DpKck9Na.js} +4 -4
  51. package/dist/cdn/chunks/CloudPanels-DpKck9Na.js.map +1 -0
  52. package/dist/cdn/chunks/{CollaboratorBar-lCZU8qkg.js → CollaboratorBar-Clm1ZVXQ.js} +3 -3
  53. package/dist/cdn/chunks/CollaboratorBar-Clm1ZVXQ.js.map +1 -0
  54. package/dist/cdn/chunks/{CommentsPanel-BtAGZfF6.js → CommentsPanel-CjhxGMjL.js} +2 -2
  55. package/dist/cdn/chunks/CommentsPanel-CjhxGMjL.js.map +1 -0
  56. package/dist/cdn/chunks/{CommentsSidebar-Ws07MXYj.js → CommentsSidebar-D9bqMnlt.js} +16 -16
  57. package/dist/cdn/chunks/CommentsSidebar-D9bqMnlt.js.map +1 -0
  58. package/dist/cdn/chunks/CountdownBlock-sVDjKkRw.js.map +1 -1
  59. package/dist/cdn/chunks/{CountdownToolbar-Bu93WiS8.js → CountdownToolbar-CIRx5w1U.js} +4 -4
  60. package/dist/cdn/chunks/CountdownToolbar-CIRx5w1U.js.map +1 -0
  61. package/dist/cdn/chunks/{DesignReferenceSidebar-BlSdh5B7.js → DesignReferenceSidebar-C1-SbE_R.js} +9 -9
  62. package/dist/cdn/chunks/DesignReferenceSidebar-C1-SbE_R.js.map +1 -0
  63. package/dist/cdn/chunks/{IssuesPanel-CKkv9z9g.js → IssuesPanel-qKY6UDd-.js} +8 -8
  64. package/dist/cdn/chunks/IssuesPanel-qKY6UDd-.js.map +1 -0
  65. package/dist/cdn/chunks/LoadingTrack-_7DH_5aw.js.map +1 -1
  66. package/dist/cdn/chunks/{LogicTagInsertButton-CHc1yZ9v.js → LogicTagInsertButton-CItDS_U1.js} +250 -226
  67. package/dist/cdn/chunks/LogicTagInsertButton-CItDS_U1.js.map +1 -0
  68. package/dist/cdn/chunks/{MediaEditModal-Cw9MhjVz.js → MediaEditModal-CzUtXkpk.js} +2 -2
  69. package/dist/cdn/chunks/MediaEditModal-CzUtXkpk.js.map +1 -0
  70. package/dist/cdn/chunks/{MediaPanels-FD_aX3H5.js → MediaPanels-DOLsh7WC.js} +2 -2
  71. package/dist/cdn/chunks/MediaPanels-DOLsh7WC.js.map +1 -0
  72. package/dist/cdn/chunks/{MergeTagInput--qTYfCWk.js → MergeTagInput-B33u3nxj.js} +2 -2
  73. package/dist/cdn/chunks/MergeTagInput-B33u3nxj.js.map +1 -0
  74. package/dist/cdn/chunks/{MergeTagModeToggle-CdWIGo3Q.js → MergeTagModeToggle-Bz1CH3NT.js} +10 -10
  75. package/dist/cdn/chunks/MergeTagModeToggle-Bz1CH3NT.js.map +1 -0
  76. package/dist/cdn/chunks/{ParagraphEditor-w2qEZTkM.js → ParagraphEditor-J74SIn-m.js} +51 -51
  77. package/dist/cdn/chunks/ParagraphEditor-J74SIn-m.js.map +1 -0
  78. package/dist/cdn/chunks/{RestoreVersionDialog-cnWVN3nX.js → RestoreVersionDialog-CFE7jqDB.js} +3 -3
  79. package/dist/cdn/chunks/RestoreVersionDialog-CFE7jqDB.js.map +1 -0
  80. package/dist/cdn/chunks/{RichTextEditorContent-Fzuu9pLP.js → RichTextEditorContent-dOg1xFqv.js} +5 -5
  81. package/dist/cdn/chunks/RichTextEditorContent-dOg1xFqv.js.map +1 -0
  82. package/dist/cdn/chunks/{SaveBlockDialog-jxQtKnVs.js → SaveBlockDialog-DZOmDCC8.js} +38 -38
  83. package/dist/cdn/chunks/SaveBlockDialog-DZOmDCC8.js.map +1 -0
  84. package/dist/cdn/chunks/{SavedBlocksBrowserModal-DpXWf4Pi.js → SavedBlocksBrowserModal-B43H5zKA.js} +10 -10
  85. package/dist/cdn/chunks/SavedBlocksBrowserModal-B43H5zKA.js.map +1 -0
  86. package/dist/cdn/chunks/{SavedBlocksPanels-B-ASmf1P.js → SavedBlocksPanels-uTSb2xO_.js} +2 -2
  87. package/dist/cdn/chunks/SavedBlocksPanels-uTSb2xO_.js.map +1 -0
  88. package/dist/cdn/chunks/{SavedBlocksPickBar-CEFIQ8Nd.js → SavedBlocksPickBar-BUvmGqHK.js} +3 -3
  89. package/dist/cdn/chunks/SavedBlocksPickBar-BUvmGqHK.js.map +1 -0
  90. package/dist/cdn/chunks/{TemplateScoringPanel-d_Ppowsq.js → TemplateScoringPanel-ebsrVge3.js} +19 -19
  91. package/dist/cdn/chunks/TemplateScoringPanel-ebsrVge3.js.map +1 -0
  92. package/dist/cdn/chunks/{TemplateSettings-Dr5OJ0Gi.js → TemplateSettings-BaEVdUTM.js} +9 -9
  93. package/dist/cdn/chunks/TemplateSettings-BaEVdUTM.js.map +1 -0
  94. package/dist/cdn/chunks/{TestEmailModal-CrjzVw8e.js → TestEmailModal-ide7UP_H.js} +7 -7
  95. package/dist/cdn/chunks/TestEmailModal-ide7UP_H.js.map +1 -0
  96. package/dist/cdn/chunks/{TestEmailPanel-k9vdulTu.js → TestEmailPanel-Cqdsnufx.js} +2 -2
  97. package/dist/cdn/chunks/TestEmailPanel-Cqdsnufx.js.map +1 -0
  98. package/dist/cdn/chunks/{TitleEditor-laILWfKh.js → TitleEditor-CoFzxLPA.js} +16 -16
  99. package/dist/cdn/chunks/TitleEditor-CoFzxLPA.js.map +1 -0
  100. package/dist/cdn/chunks/{ToggleSwitch-0dGCYnRY.js → ToggleSwitch-D-8dCEbI.js} +2 -2
  101. package/dist/cdn/chunks/ToggleSwitch-D-8dCEbI.js.map +1 -0
  102. package/dist/cdn/chunks/{Toolbar-BsiHW9gY.js → Toolbar-BDzhrmGV.js} +814 -566
  103. package/dist/cdn/chunks/Toolbar-BDzhrmGV.js.map +1 -0
  104. package/dist/cdn/chunks/{TplModal-D8DoPbFa.js → TplModal-mLMmolQp.js} +3 -3
  105. package/dist/cdn/chunks/TplModal-mLMmolQp.js.map +1 -0
  106. package/dist/cdn/chunks/{VersionHistoryMenu-B3-hNu9c.js → VersionHistoryMenu-C_bBKOYU.js} +7 -7
  107. package/dist/cdn/chunks/VersionHistoryMenu-C_bBKOYU.js.map +1 -0
  108. package/dist/cdn/chunks/{VersionHistoryPanels-zw1AtzGN.js → VersionHistoryPanels-B9LwOKxm.js} +2 -2
  109. package/dist/cdn/chunks/VersionHistoryPanels-B9LwOKxm.js.map +1 -0
  110. package/dist/cdn/chunks/{VersionPreviewBanner-PzVQSBJE.js → VersionPreviewBanner-CKyrcgxJ.js} +2 -2
  111. package/dist/cdn/chunks/VersionPreviewBanner-CKyrcgxJ.js.map +1 -0
  112. package/dist/cdn/chunks/{WrapperBlock-CpRvfAV0.js → WrapperBlock-DI8vSvTO.js} +4 -4
  113. package/dist/cdn/chunks/WrapperBlock-DI8vSvTO.js.map +1 -0
  114. package/dist/cdn/chunks/blockTypeIcons-CdfCfqHE.js +21 -0
  115. package/dist/cdn/chunks/blockTypeIcons-CdfCfqHE.js.map +1 -0
  116. package/dist/{ca-Ckgw9yd0.js → cdn/chunks/ca--zGWn9AU.js} +16 -0
  117. package/dist/cdn/chunks/ca--zGWn9AU.js.map +1 -0
  118. package/dist/cdn/chunks/ca-BTf1Zbqb.js.map +1 -1
  119. package/dist/cdn/chunks/{cloud-CIT4yPp4.js → cloud-De-eh1u6.js} +2 -2
  120. package/dist/cdn/chunks/cloud-De-eh1u6.js.map +1 -0
  121. package/dist/cdn/chunks/{createCloudRuntime-COUxbPvd.js → createCloudRuntime-DsaUaWvr.js} +3 -3
  122. package/dist/cdn/chunks/createCloudRuntime-DsaUaWvr.js.map +1 -0
  123. package/dist/cdn/chunks/{de-D4dweQw8.js → de-BRNLKcDl.js} +15 -1
  124. package/dist/cdn/chunks/de-BRNLKcDl.js.map +1 -0
  125. package/dist/cdn/chunks/de-mMY5yQJu.js.map +1 -1
  126. package/dist/cdn/chunks/de-xzrh9PA3.js.map +1 -1
  127. package/dist/cdn/chunks/dist-BRZhN8qJ.js.map +1 -1
  128. package/dist/cdn/chunks/dist-CXB2_kBa.js.map +1 -1
  129. package/dist/cdn/chunks/draggable-IB4x6w2O.js.map +1 -1
  130. package/dist/cdn/chunks/{editor-modal-CKu8HhiB.js → editor-modal-CZQArGNj.js} +88 -88
  131. package/dist/cdn/chunks/editor-modal-CZQArGNj.js.map +1 -0
  132. package/dist/cdn/chunks/emojiData-6m0DBh7O.js.map +1 -1
  133. package/dist/cdn/chunks/en-9unxBjV8.js +2 -0
  134. package/dist/cdn/chunks/en-ChVeGQBY.js.map +1 -1
  135. package/dist/{en-VE7R7NyL.js → cdn/chunks/en-JJjqHMjy.js} +16 -0
  136. package/dist/cdn/chunks/en-JJjqHMjy.js.map +1 -0
  137. package/dist/cdn/chunks/{es-B9OhP8H3.js → es-BIh8BxV9.js} +15 -1
  138. package/dist/cdn/chunks/es-BIh8BxV9.js.map +1 -0
  139. package/dist/cdn/chunks/es-BT4LSpE2.js.map +1 -1
  140. package/dist/cdn/chunks/{extensions-CafoGF65.js → extensions-Dy251lPd.js} +5 -5
  141. package/dist/cdn/chunks/extensions-Dy251lPd.js.map +1 -0
  142. package/dist/cdn/chunks/formatAbsoluteDateTime-DMmop3C-.js.map +1 -1
  143. package/dist/cdn/chunks/formatRelativeTime-CtUU-QZ8.js.map +1 -1
  144. package/dist/{fr-wrXhIi00.js → cdn/chunks/fr-C5VUZogP.js} +16 -0
  145. package/dist/cdn/chunks/fr-C5VUZogP.js.map +1 -0
  146. package/dist/cdn/chunks/fr-CI7OJoql.js.map +1 -1
  147. package/dist/cdn/chunks/htmlparser-ClUZ6waR.js.map +1 -1
  148. package/dist/cdn/chunks/{icons-BKkJ-5xF.js → icons-CQnjugjy.js} +86 -45
  149. package/dist/cdn/chunks/icons-CQnjugjy.js.map +1 -0
  150. package/dist/cdn/chunks/ja-DhlWUfke.js.map +1 -1
  151. package/dist/cdn/chunks/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +15 -1
  152. package/dist/cdn/chunks/ja-x5JYxaCW.js.map +1 -0
  153. package/dist/cdn/chunks/keys-DCWeHkSF.js.map +1 -1
  154. package/dist/cdn/chunks/liquid.browser-BypTT2oc.js.map +1 -1
  155. package/dist/cdn/chunks/logger-ByvdoAwq.js.map +1 -1
  156. package/dist/cdn/chunks/nl-D8y5OWGB.js.map +1 -1
  157. package/dist/cdn/chunks/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +15 -1
  158. package/dist/cdn/chunks/nl-UXxFyXV6.js.map +1 -0
  159. package/dist/cdn/chunks/{preRenderCustomBlocks-Dpj-_cOj.js → preRenderCustomBlocks-h4ZFBKbD.js} +2 -2
  160. package/dist/cdn/chunks/preRenderCustomBlocks-h4ZFBKbD.js.map +1 -0
  161. package/dist/cdn/chunks/pt-BR-CoCwRUi0.js.map +1 -1
  162. package/dist/{pt-BR-Bh1J8ey6.js → cdn/chunks/pt-BR-DHm3SP6W.js} +16 -0
  163. package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +1 -0
  164. package/dist/cdn/chunks/pt-BR-DMCbUBvJ.js.map +1 -1
  165. package/dist/cdn/chunks/pusher-BbIHJKf3.js.map +1 -1
  166. package/dist/cdn/chunks/readableTextColor-f8Kykfnh.js.map +1 -1
  167. package/dist/cdn/chunks/{socialIcons-BvYNMn1G.js → socialIcons-ZOCwCCyn.js} +3 -3
  168. package/dist/cdn/chunks/socialIcons-ZOCwCCyn.js.map +1 -0
  169. package/dist/cdn/chunks/{src-DveVYoWS.js → src-BR3I3gOW.js} +265 -224
  170. package/dist/cdn/chunks/src-BR3I3gOW.js.map +1 -0
  171. package/dist/cdn/chunks/{src-Cnc5E71L.js → src-fZSLjhjy.js} +12 -12
  172. package/dist/cdn/chunks/src-fZSLjhjy.js.map +1 -0
  173. package/dist/cdn/chunks/{src-L6HEdwFn.js → src-nhIF5-mf.js} +179 -166
  174. package/dist/cdn/chunks/src-nhIF5-mf.js.map +1 -0
  175. package/dist/cdn/chunks/styleConstants-C_YAPdQS.js.map +1 -1
  176. package/dist/cdn/chunks/templateSettingsFields-YCFVBVYh.js.map +1 -1
  177. package/dist/cdn/chunks/timeouts-DHPZQ0G3.js.map +1 -1
  178. package/dist/cdn/chunks/{tiptap-DhLuKyDc.js → tiptap-D1u8JIUW.js} +1916 -1917
  179. package/dist/cdn/chunks/tiptap-D1u8JIUW.js.map +1 -0
  180. package/dist/cdn/chunks/useAliveFlag-DAlQLbM2.js.map +1 -1
  181. package/dist/cdn/chunks/useCloudI18n-CrjWbpbu.js.map +1 -1
  182. package/dist/cdn/chunks/{useEditorCore-DZmtKwJh.js → useEditorCore-D4kzTMtQ.js} +458 -446
  183. package/dist/cdn/chunks/useEditorCore-D4kzTMtQ.js.map +1 -0
  184. package/dist/cdn/chunks/useEditorRoot-CshefZLP.js.map +1 -1
  185. package/dist/cdn/chunks/useI18n-CyO_Nmca.js.map +1 -1
  186. package/dist/cdn/chunks/{useLogicTag-DF7-l66G.js → useLogicTag-J_i_hvZg.js} +2 -2
  187. package/dist/cdn/chunks/useLogicTag-J_i_hvZg.js.map +1 -0
  188. package/dist/cdn/chunks/{useMergeTag-DbIHw4NG.js → useMergeTag-C5MyHYxJ.js} +2 -2
  189. package/dist/cdn/chunks/useMergeTag-C5MyHYxJ.js.map +1 -0
  190. package/dist/cdn/chunks/{usePopoverPosition-BJRvAb-h.js → usePopoverPosition-DGXjXKjM.js} +25 -12
  191. package/dist/cdn/chunks/usePopoverPosition-DGXjXKjM.js.map +1 -0
  192. package/dist/cdn/chunks/useScrollToBlock-DgHZJvID.js.map +1 -1
  193. package/dist/cdn/editor.css +1 -1
  194. package/dist/cdn/editor.js +493 -464
  195. package/dist/cdn/editor.js.map +1 -1
  196. package/dist/{check-eqb2JhqU.js → check-BMx_TLAS.js} +1 -1
  197. package/dist/{chevron-down-Dkr-7Ywl.js → chevron-down-KZf5mh9_.js} +1 -1
  198. package/dist/{chevron-right-Bge4XF_B.js → chevron-right-BLZ60Vxw.js} +1 -1
  199. package/dist/{chevron-up-CQAyl0kC.js → chevron-up-B9ynB8eU.js} +1 -1
  200. package/dist/{circle-alert-CYgTHKWr.js → circle-alert-pjvFFDrF.js} +1 -1
  201. package/dist/{clock-BgSJZJP5.js → clock-CiiTBfF3.js} +1 -1
  202. package/dist/{cloud-DsqqUfzh.js → cloud-AO_X8T9g.js} +1 -1
  203. package/dist/{copy-mw6PVnOJ.js → copy-BF0_pQjH.js} +1 -1
  204. package/dist/{createCloudRuntime-iLflkYzP.js → createCloudRuntime-D641nhri.js} +2 -2
  205. package/dist/{createLucideIcon-BxY5yz5u.js → createLucideIcon-C2YOVVWx.js} +8 -8
  206. package/dist/{de-D4dweQw8.js → de-BRNLKcDl.js} +14 -0
  207. package/dist/{dist-CQSVro7b.js → dist-B0dkvy7z.js} +1 -1
  208. package/dist/{dist-BJM3eFyT.js → dist-BPWuzECC.js} +1 -1
  209. package/dist/{dist-EDkj3Ub1.js → dist-BUHovM4k.js} +1 -1
  210. package/dist/{dist-BmsLcnrO.js → dist-BwoEME8p.js} +1 -1
  211. package/dist/{dist-DerFNpDY.js → dist-C2Ge44fD.js} +760 -761
  212. package/dist/{dist-BdHF7nyq.js → dist-CIxmOOSG.js} +5 -5
  213. package/dist/{dist-wyXxj3R1.js → dist-CJ7RZARo.js} +1 -1
  214. package/dist/{dist-Bl4rxpX9.js → dist-CxmLZQso.js} +1 -1
  215. package/dist/{dist-DyrEOKEJ.js → dist-DRmafAUz.js} +1 -1
  216. package/dist/{dist-Djmzp6wi.js → dist-DdOAotfz.js} +1 -1
  217. package/dist/{dist-BxVnMgXZ.js → dist-W_ERo6c_.js} +1 -1
  218. package/dist/{dist-DbUZvK04.js → dist-iLUVPysa.js} +263 -222
  219. package/dist/{dist-DQbOebGW.js → dist-p-LkiWIQ.js} +1 -1
  220. package/dist/{editor-modal-qzmEVcIk.js → editor-modal-99FB40dl.js} +16 -16
  221. package/dist/en-BvOjRLiw.js +2 -0
  222. package/dist/{cdn/chunks/en-VE7R7NyL.js → en-JJjqHMjy.js} +14 -2
  223. package/dist/{es-B9OhP8H3.js → es-BIh8BxV9.js} +14 -0
  224. package/dist/{extensions-DDhjuVKy.js → extensions-DqQXxrvP.js} +13 -13
  225. package/dist/{eye-DphkRtAD.js → eye-RdXYgrQZ.js} +1 -1
  226. package/dist/{cdn/chunks/fr-wrXhIi00.js → fr-C5VUZogP.js} +14 -2
  227. package/dist/{image-up-Qi2BXD7T.js → image-up-BwdoReQt.js} +1 -1
  228. package/dist/{info-VSyAs02q.js → info-zFMSGrL1.js} +1 -1
  229. package/dist/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +14 -0
  230. package/dist/{link-CNuozGqD.js → link-D7orqFBT.js} +1 -1
  231. package/dist/{list-BA0I5Wmu.js → list-BGt4Pgxc.js} +1 -1
  232. package/dist/{loader-circle-z5eeqasv.js → loader-circle-BVBgkRGz.js} +1 -1
  233. package/dist/{message-circle-BsQ9hEup.js → message-circle-D8YLsarv.js} +1 -1
  234. package/dist/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +14 -0
  235. package/dist/{package-OkXfuRoO.js → package-CuFvk0L5.js} +1 -1
  236. package/dist/{pencil-DFVFA7Dj.js → pencil-DRYbmZW7.js} +1 -1
  237. package/dist/{plus-D8bkVjZ7.js → plus-Dv0b3kh0.js} +1 -1
  238. package/dist/{preRenderCustomBlocks-BBSqQgke.js → preRenderCustomBlocks-CTstI3Q-.js} +4 -4
  239. package/dist/{cdn/chunks/pt-BR-Bh1J8ey6.js → pt-BR-DHm3SP6W.js} +14 -2
  240. package/dist/{refresh-cw-DPB7YFuz.js → refresh-cw-gYTIS5bJ.js} +1 -1
  241. package/dist/{search-C-dCgqIa.js → search-D84_Ki_a.js} +1 -1
  242. package/dist/{send-Cm2fy2zm.js → send-Cn-8_H_U.js} +1 -1
  243. package/dist/{shield-check-COgBYFDF.js → shield-check-BOoVWJtD.js} +1 -1
  244. package/dist/{smartphone-v0u_MOEV.js → smartphone-4Ej3PBw5.js} +1 -1
  245. package/dist/{socialIcons-O7PZCW2P.js → socialIcons-CzRJskEk.js} +47 -47
  246. package/dist/{sparkles-Ds0RF8Iu.js → sparkles-D3vnNe7O.js} +1 -1
  247. package/dist/style.css +1 -1
  248. package/dist/templatical-editor.js +432 -403
  249. package/dist/{text-align-start-DiavaIm6.js → text-align-start-Ci2h_Xly.js} +1 -1
  250. package/dist/{trash-DnOkfu2V.js → trash-wmi4UKvv.js} +1 -1
  251. package/dist/{triangle-alert-DGLAyXnh.js → triangle-alert-DJ0r2wmQ.js} +1 -1
  252. package/dist/{upload-BBDme2cy.js → upload-XMde-TXf.js} +1 -1
  253. package/dist/{useEditorCore-dcdHB_7H.js → useEditorCore-Dhit8Ygg.js} +894 -882
  254. package/dist/{useLogicTag-D9pvgxQR.js → useLogicTag-BaH8gey0.js} +1 -1
  255. package/dist/{useMergeTag-BaVFPdlq.js → useMergeTag-Cyad1yBE.js} +1 -1
  256. package/dist/{usePopoverPosition-DH7z3Ghm.js → usePopoverPosition-BYmd-lhR.js} +24 -11
  257. package/dist/{useScrollToBlock-Dj4v3rxr.js → useScrollToBlock-B4F7KCrf.js} +1 -1
  258. package/dist/{x-3BHV3Q6K.js → x-BWrtpVl-.js} +1 -1
  259. package/package.json +10 -10
  260. package/dist/cdn/chunks/AiChatSidebar-mKImUXi6.js.map +0 -1
  261. package/dist/cdn/chunks/AiFeatureMenu-Cx3qdStQ.js.map +0 -1
  262. package/dist/cdn/chunks/BlockIssueBadge-DS1pUf5g.js.map +0 -1
  263. package/dist/cdn/chunks/BlockPreviewCanvas-D31TNib3.js.map +0 -1
  264. package/dist/cdn/chunks/CloudHeaderExtras-0zdlKiq7.js.map +0 -1
  265. package/dist/cdn/chunks/CloudPanels-DPh71yOO.js.map +0 -1
  266. package/dist/cdn/chunks/CollaboratorBar-lCZU8qkg.js.map +0 -1
  267. package/dist/cdn/chunks/CommentsPanel-BtAGZfF6.js.map +0 -1
  268. package/dist/cdn/chunks/CommentsSidebar-Ws07MXYj.js.map +0 -1
  269. package/dist/cdn/chunks/CountdownToolbar-Bu93WiS8.js.map +0 -1
  270. package/dist/cdn/chunks/DesignReferenceSidebar-BlSdh5B7.js.map +0 -1
  271. package/dist/cdn/chunks/IssuesPanel-CKkv9z9g.js.map +0 -1
  272. package/dist/cdn/chunks/LogicTagInsertButton-CHc1yZ9v.js.map +0 -1
  273. package/dist/cdn/chunks/MediaEditModal-Cw9MhjVz.js.map +0 -1
  274. package/dist/cdn/chunks/MediaPanels-FD_aX3H5.js.map +0 -1
  275. package/dist/cdn/chunks/MergeTagInput--qTYfCWk.js.map +0 -1
  276. package/dist/cdn/chunks/MergeTagModeToggle-CdWIGo3Q.js.map +0 -1
  277. package/dist/cdn/chunks/ParagraphEditor-w2qEZTkM.js.map +0 -1
  278. package/dist/cdn/chunks/RestoreVersionDialog-cnWVN3nX.js.map +0 -1
  279. package/dist/cdn/chunks/RichTextEditorContent-Fzuu9pLP.js.map +0 -1
  280. package/dist/cdn/chunks/SaveBlockDialog-jxQtKnVs.js.map +0 -1
  281. package/dist/cdn/chunks/SavedBlocksBrowserModal-DpXWf4Pi.js.map +0 -1
  282. package/dist/cdn/chunks/SavedBlocksPanels-B-ASmf1P.js.map +0 -1
  283. package/dist/cdn/chunks/SavedBlocksPickBar-CEFIQ8Nd.js.map +0 -1
  284. package/dist/cdn/chunks/TemplateScoringPanel-d_Ppowsq.js.map +0 -1
  285. package/dist/cdn/chunks/TemplateSettings-Dr5OJ0Gi.js.map +0 -1
  286. package/dist/cdn/chunks/TestEmailModal-CrjzVw8e.js.map +0 -1
  287. package/dist/cdn/chunks/TestEmailPanel-k9vdulTu.js.map +0 -1
  288. package/dist/cdn/chunks/TitleEditor-laILWfKh.js.map +0 -1
  289. package/dist/cdn/chunks/ToggleSwitch-0dGCYnRY.js.map +0 -1
  290. package/dist/cdn/chunks/Toolbar-BsiHW9gY.js.map +0 -1
  291. package/dist/cdn/chunks/TplModal-D8DoPbFa.js.map +0 -1
  292. package/dist/cdn/chunks/VersionHistoryMenu-B3-hNu9c.js.map +0 -1
  293. package/dist/cdn/chunks/VersionHistoryPanels-zw1AtzGN.js.map +0 -1
  294. package/dist/cdn/chunks/VersionPreviewBanner-PzVQSBJE.js.map +0 -1
  295. package/dist/cdn/chunks/WrapperBlock-CpRvfAV0.js.map +0 -1
  296. package/dist/cdn/chunks/blockTypeIcons-raBjStuv.js +0 -21
  297. package/dist/cdn/chunks/blockTypeIcons-raBjStuv.js.map +0 -1
  298. package/dist/cdn/chunks/ca-Ckgw9yd0.js.map +0 -1
  299. package/dist/cdn/chunks/cloud-CIT4yPp4.js.map +0 -1
  300. package/dist/cdn/chunks/createCloudRuntime-COUxbPvd.js.map +0 -1
  301. package/dist/cdn/chunks/de-D4dweQw8.js.map +0 -1
  302. package/dist/cdn/chunks/editor-modal-CKu8HhiB.js.map +0 -1
  303. package/dist/cdn/chunks/en-DqzAdO8F.js +0 -2
  304. package/dist/cdn/chunks/en-VE7R7NyL.js.map +0 -1
  305. package/dist/cdn/chunks/es-B9OhP8H3.js.map +0 -1
  306. package/dist/cdn/chunks/extensions-CafoGF65.js.map +0 -1
  307. package/dist/cdn/chunks/fr-wrXhIi00.js.map +0 -1
  308. package/dist/cdn/chunks/icons-BKkJ-5xF.js.map +0 -1
  309. package/dist/cdn/chunks/ja-fKnVWXHC.js.map +0 -1
  310. package/dist/cdn/chunks/nl-DeFatKJe.js.map +0 -1
  311. package/dist/cdn/chunks/preRenderCustomBlocks-Dpj-_cOj.js.map +0 -1
  312. package/dist/cdn/chunks/pt-BR-Bh1J8ey6.js.map +0 -1
  313. package/dist/cdn/chunks/socialIcons-BvYNMn1G.js.map +0 -1
  314. package/dist/cdn/chunks/src-Cnc5E71L.js.map +0 -1
  315. package/dist/cdn/chunks/src-DveVYoWS.js.map +0 -1
  316. package/dist/cdn/chunks/src-L6HEdwFn.js.map +0 -1
  317. package/dist/cdn/chunks/tiptap-DhLuKyDc.js.map +0 -1
  318. package/dist/cdn/chunks/useEditorCore-DZmtKwJh.js.map +0 -1
  319. package/dist/cdn/chunks/useLogicTag-DF7-l66G.js.map +0 -1
  320. package/dist/cdn/chunks/useMergeTag-DbIHw4NG.js.map +0 -1
  321. package/dist/cdn/chunks/usePopoverPosition-BJRvAb-h.js.map +0 -1
  322. package/dist/en-1tgLzB8q.js +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogicTagInsertButton-CItDS_U1.js","names":["css","hueCss","saturationCss","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_unref","_normalizeStyle","_createVNode","_createCommentVNode","_createBlock","_Teleport","_Transition","_withCtx","_createTextVNode","_toDisplayString","_Fragment","_renderList","_withKeys","_withModifiers","_sfc_main","$emit","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_toDisplayString","_createCommentVNode","_Fragment","_renderList","_normalizeClass","_withModifiers","_createElementVNode","_sfc_main","MergeTagSuggestionList","_hoisted_1","_openBlock","_createElementBlock","_unref","_normalizeClass","_Fragment","_renderList","_withModifiers","_toDisplayString","_createElementVNode","_createVNode","_sfc_main","$emit","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_unref","_withModifiers","_createVNode","_createTextVNode","_toDisplayString","_sfc_main","$emit","_openBlock","_createElementBlock","_normalizeClass","_unref","_withModifiers","_createVNode","_createTextVNode","_toDisplayString","_sfc_main","inject","renderList","withCtx","getCurrentScope","openBlock","ref","defineComponent","onScopeDispose","unref","createVNode","withKeys","normalizeClass","Teleport","computed","createElementBlock","nextTick","withModifiers","normalizeStyle","createBlock","Transition","createBaseVNode","isRef","createApp","Fragment","toDisplayString","h","createTextVNode","getLogicMergeTagKeyword","getSyntaxClosingChar","isLogicMergeTagValue","getSyntaxTriggerChar","onClickOutside","THEME_STYLES_KEY","TRANSLATIONS_KEY","UI_THEME_KEY","COLORS_KEY","useI18n","opaqueHex","normalizeColorToHex","usePopoverRoot","DEFAULT_RESOLVED_COLORS","canonicalizeHexColor","usePopoverPosition","ScanLine","X","Pencil","Braces","useMergeTag","useLogicTag","colorTextClass","clamp","number","max","min","round","digits","base","hexToHsva","rgbaToHsva","hexToRgba","hex","hsvaToHex","rgbaToHex","hsvaToRgba","hsva","hsvaToHsla","hh","s","v","a","hsvaToHslString","l","b","c","d","module","format","alphaHex","r","g","delta","equalColorObjects","first","second","prop","equalHex","cache","tpl","template","html","fire","type","hasTouched","isTouch","isValid","event","pointerMove","pointer","rect","target","keyMove","keyCode","Slider","part","aria","el","root","xy","toggleEvent","state","p","style","i","Hue","key","offset","Saturation","color_picker_default","hue_default","saturation_default","$isSame","$color","$hsva","$update","$parts","$css","$sliders","ColorPicker","newColor","newHsva","slider","value","\"color\"","color","newVal","oldHsva","colorModel","HexBase","_hoisted_1$4","_hoisted_2$2","_hoisted_3$2","_hoisted_4$2","_hoisted_5$2","_hoisted_6$1","_hoisted_7$1","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","ColorPicker_default","__emit","props","__props","emit","open","popoverRef","swatchRef","pickerTouched","isUnset","isTransparent","displayValue","seed","showClear","editorColors","presets","allowCustom","hasPresets","showFreeform","normalizedValue","isPresetSelected","preset","selectPreset","showNoneChip","presetPosition","index","rovingIndex","selected","noneChipTabindex","presetTabindex","onPresetGridKeydown","radios","current","currentIndex","onPickerChange","onPickerPointerDown","onPickerPointerUp","onTextInput","clear","selectTransparent","popoverRoot","themeStyles","tplUiTheme","popoverPosition","toggleOpen","viewportTop","toLocal","t","createCommentVNode","_cache","$event","_hoisted_1$3","_hoisted_2$1","_hoisted_3$1","_hoisted_4$1","_hoisted_5$1","MergeTagSuggestionList_default","optionId","item","_ctx","MAX_RESULTS","POPUP_ID_SEQ","filterMergeTags","trimmed","query","tags","label","tag","handleSuggestionKeyDown","items","selectedIndex","onSelect","createMergeTagPopup","app","container","editableEl","itemsRef","currentCommand","listId","latestClientRect","scrollTargets","pendingRaf","reposition","repositionAfterPaint","collectScrollAncestors","result","node","overflow","attachScrollListeners","viewDom","detachScrollListeners","position","origin","originTop","popupHeight","flippedTop","applyThemeContext","themeRoot","editorEl","themeValue","setEditableAria","active","setActiveDescendant","select","onCommand","anchorEl","popoverRootRef","getRect","handled","findOpenMergeTagTrigger","triggerChar","before","caret","triggerStart","closingChar","MIRRORED_PROPERTIES","toRect","left","bottom","top","getCaretRect","doc","win","isInput","mirror","source","head","marker","markerTop","markerLeft","elRect","borderTop","borderLeft","lineHeight","CARET_MOVE_KEYS","useMergeTagAutocomplete","options","syntax","available","enabled","mergeTags","injectedTranslations","popup","disposed","currentMatch","applySelection","modelValue","afterCaret","match","trailing","after","newValue","newPos","onInsert","elementRef","refresh","handleKeydown","useMergeTagField","isRequestingMergeTag","isRequestingLogicTag","isEditing","insertingMergeTag","typeahead","autocomplete","segments","val","combinedSource","regex","lastIndex","matched","isMergeTagValue","getMergeTagDisplayLabel","hasMergeTags","startEditing","len","stopEditing","handleInput","handleClick","handleBlur","clearValue","insertMergeTag","cursorPos","mergeTag","requestMergeTag","replaceMergeTagAt","snapshot","replacement","findMergeTag","segment","insertLogicTag","start","end","requestLogicTag","_hoisted_1$2","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","MergeTagSegments_default","onEdit","seg","canRepickMergeTag","showRawValue","\"border\"","_hoisted_1$1","mergeTagBtnClass","MergeTagInsertButton_default","_hoisted_1","logicBtnClass","LogicTagInsertButton_default"],"sources":["../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/math.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/convert.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/compare.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/dom.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/slider.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/hue.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/saturation.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/color-picker.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/hue.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/saturation.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/color-picker.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/entrypoints/hex.js","../../../../../node_modules/.pnpm/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/hex-color-picker.js","../../../src/components/ColorPicker.vue","../../../src/components/ColorPicker.vue","../../../src/components/MergeTagSuggestionList.vue","../../../src/components/MergeTagSuggestionList.vue","../../../src/utils/mergeTagPopup.ts","../../../src/utils/mergeTagTrigger.ts","../../../src/utils/textFieldCaret.ts","../../../src/composables/useMergeTagAutocomplete.ts","../../../src/composables/useMergeTagField.ts","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/LogicTagInsertButton.vue","../../../src/components/LogicTagInsertButton.vue"],"sourcesContent":["// Clamps a value between an upper and lower bound.\n// We use ternary operators because it makes the minified code\n// 2 times shorter then `Math.min(Math.max(a,b),c)`\nexport const clamp = (number, min = 0, max = 1) => {\n return number > max ? max : number < min ? min : number;\n};\nexport const round = (number, digits = 0, base = Math.pow(10, digits)) => {\n return Math.round(base * number) / base;\n};\n//# sourceMappingURL=math.js.map","import { round } from './math.js';\n/**\n * Valid CSS <angle> units.\n * https://developer.mozilla.org/en-US/docs/Web/CSS/angle\n */\nconst angleUnits = {\n grad: 360 / 400,\n turn: 360,\n rad: 360 / (Math.PI * 2)\n};\nexport const hexToHsva = (hex) => rgbaToHsva(hexToRgba(hex));\nexport const hexToRgba = (hex) => {\n if (hex[0] === '#')\n hex = hex.substring(1);\n if (hex.length < 6) {\n return {\n r: parseInt(hex[0] + hex[0], 16),\n g: parseInt(hex[1] + hex[1], 16),\n b: parseInt(hex[2] + hex[2], 16),\n a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1\n };\n }\n return {\n r: parseInt(hex.substring(0, 2), 16),\n g: parseInt(hex.substring(2, 4), 16),\n b: parseInt(hex.substring(4, 6), 16),\n a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1\n };\n};\nexport const parseHue = (value, unit = 'deg') => {\n return Number(value) * (angleUnits[unit] || 1);\n};\nexport const hslaStringToHsva = (hslString) => {\n const matcher = /hsla?\\(?\\s*(-?\\d*\\.?\\d+)(deg|rad|grad|turn)?[,\\s]+(-?\\d*\\.?\\d+)%?[,\\s]+(-?\\d*\\.?\\d+)%?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(hslString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return hslaToHsva({\n h: parseHue(match[1], match[2]),\n s: Number(match[3]),\n l: Number(match[4]),\n a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1)\n });\n};\nexport const hslStringToHsva = hslaStringToHsva;\nexport const hslaToHsva = ({ h, s, l, a }) => {\n s *= (l < 50 ? l : 100 - l) / 100;\n return {\n h: h,\n s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0,\n v: l + s,\n a\n };\n};\nexport const hsvaToHex = (hsva) => rgbaToHex(hsvaToRgba(hsva));\nexport const hsvaToHsla = ({ h, s, v, a }) => {\n const hh = ((200 - s) * v) / 100;\n return {\n h: round(h),\n s: round(hh > 0 && hh < 200 ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 : 0),\n l: round(hh / 2),\n a: round(a, 2)\n };\n};\nexport const hsvaToHsvString = (hsva) => {\n const { h, s, v } = roundHsva(hsva);\n return `hsv(${h}, ${s}%, ${v}%)`;\n};\nexport const hsvaToHsvaString = (hsva) => {\n const { h, s, v, a } = roundHsva(hsva);\n return `hsva(${h}, ${s}%, ${v}%, ${a})`;\n};\nexport const hsvaToHslString = (hsva) => {\n const { h, s, l } = hsvaToHsla(hsva);\n return `hsl(${h}, ${s}%, ${l}%)`;\n};\nexport const hsvaToHslaString = (hsva) => {\n const { h, s, l, a } = hsvaToHsla(hsva);\n return `hsla(${h}, ${s}%, ${l}%, ${a})`;\n};\nexport const hsvaToRgba = ({ h, s, v, a }) => {\n h = (h / 360) * 6;\n s = s / 100;\n v = v / 100;\n const hh = Math.floor(h), b = v * (1 - s), c = v * (1 - (h - hh) * s), d = v * (1 - (1 - h + hh) * s), module = hh % 6;\n return {\n r: round([v, c, b, b, d, v][module] * 255),\n g: round([d, v, v, c, b, b][module] * 255),\n b: round([b, b, d, v, v, c][module] * 255),\n a: round(a, 2)\n };\n};\nexport const hsvaToRgbString = (hsva) => {\n const { r, g, b } = hsvaToRgba(hsva);\n return `rgb(${r}, ${g}, ${b})`;\n};\nexport const hsvaToRgbaString = (hsva) => {\n const { r, g, b, a } = hsvaToRgba(hsva);\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n};\nexport const hsvaStringToHsva = (hsvString) => {\n const matcher = /hsva?\\(?\\s*(-?\\d*\\.?\\d+)(deg|rad|grad|turn)?[,\\s]+(-?\\d*\\.?\\d+)%?[,\\s]+(-?\\d*\\.?\\d+)%?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(hsvString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return roundHsva({\n h: parseHue(match[1], match[2]),\n s: Number(match[3]),\n v: Number(match[4]),\n a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1)\n });\n};\nexport const hsvStringToHsva = hsvaStringToHsva;\nexport const rgbaStringToHsva = (rgbaString) => {\n const matcher = /rgba?\\(?\\s*(-?\\d*\\.?\\d+)(%)?[,\\s]+(-?\\d*\\.?\\d+)(%)?[,\\s]+(-?\\d*\\.?\\d+)(%)?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(rgbaString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return rgbaToHsva({\n r: Number(match[1]) / (match[2] ? 100 / 255 : 1),\n g: Number(match[3]) / (match[4] ? 100 / 255 : 1),\n b: Number(match[5]) / (match[6] ? 100 / 255 : 1),\n a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1)\n });\n};\nexport const rgbStringToHsva = rgbaStringToHsva;\nconst format = (number) => {\n const hex = number.toString(16);\n return hex.length < 2 ? '0' + hex : hex;\n};\nexport const rgbaToHex = ({ r, g, b, a }) => {\n const alphaHex = a < 1 ? format(round(a * 255)) : '';\n return '#' + format(r) + format(g) + format(b) + alphaHex;\n};\nexport const rgbaToHsva = ({ r, g, b, a }) => {\n const max = Math.max(r, g, b);\n const delta = max - Math.min(r, g, b);\n // prettier-ignore\n const hh = delta\n ? max === r\n ? (g - b) / delta\n : max === g\n ? 2 + (b - r) / delta\n : 4 + (r - g) / delta\n : 0;\n return {\n h: round(60 * (hh < 0 ? hh + 6 : hh)),\n s: round(max ? (delta / max) * 100 : 0),\n v: round((max / 255) * 100),\n a\n };\n};\nexport const roundHsva = (hsva) => ({\n h: round(hsva.h),\n s: round(hsva.s),\n v: round(hsva.v),\n a: round(hsva.a, 2)\n});\nexport const rgbaToRgb = ({ r, g, b }) => ({ r, g, b });\nexport const hslaToHsl = ({ h, s, l }) => ({ h, s, l });\nexport const hsvaToHsv = (hsva) => {\n const { h, s, v } = roundHsva(hsva);\n return { h, s, v };\n};\n//# sourceMappingURL=convert.js.map","import { hexToRgba } from './convert.js';\nexport const equalColorObjects = (first, second) => {\n if (first === second)\n return true;\n for (const prop in first) {\n // The following allows for a type-safe calling of this function (first & second have to be HSL, HSV, or RGB)\n // with type-unsafe iterating over object keys. TS does not allow this without an index (`[key: string]: number`)\n // on an object to define how iteration is normally done. To ensure extra keys are not allowed on our types,\n // we must cast our object to unknown (as RGB demands `r` be a key, while `Record<string, x>` does not care if\n // there is or not), and then as a type TS can iterate over.\n if (first[prop] !==\n second[prop])\n return false;\n }\n return true;\n};\nexport const equalColorString = (first, second) => {\n return first.replace(/\\s/g, '') === second.replace(/\\s/g, '');\n};\nexport const equalHex = (first, second) => {\n if (first.toLowerCase() === second.toLowerCase())\n return true;\n // To compare colors like `#FFF` and `ffffff` we convert them into RGB objects\n return equalColorObjects(hexToRgba(first), hexToRgba(second));\n};\n//# sourceMappingURL=compare.js.map","const cache = {};\nexport const tpl = (html) => {\n let template = cache[html];\n if (!template) {\n template = document.createElement('template');\n template.innerHTML = html;\n cache[html] = template;\n }\n return template;\n};\nexport const fire = (target, type, detail) => {\n target.dispatchEvent(new CustomEvent(type, {\n bubbles: true,\n detail\n }));\n};\n//# sourceMappingURL=dom.js.map","import { fire, tpl } from '../utils/dom.js';\nimport { clamp } from '../utils/math.js';\nlet hasTouched = false;\n// Check if an event was triggered by touch\nconst isTouch = (e) => 'touches' in e;\n// Prevent mobile browsers from handling mouse events (conflicting with touch ones).\n// If we detected a touch interaction before, we prefer reacting to touch events only.\nconst isValid = (event) => {\n if (hasTouched && !isTouch(event))\n return false;\n if (!hasTouched)\n hasTouched = isTouch(event);\n return true;\n};\nconst pointerMove = (target, event) => {\n const pointer = isTouch(event) ? event.touches[0] : event;\n const rect = target.el.getBoundingClientRect();\n fire(target.el, 'move', target.getMove({\n x: clamp((pointer.pageX - (rect.left + window.pageXOffset)) / rect.width),\n y: clamp((pointer.pageY - (rect.top + window.pageYOffset)) / rect.height)\n }));\n};\nconst keyMove = (target, event) => {\n // We use `keyCode` instead of `key` to reduce the size of the library.\n const keyCode = event.keyCode;\n // Ignore all keys except arrow ones, Page Up, Page Down, Home and End.\n if (keyCode > 40 || (target.xy && keyCode < 37) || keyCode < 33)\n return;\n // Do not scroll page by keys when color picker element has focus.\n event.preventDefault();\n // Send relative offset to the parent component.\n fire(target.el, 'move', target.getMove({\n x: keyCode === 39 // Arrow Right\n ? 0.01\n : keyCode === 37 // Arrow Left\n ? -0.01\n : keyCode === 34 // Page Down\n ? 0.05\n : keyCode === 33 // Page Up\n ? -0.05\n : keyCode === 35 // End\n ? 1\n : keyCode === 36 // Home\n ? -1\n : 0,\n y: keyCode === 40 // Arrow down\n ? 0.01\n : keyCode === 38 // Arrow Up\n ? -0.01\n : 0\n }, true));\n};\nexport class Slider {\n constructor(root, part, aria, xy) {\n const template = tpl(`<div role=\"slider\" tabindex=\"0\" part=\"${part}\" ${aria}><div part=\"${part}-pointer\"></div></div>`);\n root.appendChild(template.content.cloneNode(true));\n const el = root.querySelector(`[part=${part}]`);\n el.addEventListener('mousedown', this);\n el.addEventListener('touchstart', this);\n el.addEventListener('keydown', this);\n this.el = el;\n this.xy = xy;\n this.nodes = [el.firstChild, el];\n }\n set dragging(state) {\n const toggleEvent = state ? document.addEventListener : document.removeEventListener;\n toggleEvent(hasTouched ? 'touchmove' : 'mousemove', this);\n toggleEvent(hasTouched ? 'touchend' : 'mouseup', this);\n }\n handleEvent(event) {\n switch (event.type) {\n case 'mousedown':\n case 'touchstart':\n event.preventDefault();\n // event.button is 0 in mousedown for left button activation\n if (!isValid(event) || (!hasTouched && event.button != 0))\n return;\n this.el.focus();\n pointerMove(this, event);\n this.dragging = true;\n break;\n case 'mousemove':\n case 'touchmove':\n event.preventDefault();\n pointerMove(this, event);\n break;\n case 'mouseup':\n case 'touchend':\n this.dragging = false;\n break;\n case 'keydown':\n keyMove(this, event);\n break;\n }\n }\n style(styles) {\n styles.forEach((style, i) => {\n for (const p in style) {\n this.nodes[i].style.setProperty(p, style[p]);\n }\n });\n }\n}\n//# sourceMappingURL=slider.js.map","import { Slider } from './slider.js';\nimport { hsvaToHslString } from '../utils/convert.js';\nimport { clamp, round } from '../utils/math.js';\nexport class Hue extends Slider {\n constructor(root) {\n super(root, 'hue', 'aria-label=\"Hue\" aria-valuemin=\"0\" aria-valuemax=\"360\"', false);\n }\n update({ h }) {\n this.h = h;\n this.style([\n {\n left: `${(h / 360) * 100}%`,\n color: hsvaToHslString({ h, s: 100, v: 100, a: 1 })\n }\n ]);\n this.el.setAttribute('aria-valuenow', `${round(h)}`);\n }\n getMove(offset, key) {\n // Hue measured in degrees of the color circle ranging from 0 to 360\n return { h: key ? clamp(this.h + offset.x * 360, 0, 360) : 360 * offset.x };\n }\n}\n//# sourceMappingURL=hue.js.map","import { Slider } from './slider.js';\nimport { hsvaToHslString } from '../utils/convert.js';\nimport { clamp, round } from '../utils/math.js';\nexport class Saturation extends Slider {\n constructor(root) {\n super(root, 'saturation', 'aria-label=\"Color\"', true);\n }\n update(hsva) {\n this.hsva = hsva;\n this.style([\n {\n top: `${100 - hsva.v}%`,\n left: `${hsva.s}%`,\n color: hsvaToHslString(hsva)\n },\n {\n 'background-color': hsvaToHslString({ h: hsva.h, s: 100, v: 100, a: 1 })\n }\n ]);\n this.el.setAttribute('aria-valuetext', `Saturation ${round(hsva.s)}%, Brightness ${round(hsva.v)}%`);\n }\n getMove(offset, key) {\n // Saturation and brightness always fit into [0, 100] range\n return {\n s: key ? clamp(this.hsva.s + offset.x * 100, 0, 100) : offset.x * 100,\n v: key ? clamp(this.hsva.v - offset.y * 100, 0, 100) : Math.round(100 - offset.y * 100)\n };\n }\n}\n//# sourceMappingURL=saturation.js.map","export default `:host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:\"\";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}`;\n//# sourceMappingURL=color-picker.js.map","export default `[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}`;\n//# sourceMappingURL=hue.js.map","export default `[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}`;\n//# sourceMappingURL=saturation.js.map","import { equalColorObjects } from '../utils/compare.js';\nimport { fire, tpl } from '../utils/dom.js';\nimport { Hue } from './hue.js';\nimport { Saturation } from './saturation.js';\nimport css from '../styles/color-picker.js';\nimport hueCss from '../styles/hue.js';\nimport saturationCss from '../styles/saturation.js';\nconst $isSame = Symbol('same');\nconst $color = Symbol('color');\nconst $hsva = Symbol('hsva');\nconst $update = Symbol('update');\nconst $parts = Symbol('parts');\nexport const $css = Symbol('css');\nexport const $sliders = Symbol('sliders');\nexport class ColorPicker extends HTMLElement {\n static get observedAttributes() {\n return ['color'];\n }\n get [$css]() {\n return [css, hueCss, saturationCss];\n }\n get [$sliders]() {\n return [Saturation, Hue];\n }\n get color() {\n return this[$color];\n }\n set color(newColor) {\n if (!this[$isSame](newColor)) {\n const newHsva = this.colorModel.toHsva(newColor);\n this[$update](newHsva);\n this[$color] = newColor;\n }\n }\n constructor() {\n super();\n const template = tpl(`<style>${this[$css].join('')}</style>`);\n const root = this.attachShadow({ mode: 'open' });\n root.appendChild(template.content.cloneNode(true));\n root.addEventListener('move', this);\n this[$parts] = this[$sliders].map((slider) => new slider(root));\n }\n connectedCallback() {\n // A user may set a property on an _instance_ of an element,\n // before its prototype has been connected to this class.\n // If so, we need to run it through the proper class setter.\n if (this.hasOwnProperty('color')) {\n const value = this.color;\n delete this['color'];\n this.color = value;\n }\n else if (!this.color) {\n this.color = this.colorModel.defaultColor;\n }\n }\n attributeChangedCallback(_attr, _oldVal, newVal) {\n const color = this.colorModel.fromAttr(newVal);\n if (!this[$isSame](color)) {\n this.color = color;\n }\n }\n handleEvent(event) {\n // Merge the current HSV color object with updated params.\n const oldHsva = this[$hsva];\n const newHsva = { ...oldHsva, ...event.detail };\n this[$update](newHsva);\n let newColor;\n if (!equalColorObjects(newHsva, oldHsva) &&\n !this[$isSame]((newColor = this.colorModel.fromHsva(newHsva)))) {\n this[$color] = newColor;\n fire(this, 'color-changed', { value: newColor });\n }\n }\n [$isSame](color) {\n return this.color && this.colorModel.equal(color, this.color);\n }\n [$update](hsva) {\n this[$hsva] = hsva;\n this[$parts].forEach((part) => part.update(hsva));\n }\n}\n//# sourceMappingURL=color-picker.js.map","import { ColorPicker } from '../components/color-picker.js';\nimport { hexToHsva, hsvaToHex } from '../utils/convert.js';\nimport { equalHex } from '../utils/compare.js';\nconst colorModel = {\n defaultColor: '#000',\n toHsva: hexToHsva,\n fromHsva: ({ h, s, v }) => hsvaToHex({ h, s, v, a: 1 }),\n equal: equalHex,\n fromAttr: (color) => color\n};\nexport class HexBase extends ColorPicker {\n get colorModel() {\n return colorModel;\n }\n}\n//# sourceMappingURL=hex.js.map","import { HexBase } from './lib/entrypoints/hex.js';\n/**\n * A color picker custom element that uses HEX format.\n *\n * @element hex-color-picker\n *\n * @prop {string} color - Selected color in HEX format.\n * @attr {string} color - Selected color in HEX format.\n *\n * @fires color-changed - Event fired when color property changes.\n *\n * @csspart hue - A hue selector container.\n * @csspart saturation - A saturation selector container\n * @csspart hue-pointer - A hue pointer element.\n * @csspart saturation-pointer - A saturation pointer element.\n */\nexport class HexColorPicker extends HexBase {\n}\ncustomElements.define('hex-color-picker', HexColorPicker);\n//# sourceMappingURL=hex-color-picker.js.map","<script setup lang=\"ts\">\nimport { computed, inject, ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { X } from \"@lucide/vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { usePopoverPosition } from \"../composables/usePopoverPosition\";\nimport { colorTextClass } from \"../constants/styleConstants\";\nimport {\n canonicalizeHexColor,\n normalizeColorToHex,\n opaqueHex,\n} from \"../utils/color\";\nimport { COLORS_KEY, THEME_STYLES_KEY, UI_THEME_KEY } from \"../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../utils/resolveColorsConfig\";\nimport \"vanilla-colorful\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n /**\n * Color the picker wheel opens on while the field is unset. It only\n * positions the wheel — it never paints the swatch or fills the input,\n * so an unset field still reads as \"not set\".\n */\n seedColor?: string;\n swatchOnly?: boolean;\n disabled?: boolean;\n /**\n * Swatch trigger size. `\"md\"` (40px) is the default for sidebar panels;\n * `\"sm\"` (32px) matches the compact rich-text floating toolbar controls.\n */\n size?: \"sm\" | \"md\";\n /**\n * Accessible label (and hover title) for the swatch trigger. Falls back to\n * the generic \"pick color\" label. Set it when several pickers sit side by\n * side and must be told apart (e.g. text color vs highlight in the toolbar).\n */\n ariaLabel?: string;\n /**\n * Per-instance override of the editor-wide `colors.presets` for this one\n * picker. This is the extension seam a caller uses to scope a single picker\n * to its own palette; when omitted, the injected editor-level presets apply.\n */\n presets?: string[];\n /**\n * Per-instance override of the editor-wide `colors.allowCustom` for this one\n * picker. When omitted, the injected editor-level setting applies.\n */\n allowCustom?: boolean;\n /**\n * Offer the keyword `transparent` (a checker swatch). Background fills only:\n * text, borders, and links stay opaque. Clear still writes `\"\"`.\n */\n allowTransparent?: boolean;\n }>(),\n {\n placeholder: \"\",\n seedColor: \"#ffffff\",\n swatchOnly: false,\n disabled: false,\n size: \"md\",\n ariaLabel: \"\",\n presets: undefined,\n allowCustom: undefined,\n allowTransparent: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst popoverRef = ref<HTMLElement>();\nconst swatchRef = ref<HTMLElement>();\n\n// Whether the picker emitted a real change during the current pointer gesture.\n// vanilla-colorful only fires `color-changed` when the chosen color differs\n// from the one the wheel was seeded with — so picking a color equal to the\n// seed emits nothing. When the field is still unset after such a no-change\n// pick, we commit the seed ourselves on pointerup so the value persists\n// (otherwise e.g. white on an empty background can never be set — issue #282).\nconst pickerTouched = ref(false);\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [swatchRef] },\n);\n\nconst isUnset = computed(() => !props.modelValue);\n\nconst isTransparent = computed(\n () => props.modelValue.trim().toLowerCase() === \"transparent\",\n);\n\n// vanilla-colorful and the hex field are hex-only, but a stored value read back\n// from the DOM (e.g. a TipTap textStyle/highlight color) can be `rgb(...)`. Show\n// and seed the wheel with the hex form; the raw value still drives unset/emit.\n// A partial-alpha `rgba()` stays in the field — the wheel is positioned on its\n// RGB channels and does not write that hex back until the user picks.\nconst displayValue = computed(() => normalizeColorToHex(props.modelValue));\n\nconst seed = computed(() => {\n const fromValue = props.modelValue ? opaqueHex(props.modelValue) : null;\n if (fromValue) return fromValue;\n return opaqueHex(props.seedColor) ?? \"#ffffff\";\n});\n\n// The clear (×) sits inside whichever hex field is active — the inline field in\n// full mode, or the popover field in swatch-only mode — shown only when a value\n// is set. Not gated on swatchOnly: each field renders only in its own mode.\nconst showClear = computed(() => !props.disabled && !isUnset.value);\n\n// Color palette: a per-instance `presets`/`allowCustom` prop wins over the\n// injected editor-wide `colors` config, per property. Default keeps the picker\n// unchanged when neither is configured. \"preset\" is kept distinct from the\n// trigger \"swatch\" (swatchOnly/swatchRef) on purpose — they are two different\n// concepts in this file.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\n// Deduplicated: a repeated entry in the configured palette would render a\n// pointless second chip and collide on the value-based `v-for` key.\nconst presets = computed(() => [\n ...new Set(props.presets ?? editorColors.presets),\n]);\nconst allowCustom = computed(\n () => props.allowCustom ?? editorColors.allowCustom,\n);\nconst hasPresets = computed(() => presets.value.length > 0);\n\n// Show the free-form controls (wheel + hex inputs) whenever custom entry is\n// allowed OR there are no presets to fall back on — so a picker is never left\n// with no way to choose a color. `resolveColorsConfig` already forbids\n// `allowCustom: false` without presets at the editor level; a custom-block\n// color field locking itself with no palette anywhere (`resolveFieldColors`)\n// relies on this guard instead.\nconst showFreeform = computed(() => allowCustom.value || !hasPresets.value);\n\n// A preset reads as selected when its hex equals the current value, compared in\n// the canonical (rgb→hex, 3-digit expanded, lowercased) form — so a `#abc`\n// preset matches an `#aabbcc` browser round-trip and case never matters.\nconst normalizedValue = computed(() => canonicalizeHexColor(props.modelValue));\nfunction isPresetSelected(preset: string): boolean {\n return (\n normalizedValue.value !== \"\" &&\n canonicalizeHexColor(preset) === normalizedValue.value\n );\n}\nfunction selectPreset(preset: string): void {\n emit(\"update:modelValue\", preset);\n}\n\n// A leading \"no colour\" (unset/inherit) chip is rendered ONLY in locked mode\n// (`!showFreeform`) — the one mode with no other clear affordance. The freeform\n// modes already own clear via the hex-field ×; a preset-only grid otherwise has\n// no way back to unset once a colour is picked.\nconst showNoneChip = computed(() => hasPresets.value && !showFreeform.value);\n\n// Group-position of preset `index`, shifted by the leading none chip when it's\n// present, so the whole grid indexes as one radio list.\nfunction presetPosition(index: number): number {\n return showNoneChip.value ? index + 1 : index;\n}\n\n// Roving tabindex (WAI-ARIA APG radio group): the grid exposes exactly one tab\n// stop — the checked radio, or the FIRST radio when nothing is checked (value\n// unset with no none chip, or off-palette) — so the group is reachable by Tab\n// but a single Tab steps past it. The none chip counts as checked when the\n// value is unset. Arrow keys then rove focus between the radios.\nconst rovingIndex = computed(() => {\n if (showNoneChip.value && isUnset.value) {\n return 0;\n }\n const selected = presets.value.findIndex((preset) =>\n isPresetSelected(preset),\n );\n return selected === -1 ? 0 : presetPosition(selected);\n});\nfunction noneChipTabindex(): number {\n return rovingIndex.value === 0 ? 0 : -1;\n}\nfunction presetTabindex(index: number): number {\n return presetPosition(index) === rovingIndex.value ? 0 : -1;\n}\n\n// Arrow keys rove FOCUS across the chips (wrapping); they do NOT select.\n// Enter/Space activate the focused chip via native button click. This\n// deviates from APG's default \"selection follows focus\" on purpose: every\n// selection emits `update:modelValue`, and on the rich-text toolbar path that\n// runs `editor.chain().focus().setColor()` (see the swatch-only field comment\n// in the template), which refocuses the canvas — so following focus would\n// steal focus after the first arrow key and break traversal. APG sanctions\n// explicit activation when moving focus has side effects.\nfunction onPresetGridKeydown(e: KeyboardEvent): void {\n const arrows = [\"ArrowRight\", \"ArrowDown\", \"ArrowLeft\", \"ArrowUp\"];\n if (!arrows.includes(e.key)) {\n return;\n }\n // Resolve the current chip from `event.target`, never `document.activeElement`\n // — the latter is unreliable across a shadow-DOM boundary (this editor can\n // mount in a shadow root; the repo threads the real root via EDITOR_ROOT_KEY\n // for the same reason).\n const group = e.currentTarget as HTMLElement;\n const radios = Array.from(\n group.querySelectorAll<HTMLElement>('[role=\"radio\"]'),\n );\n const current = (e.target as HTMLElement).closest('[role=\"radio\"]');\n const currentIndex = current ? radios.indexOf(current as HTMLElement) : -1;\n if (currentIndex === -1) {\n return;\n }\n const forward = e.key === \"ArrowRight\" || e.key === \"ArrowDown\";\n const nextIndex =\n (currentIndex + (forward ? 1 : -1) + radios.length) % radios.length;\n radios[nextIndex]?.focus();\n e.preventDefault();\n}\n\nfunction onPickerChange(e: Event): void {\n pickerTouched.value = true;\n emit(\"update:modelValue\", (e as CustomEvent).detail.value);\n}\n\nfunction onPickerPointerDown(): void {\n pickerTouched.value = false;\n}\n\nfunction onPickerPointerUp(): void {\n // A deliberate pick equal to the seed fires no `color-changed`; commit it.\n if (!pickerTouched.value && isUnset.value) {\n emit(\"update:modelValue\", seed.value);\n }\n}\n\nfunction onTextInput(e: Event): void {\n emit(\"update:modelValue\", (e.target as HTMLInputElement).value);\n}\n\nfunction clear(): void {\n emit(\"update:modelValue\", \"\");\n}\n\nfunction selectTransparent(): void {\n emit(\"update:modelValue\", \"transparent\");\n}\n\n// The popover teleports to the shared popover root so it escapes any clipping /\n// overflow-hidden ancestor (e.g. the link dialog's card, where clicking the\n// clipped wheel landed on the backdrop and closed the modal). Position is\n// captured from the swatch on open and applied as `absolute`, in coordinates\n// local to the popover root (`toLocal`) so a transformed ancestor of the\n// editor doesn't offset it — see usePopoverPosition.\nconst popoverRoot = usePopoverRoot();\nconst { toLocal } = usePopoverPosition();\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverPosition = ref({ top: 0, left: 0 });\n\nfunction toggleOpen(): void {\n if (props.disabled) return;\n if (!open.value) {\n const rect = swatchRef.value?.getBoundingClientRect();\n if (rect) {\n const PICKER_HEIGHT = 240; // wheel + hue slider + padding, approx\n const gap = 8;\n // Open below the swatch, or flip above when it would overflow the viewport\n // bottom — the popover is `fixed`, so it can't scroll into view otherwise.\n const fitsBelow = rect.bottom + gap + PICKER_HEIGHT <= window.innerHeight;\n const viewportTop = fitsBelow\n ? rect.bottom + gap\n : Math.max(gap, rect.top - PICKER_HEIGHT - gap);\n popoverPosition.value = toLocal({ top: viewportTop, left: rect.left });\n }\n }\n open.value = !open.value;\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'tpl:flex tpl:items-center tpl:gap-2 tpl:relative',\n disabled && 'tpl:opacity-60 tpl:cursor-not-allowed',\n ]\"\n >\n <button\n ref=\"swatchRef\"\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel || t.colorPicker.pickColor\"\n :title=\"ariaLabel || undefined\"\n :aria-expanded=\"open\"\n :class=\"[\n 'tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-0.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n open\n ? 'tpl:border-[var(--tpl-primary)] tpl:shadow-[var(--tpl-ring)]'\n : !disabled && 'tpl:hover:border-[var(--tpl-text-dim)]',\n size === 'sm' ? 'tpl:size-8' : 'tpl:size-10',\n ]\"\n @click=\"toggleOpen\"\n >\n <span\n class=\"tpl:block tpl:size-full tpl:rounded-[calc(var(--tpl-radius-sm)-2px)]\"\n :class=\"{\n 'tpl-color-swatch-empty': isUnset,\n 'tpl-color-swatch-transparent': isTransparent,\n }\"\n :style=\"\n isUnset || isTransparent\n ? undefined\n : { backgroundColor: displayValue }\n \"\n />\n </button>\n <div v-if=\"!swatchOnly && showFreeform\" class=\"tpl:relative tpl:flex-1\">\n <input\n type=\"text\"\n :class=\"[colorTextClass, 'tpl:w-full']\"\n :style=\"showClear ? { paddingRight: '2.25rem' } : undefined\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || t.colorPicker.notSet\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @input=\"onTextInput\"\n />\n <button\n v-if=\"showClear\"\n type=\"button\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n class=\"tpl:absolute tpl:right-2 tpl:top-1/2 tpl:flex tpl:size-6 tpl:-translate-y-1/2 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:text-[var(--tpl-text-dim)] tpl:transition-colors tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"clear\"\n >\n <X :size=\"14\" :stroke-width=\"1.5\" />\n </button>\n </div>\n <Teleport :to=\"popoverRoot\" :disabled=\"!popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n enter-from-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n enter-to-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-active-class=\"tpl:transition-all tpl:duration-[80ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n leave-from-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-to-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n >\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl-color-popover tpl:absolute tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:shadow-[var(--tpl-shadow-lg)]\"\n :style=\"{\n top: `${popoverPosition.top}px`,\n left: `${popoverPosition.left}px`,\n ...themeStyles,\n }\"\n >\n <button\n v-if=\"allowTransparent\"\n type=\"button\"\n data-testid=\"color-picker-transparent\"\n :aria-pressed=\"isTransparent\"\n :aria-label=\"t.colorPicker.transparent\"\n :title=\"t.colorPicker.transparent\"\n :class=\"[\n 'tpl:mb-2 tpl:flex tpl:w-full tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:py-1 tpl:text-left tpl:text-xs tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)]',\n isTransparent\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"selectTransparent\"\n >\n <span\n class=\"tpl-color-swatch-transparent tpl:size-4 tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)]\"\n />\n {{ t.colorPicker.transparent }}\n </button>\n <!-- Preset color grid — an ARIA radio group: each chip is a\n `role=\"radio\"`, arrow keys rove focus between them (roving\n tabindex), and Enter/Space activate. Supplements the wheel/hex\n field; the only control when `allowCustom` is false. Each preset\n carries its color value as its accessible label. -->\n <div\n v-if=\"hasPresets\"\n role=\"radiogroup\"\n :aria-label=\"t.colorPicker.presetColors\"\n :class=\"[\n 'tpl:flex tpl:flex-wrap tpl:gap-1.5',\n showFreeform && 'tpl:mb-2',\n ]\"\n @keydown=\"onPresetGridKeydown\"\n >\n <!-- Leading \"no colour\" (unset/inherit) chip — locked mode only.\n Always present, never gated on the current value: unset is a\n first-class member of the value domain, and a chip that\n disappeared on activation would strand keyboard focus mid-grid.\n Reuses the diagonal-slash unset visual and the same chip\n chrome; activating it emits \"\" to clear. -->\n <button\n v-if=\"showNoneChip\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n :aria-checked=\"isUnset\"\n :tabindex=\"noneChipTabindex()\"\n :class=\"[\n 'tpl-color-swatch-empty tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)] tpl:focus-visible:ring-offset-1 tpl:focus-visible:ring-offset-[var(--tpl-bg-elevated)]',\n isUnset\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"clear\"\n />\n <button\n v-for=\"(preset, index) in presets\"\n :key=\"preset\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"preset\"\n :title=\"preset\"\n :aria-checked=\"isPresetSelected(preset)\"\n :tabindex=\"presetTabindex(index)\"\n :style=\"{ backgroundColor: preset }\"\n :class=\"[\n 'tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)] tpl:focus-visible:ring-offset-1 tpl:focus-visible:ring-offset-[var(--tpl-bg-elevated)]',\n isPresetSelected(preset)\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"selectPreset(preset)\"\n />\n </div>\n <hex-color-picker\n v-if=\"showFreeform\"\n :color=\"seed\"\n :aria-label=\"t.colorPicker.pickColor\"\n @color-changed=\"onPickerChange\"\n @pointerdown=\"onPickerPointerDown\"\n @pointerup=\"onPickerPointerUp\"\n @keydown.escape=\"open = false\"\n />\n <!-- Swatch-only mode has no inline field beside the swatch, so the\n popover carries the manual hex field (+ inline × clear) — the same\n control as the full-mode sidebar field. It commits on change/Enter,\n NOT @input: in the rich-text toolbar every emit calls\n editor.chain().focus().setColor(), and focusing the canvas mid-type\n would steal focus after the first character. Shown even when unset\n so a first color can be typed; the × appears only once a value is set. -->\n <div v-if=\"swatchOnly && showFreeform\" class=\"tpl:relative tpl:mt-2\">\n <input\n type=\"text\"\n :class=\"[colorTextClass, 'tpl:w-full']\"\n :style=\"showClear ? { paddingRight: '2.25rem' } : undefined\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || t.colorPicker.notSet\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @change=\"onTextInput\"\n @keydown.enter.prevent=\"\n ($event.target as HTMLInputElement).blur()\n \"\n />\n <button\n v-if=\"showClear\"\n type=\"button\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n class=\"tpl:absolute tpl:right-2 tpl:top-1/2 tpl:flex tpl:size-6 tpl:-translate-y-1/2 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:text-[var(--tpl-text-dim)] tpl:transition-colors tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"clear\"\n >\n <X :size=\"14\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style>\nhex-color-picker {\n --hcp-width: 200px;\n}\n\nhex-color-picker::part(hue) {\n border-radius: var(--tpl-radius-sm, 7px);\n}\n\nhex-color-picker::part(saturation) {\n border-radius: var(--tpl-radius-sm, 7px) var(--tpl-radius-sm, 7px) 0 0;\n}\n\n/* Unset swatch: paper with a single thin diagonal slash = \"no color set\".\n A neutral non-color signal — never Signal Amber (intent) or Danger. */\n.tpl-color-swatch-empty {\n background-color: var(--tpl-bg);\n background-image: linear-gradient(\n to top right,\n transparent calc(50% - 0.75px),\n var(--tpl-text-dim) calc(50% - 0.75px),\n var(--tpl-text-dim) calc(50% + 0.75px),\n transparent calc(50% + 0.75px)\n );\n}\n\n/* Stored keyword `transparent`: a checker, distinct from the unset slash. */\n.tpl-color-swatch-transparent {\n background-color: var(--tpl-bg);\n background-image: repeating-conic-gradient(\n var(--tpl-border) 0% 25%,\n var(--tpl-bg) 0% 50%\n );\n background-size: 8px 8px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { X } from \"@lucide/vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { usePopoverPosition } from \"../composables/usePopoverPosition\";\nimport { colorTextClass } from \"../constants/styleConstants\";\nimport {\n canonicalizeHexColor,\n normalizeColorToHex,\n opaqueHex,\n} from \"../utils/color\";\nimport { COLORS_KEY, THEME_STYLES_KEY, UI_THEME_KEY } from \"../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../utils/resolveColorsConfig\";\nimport \"vanilla-colorful\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n /**\n * Color the picker wheel opens on while the field is unset. It only\n * positions the wheel — it never paints the swatch or fills the input,\n * so an unset field still reads as \"not set\".\n */\n seedColor?: string;\n swatchOnly?: boolean;\n disabled?: boolean;\n /**\n * Swatch trigger size. `\"md\"` (40px) is the default for sidebar panels;\n * `\"sm\"` (32px) matches the compact rich-text floating toolbar controls.\n */\n size?: \"sm\" | \"md\";\n /**\n * Accessible label (and hover title) for the swatch trigger. Falls back to\n * the generic \"pick color\" label. Set it when several pickers sit side by\n * side and must be told apart (e.g. text color vs highlight in the toolbar).\n */\n ariaLabel?: string;\n /**\n * Per-instance override of the editor-wide `colors.presets` for this one\n * picker. This is the extension seam a caller uses to scope a single picker\n * to its own palette; when omitted, the injected editor-level presets apply.\n */\n presets?: string[];\n /**\n * Per-instance override of the editor-wide `colors.allowCustom` for this one\n * picker. When omitted, the injected editor-level setting applies.\n */\n allowCustom?: boolean;\n /**\n * Offer the keyword `transparent` (a checker swatch). Background fills only:\n * text, borders, and links stay opaque. Clear still writes `\"\"`.\n */\n allowTransparent?: boolean;\n }>(),\n {\n placeholder: \"\",\n seedColor: \"#ffffff\",\n swatchOnly: false,\n disabled: false,\n size: \"md\",\n ariaLabel: \"\",\n presets: undefined,\n allowCustom: undefined,\n allowTransparent: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst popoverRef = ref<HTMLElement>();\nconst swatchRef = ref<HTMLElement>();\n\n// Whether the picker emitted a real change during the current pointer gesture.\n// vanilla-colorful only fires `color-changed` when the chosen color differs\n// from the one the wheel was seeded with — so picking a color equal to the\n// seed emits nothing. When the field is still unset after such a no-change\n// pick, we commit the seed ourselves on pointerup so the value persists\n// (otherwise e.g. white on an empty background can never be set — issue #282).\nconst pickerTouched = ref(false);\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [swatchRef] },\n);\n\nconst isUnset = computed(() => !props.modelValue);\n\nconst isTransparent = computed(\n () => props.modelValue.trim().toLowerCase() === \"transparent\",\n);\n\n// vanilla-colorful and the hex field are hex-only, but a stored value read back\n// from the DOM (e.g. a TipTap textStyle/highlight color) can be `rgb(...)`. Show\n// and seed the wheel with the hex form; the raw value still drives unset/emit.\n// A partial-alpha `rgba()` stays in the field — the wheel is positioned on its\n// RGB channels and does not write that hex back until the user picks.\nconst displayValue = computed(() => normalizeColorToHex(props.modelValue));\n\nconst seed = computed(() => {\n const fromValue = props.modelValue ? opaqueHex(props.modelValue) : null;\n if (fromValue) return fromValue;\n return opaqueHex(props.seedColor) ?? \"#ffffff\";\n});\n\n// The clear (×) sits inside whichever hex field is active — the inline field in\n// full mode, or the popover field in swatch-only mode — shown only when a value\n// is set. Not gated on swatchOnly: each field renders only in its own mode.\nconst showClear = computed(() => !props.disabled && !isUnset.value);\n\n// Color palette: a per-instance `presets`/`allowCustom` prop wins over the\n// injected editor-wide `colors` config, per property. Default keeps the picker\n// unchanged when neither is configured. \"preset\" is kept distinct from the\n// trigger \"swatch\" (swatchOnly/swatchRef) on purpose — they are two different\n// concepts in this file.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\n// Deduplicated: a repeated entry in the configured palette would render a\n// pointless second chip and collide on the value-based `v-for` key.\nconst presets = computed(() => [\n ...new Set(props.presets ?? editorColors.presets),\n]);\nconst allowCustom = computed(\n () => props.allowCustom ?? editorColors.allowCustom,\n);\nconst hasPresets = computed(() => presets.value.length > 0);\n\n// Show the free-form controls (wheel + hex inputs) whenever custom entry is\n// allowed OR there are no presets to fall back on — so a picker is never left\n// with no way to choose a color. `resolveColorsConfig` already forbids\n// `allowCustom: false` without presets at the editor level; a custom-block\n// color field locking itself with no palette anywhere (`resolveFieldColors`)\n// relies on this guard instead.\nconst showFreeform = computed(() => allowCustom.value || !hasPresets.value);\n\n// A preset reads as selected when its hex equals the current value, compared in\n// the canonical (rgb→hex, 3-digit expanded, lowercased) form — so a `#abc`\n// preset matches an `#aabbcc` browser round-trip and case never matters.\nconst normalizedValue = computed(() => canonicalizeHexColor(props.modelValue));\nfunction isPresetSelected(preset: string): boolean {\n return (\n normalizedValue.value !== \"\" &&\n canonicalizeHexColor(preset) === normalizedValue.value\n );\n}\nfunction selectPreset(preset: string): void {\n emit(\"update:modelValue\", preset);\n}\n\n// A leading \"no colour\" (unset/inherit) chip is rendered ONLY in locked mode\n// (`!showFreeform`) — the one mode with no other clear affordance. The freeform\n// modes already own clear via the hex-field ×; a preset-only grid otherwise has\n// no way back to unset once a colour is picked.\nconst showNoneChip = computed(() => hasPresets.value && !showFreeform.value);\n\n// Group-position of preset `index`, shifted by the leading none chip when it's\n// present, so the whole grid indexes as one radio list.\nfunction presetPosition(index: number): number {\n return showNoneChip.value ? index + 1 : index;\n}\n\n// Roving tabindex (WAI-ARIA APG radio group): the grid exposes exactly one tab\n// stop — the checked radio, or the FIRST radio when nothing is checked (value\n// unset with no none chip, or off-palette) — so the group is reachable by Tab\n// but a single Tab steps past it. The none chip counts as checked when the\n// value is unset. Arrow keys then rove focus between the radios.\nconst rovingIndex = computed(() => {\n if (showNoneChip.value && isUnset.value) {\n return 0;\n }\n const selected = presets.value.findIndex((preset) =>\n isPresetSelected(preset),\n );\n return selected === -1 ? 0 : presetPosition(selected);\n});\nfunction noneChipTabindex(): number {\n return rovingIndex.value === 0 ? 0 : -1;\n}\nfunction presetTabindex(index: number): number {\n return presetPosition(index) === rovingIndex.value ? 0 : -1;\n}\n\n// Arrow keys rove FOCUS across the chips (wrapping); they do NOT select.\n// Enter/Space activate the focused chip via native button click. This\n// deviates from APG's default \"selection follows focus\" on purpose: every\n// selection emits `update:modelValue`, and on the rich-text toolbar path that\n// runs `editor.chain().focus().setColor()` (see the swatch-only field comment\n// in the template), which refocuses the canvas — so following focus would\n// steal focus after the first arrow key and break traversal. APG sanctions\n// explicit activation when moving focus has side effects.\nfunction onPresetGridKeydown(e: KeyboardEvent): void {\n const arrows = [\"ArrowRight\", \"ArrowDown\", \"ArrowLeft\", \"ArrowUp\"];\n if (!arrows.includes(e.key)) {\n return;\n }\n // Resolve the current chip from `event.target`, never `document.activeElement`\n // — the latter is unreliable across a shadow-DOM boundary (this editor can\n // mount in a shadow root; the repo threads the real root via EDITOR_ROOT_KEY\n // for the same reason).\n const group = e.currentTarget as HTMLElement;\n const radios = Array.from(\n group.querySelectorAll<HTMLElement>('[role=\"radio\"]'),\n );\n const current = (e.target as HTMLElement).closest('[role=\"radio\"]');\n const currentIndex = current ? radios.indexOf(current as HTMLElement) : -1;\n if (currentIndex === -1) {\n return;\n }\n const forward = e.key === \"ArrowRight\" || e.key === \"ArrowDown\";\n const nextIndex =\n (currentIndex + (forward ? 1 : -1) + radios.length) % radios.length;\n radios[nextIndex]?.focus();\n e.preventDefault();\n}\n\nfunction onPickerChange(e: Event): void {\n pickerTouched.value = true;\n emit(\"update:modelValue\", (e as CustomEvent).detail.value);\n}\n\nfunction onPickerPointerDown(): void {\n pickerTouched.value = false;\n}\n\nfunction onPickerPointerUp(): void {\n // A deliberate pick equal to the seed fires no `color-changed`; commit it.\n if (!pickerTouched.value && isUnset.value) {\n emit(\"update:modelValue\", seed.value);\n }\n}\n\nfunction onTextInput(e: Event): void {\n emit(\"update:modelValue\", (e.target as HTMLInputElement).value);\n}\n\nfunction clear(): void {\n emit(\"update:modelValue\", \"\");\n}\n\nfunction selectTransparent(): void {\n emit(\"update:modelValue\", \"transparent\");\n}\n\n// The popover teleports to the shared popover root so it escapes any clipping /\n// overflow-hidden ancestor (e.g. the link dialog's card, where clicking the\n// clipped wheel landed on the backdrop and closed the modal). Position is\n// captured from the swatch on open and applied as `absolute`, in coordinates\n// local to the popover root (`toLocal`) so a transformed ancestor of the\n// editor doesn't offset it — see usePopoverPosition.\nconst popoverRoot = usePopoverRoot();\nconst { toLocal } = usePopoverPosition();\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverPosition = ref({ top: 0, left: 0 });\n\nfunction toggleOpen(): void {\n if (props.disabled) return;\n if (!open.value) {\n const rect = swatchRef.value?.getBoundingClientRect();\n if (rect) {\n const PICKER_HEIGHT = 240; // wheel + hue slider + padding, approx\n const gap = 8;\n // Open below the swatch, or flip above when it would overflow the viewport\n // bottom — the popover is `fixed`, so it can't scroll into view otherwise.\n const fitsBelow = rect.bottom + gap + PICKER_HEIGHT <= window.innerHeight;\n const viewportTop = fitsBelow\n ? rect.bottom + gap\n : Math.max(gap, rect.top - PICKER_HEIGHT - gap);\n popoverPosition.value = toLocal({ top: viewportTop, left: rect.left });\n }\n }\n open.value = !open.value;\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'tpl:flex tpl:items-center tpl:gap-2 tpl:relative',\n disabled && 'tpl:opacity-60 tpl:cursor-not-allowed',\n ]\"\n >\n <button\n ref=\"swatchRef\"\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel || t.colorPicker.pickColor\"\n :title=\"ariaLabel || undefined\"\n :aria-expanded=\"open\"\n :class=\"[\n 'tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-0.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n open\n ? 'tpl:border-[var(--tpl-primary)] tpl:shadow-[var(--tpl-ring)]'\n : !disabled && 'tpl:hover:border-[var(--tpl-text-dim)]',\n size === 'sm' ? 'tpl:size-8' : 'tpl:size-10',\n ]\"\n @click=\"toggleOpen\"\n >\n <span\n class=\"tpl:block tpl:size-full tpl:rounded-[calc(var(--tpl-radius-sm)-2px)]\"\n :class=\"{\n 'tpl-color-swatch-empty': isUnset,\n 'tpl-color-swatch-transparent': isTransparent,\n }\"\n :style=\"\n isUnset || isTransparent\n ? undefined\n : { backgroundColor: displayValue }\n \"\n />\n </button>\n <div v-if=\"!swatchOnly && showFreeform\" class=\"tpl:relative tpl:flex-1\">\n <input\n type=\"text\"\n :class=\"[colorTextClass, 'tpl:w-full']\"\n :style=\"showClear ? { paddingRight: '2.25rem' } : undefined\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || t.colorPicker.notSet\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @input=\"onTextInput\"\n />\n <button\n v-if=\"showClear\"\n type=\"button\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n class=\"tpl:absolute tpl:right-2 tpl:top-1/2 tpl:flex tpl:size-6 tpl:-translate-y-1/2 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:text-[var(--tpl-text-dim)] tpl:transition-colors tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"clear\"\n >\n <X :size=\"14\" :stroke-width=\"1.5\" />\n </button>\n </div>\n <Teleport :to=\"popoverRoot\" :disabled=\"!popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n enter-from-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n enter-to-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-active-class=\"tpl:transition-all tpl:duration-[80ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n leave-from-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-to-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n >\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl-color-popover tpl:absolute tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:shadow-[var(--tpl-shadow-lg)]\"\n :style=\"{\n top: `${popoverPosition.top}px`,\n left: `${popoverPosition.left}px`,\n ...themeStyles,\n }\"\n >\n <button\n v-if=\"allowTransparent\"\n type=\"button\"\n data-testid=\"color-picker-transparent\"\n :aria-pressed=\"isTransparent\"\n :aria-label=\"t.colorPicker.transparent\"\n :title=\"t.colorPicker.transparent\"\n :class=\"[\n 'tpl:mb-2 tpl:flex tpl:w-full tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:py-1 tpl:text-left tpl:text-xs tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)]',\n isTransparent\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"selectTransparent\"\n >\n <span\n class=\"tpl-color-swatch-transparent tpl:size-4 tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)]\"\n />\n {{ t.colorPicker.transparent }}\n </button>\n <!-- Preset color grid — an ARIA radio group: each chip is a\n `role=\"radio\"`, arrow keys rove focus between them (roving\n tabindex), and Enter/Space activate. Supplements the wheel/hex\n field; the only control when `allowCustom` is false. Each preset\n carries its color value as its accessible label. -->\n <div\n v-if=\"hasPresets\"\n role=\"radiogroup\"\n :aria-label=\"t.colorPicker.presetColors\"\n :class=\"[\n 'tpl:flex tpl:flex-wrap tpl:gap-1.5',\n showFreeform && 'tpl:mb-2',\n ]\"\n @keydown=\"onPresetGridKeydown\"\n >\n <!-- Leading \"no colour\" (unset/inherit) chip — locked mode only.\n Always present, never gated on the current value: unset is a\n first-class member of the value domain, and a chip that\n disappeared on activation would strand keyboard focus mid-grid.\n Reuses the diagonal-slash unset visual and the same chip\n chrome; activating it emits \"\" to clear. -->\n <button\n v-if=\"showNoneChip\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n :aria-checked=\"isUnset\"\n :tabindex=\"noneChipTabindex()\"\n :class=\"[\n 'tpl-color-swatch-empty tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)] tpl:focus-visible:ring-offset-1 tpl:focus-visible:ring-offset-[var(--tpl-bg-elevated)]',\n isUnset\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"clear\"\n />\n <button\n v-for=\"(preset, index) in presets\"\n :key=\"preset\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"preset\"\n :title=\"preset\"\n :aria-checked=\"isPresetSelected(preset)\"\n :tabindex=\"presetTabindex(index)\"\n :style=\"{ backgroundColor: preset }\"\n :class=\"[\n 'tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:focus-visible:ring-2 tpl:focus-visible:ring-[var(--tpl-primary)] tpl:focus-visible:ring-offset-1 tpl:focus-visible:ring-offset-[var(--tpl-bg-elevated)]',\n isPresetSelected(preset)\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-primary)] tpl:ring-offset-1 tpl:ring-offset-[var(--tpl-bg-elevated)]'\n : 'tpl:hover:border-[var(--tpl-text-dim)]',\n ]\"\n @click=\"selectPreset(preset)\"\n />\n </div>\n <hex-color-picker\n v-if=\"showFreeform\"\n :color=\"seed\"\n :aria-label=\"t.colorPicker.pickColor\"\n @color-changed=\"onPickerChange\"\n @pointerdown=\"onPickerPointerDown\"\n @pointerup=\"onPickerPointerUp\"\n @keydown.escape=\"open = false\"\n />\n <!-- Swatch-only mode has no inline field beside the swatch, so the\n popover carries the manual hex field (+ inline × clear) — the same\n control as the full-mode sidebar field. It commits on change/Enter,\n NOT @input: in the rich-text toolbar every emit calls\n editor.chain().focus().setColor(), and focusing the canvas mid-type\n would steal focus after the first character. Shown even when unset\n so a first color can be typed; the × appears only once a value is set. -->\n <div v-if=\"swatchOnly && showFreeform\" class=\"tpl:relative tpl:mt-2\">\n <input\n type=\"text\"\n :class=\"[colorTextClass, 'tpl:w-full']\"\n :style=\"showClear ? { paddingRight: '2.25rem' } : undefined\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || t.colorPicker.notSet\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @change=\"onTextInput\"\n @keydown.enter.prevent=\"\n ($event.target as HTMLInputElement).blur()\n \"\n />\n <button\n v-if=\"showClear\"\n type=\"button\"\n :aria-label=\"t.colorPicker.clear\"\n :title=\"t.colorPicker.clear\"\n class=\"tpl:absolute tpl:right-2 tpl:top-1/2 tpl:flex tpl:size-6 tpl:-translate-y-1/2 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:text-[var(--tpl-text-dim)] tpl:transition-colors tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:hover:bg-[var(--tpl-bg-hover)] tpl:hover:text-[var(--tpl-text)]\"\n @click=\"clear\"\n >\n <X :size=\"14\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<style>\nhex-color-picker {\n --hcp-width: 200px;\n}\n\nhex-color-picker::part(hue) {\n border-radius: var(--tpl-radius-sm, 7px);\n}\n\nhex-color-picker::part(saturation) {\n border-radius: var(--tpl-radius-sm, 7px) var(--tpl-radius-sm, 7px) 0 0;\n}\n\n/* Unset swatch: paper with a single thin diagonal slash = \"no color set\".\n A neutral non-color signal — never Signal Amber (intent) or Danger. */\n.tpl-color-swatch-empty {\n background-color: var(--tpl-bg);\n background-image: linear-gradient(\n to top right,\n transparent calc(50% - 0.75px),\n var(--tpl-text-dim) calc(50% - 0.75px),\n var(--tpl-text-dim) calc(50% + 0.75px),\n transparent calc(50% + 0.75px)\n );\n}\n\n/* Stored keyword `transparent`: a checker, distinct from the unset slash. */\n.tpl-color-swatch-transparent {\n background-color: var(--tpl-bg);\n background-image: repeating-conic-gradient(\n var(--tpl-border) 0% 25%,\n var(--tpl-bg) 0% 50%\n );\n background-size: 8px 8px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport type { MergeTag } from \"@templatical/types\";\n\nconst props = defineProps<{\n items: MergeTag[];\n selectedIndex: number;\n emptyText: string;\n /** Stable id used for aria-controls + per-option id derivation. */\n listId?: string;\n}>();\n\ndefineEmits<{\n (e: \"select\", item: MergeTag): void;\n (e: \"hover\", index: number): void;\n}>();\n\nfunction optionId(index: number): string | undefined {\n return props.listId ? `${props.listId}-opt-${index}` : undefined;\n}\n</script>\n\n<template>\n <div\n :id=\"listId\"\n class=\"tpl:min-w-[200px] tpl:max-w-[320px] tpl:max-h-[50vh] tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:py-1 tpl:shadow-[var(--tpl-shadow-lg)]\"\n role=\"listbox\"\n data-testid=\"merge-tag-suggestion-list\"\n >\n <div\n v-if=\"items.length === 0\"\n class=\"tpl:px-3 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n data-testid=\"merge-tag-suggestion-empty\"\n >\n {{ emptyText }}\n </div>\n <button\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n :id=\"optionId(index)\"\n type=\"button\"\n role=\"option\"\n :aria-selected=\"index === selectedIndex\"\n :data-selected=\"index === selectedIndex ? 'true' : 'false'\"\n :data-merge-tag-value=\"item.value\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-start tpl:gap-0.5 tpl:px-3 tpl:py-1.5 tpl:text-left tpl:text-xs tpl:transition-colors\"\n :class=\"\n index === selectedIndex\n ? 'tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n @mousedown.prevent.stop=\"$emit('select', item)\"\n @mousemove=\"index !== selectedIndex && $emit('hover', index)\"\n >\n <span class=\"tpl:font-medium\">{{ item.label }}</span>\n <span class=\"tpl:text-[var(--tpl-text-dim)] tpl:font-mono\">{{\n item.value\n }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { MergeTag } from \"@templatical/types\";\n\nconst props = defineProps<{\n items: MergeTag[];\n selectedIndex: number;\n emptyText: string;\n /** Stable id used for aria-controls + per-option id derivation. */\n listId?: string;\n}>();\n\ndefineEmits<{\n (e: \"select\", item: MergeTag): void;\n (e: \"hover\", index: number): void;\n}>();\n\nfunction optionId(index: number): string | undefined {\n return props.listId ? `${props.listId}-opt-${index}` : undefined;\n}\n</script>\n\n<template>\n <div\n :id=\"listId\"\n class=\"tpl:min-w-[200px] tpl:max-w-[320px] tpl:max-h-[50vh] tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:py-1 tpl:shadow-[var(--tpl-shadow-lg)]\"\n role=\"listbox\"\n data-testid=\"merge-tag-suggestion-list\"\n >\n <div\n v-if=\"items.length === 0\"\n class=\"tpl:px-3 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n data-testid=\"merge-tag-suggestion-empty\"\n >\n {{ emptyText }}\n </div>\n <button\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n :id=\"optionId(index)\"\n type=\"button\"\n role=\"option\"\n :aria-selected=\"index === selectedIndex\"\n :data-selected=\"index === selectedIndex ? 'true' : 'false'\"\n :data-merge-tag-value=\"item.value\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-start tpl:gap-0.5 tpl:px-3 tpl:py-1.5 tpl:text-left tpl:text-xs tpl:transition-colors\"\n :class=\"\n index === selectedIndex\n ? 'tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n @mousedown.prevent.stop=\"$emit('select', item)\"\n @mousemove=\"index !== selectedIndex && $emit('hover', index)\"\n >\n <span class=\"tpl:font-medium\">{{ item.label }}</span>\n <span class=\"tpl:text-[var(--tpl-text-dim)] tpl:font-mono\">{{\n item.value\n }}</span>\n </button>\n </div>\n</template>\n","import type { MergeTag } from \"@templatical/types\";\nimport { type App, createApp, h, ref, type Ref } from \"vue\";\nimport MergeTagSuggestionList from \"../components/MergeTagSuggestionList.vue\";\n\nconst MAX_RESULTS = 10;\n\n// Monotonic counter for unique listbox IDs across multiple popup instances.\nlet POPUP_ID_SEQ = 0;\n\n/**\n * Filter merge tags by query against label and value (case-insensitive).\n * Capped at MAX_RESULTS. Exported for testing.\n */\nexport function filterMergeTags(tags: MergeTag[], query: string): MergeTag[] {\n const trimmed = query.trim().toLowerCase();\n if (trimmed === \"\") {\n return tags.slice(0, MAX_RESULTS);\n }\n return tags\n .filter((tag) => {\n const label = tag.label.toLowerCase();\n const value = tag.value.toLowerCase();\n return label.includes(trimmed) || value.includes(trimmed);\n })\n .slice(0, MAX_RESULTS);\n}\n\n/**\n * Handle a keydown event against a list of items. Returns whether the\n * event was handled (so the suggestion plugin can stop propagation).\n * Exported for testing.\n */\nexport function handleSuggestionKeyDown(\n event: KeyboardEvent,\n items: MergeTag[],\n selectedIndex: Ref<number>,\n onSelect: (item: MergeTag) => void,\n): boolean {\n if (items.length === 0) {\n if (event.key === \"Enter\" || event.key === \"Tab\") return true;\n return false;\n }\n\n if (event.key === \"ArrowDown\") {\n selectedIndex.value = (selectedIndex.value + 1) % items.length;\n return true;\n }\n if (event.key === \"ArrowUp\") {\n selectedIndex.value =\n (selectedIndex.value - 1 + items.length) % items.length;\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n onSelect(items[selectedIndex.value]);\n return true;\n }\n return false;\n}\n\n/** Options for opening the merge-tag popup. */\nexport interface MergeTagPopupOpenOptions {\n /** Filtered tags to render. */\n items: MergeTag[];\n /**\n * Anchor rect getter (the caret rect). Viewport coordinates, re-read on\n * every reposition so the popup tracks the caret through scroll. Same\n * contract the TipTap suggestion plugin uses for `clientRect`.\n */\n getRect: (() => DOMRect | null) | null;\n /**\n * Element that owns the ARIA combobox state and serves as the theme +\n * scroll-ancestor anchor — the ProseMirror `view.dom` in rich text, the\n * `<input>`/`<textarea>` in a field.\n */\n anchorEl: HTMLElement | null;\n /** Called with the item the user picks. */\n onCommand: (item: MergeTag) => void;\n}\n\n/** Options for updating the popup after the query changed. */\nexport interface MergeTagPopupUpdateOptions {\n items: MergeTag[];\n getRect: (() => DOMRect | null) | null;\n onCommand: (item: MergeTag) => void;\n}\n\n/**\n * Imperative handle over a single merge-tag suggestion popup.\n * Both the TipTap suggestion renderer and the input/textarea autocomplete\n * driver drive the SAME controller, so the popup's rendering, positioning,\n * scroll-tracking, ARIA wiring, and keyboard behavior are identical across\n * rich-text and plain-field surfaces by construction.\n */\nexport interface MergeTagPopupController {\n /** Mount the popup and start tracking the caret. */\n open(options: MergeTagPopupOpenOptions): void;\n /** Refresh items + anchor after the query changed. */\n update(options: MergeTagPopupUpdateOptions): void;\n /**\n * Handle a navigation/selection key (Arrow/Enter/Tab). Returns whether it\n * was handled. Does NOT handle Escape — each caller owns Escape (TipTap's\n * plugin closes via `onExit`; the field driver calls `close()`).\n */\n handleKeyDown(event: KeyboardEvent): boolean;\n /** Unmount the popup, clear ARIA, and detach listeners. Idempotent. */\n close(): void;\n /** Whether the popup is currently mounted. */\n isOpen(): boolean;\n}\n\n/**\n * Creates a merge-tag suggestion popup controller: mount, ARIA wiring,\n * caret-tracking position (flip-above + off-screen freeze), scroll/resize\n * repositioning, and cleanup.\n *\n * TipTap-free on purpose. Field autocomplete (`useMergeTagAutocomplete`)\n * lives on the editor's eager graph — the properties panel mounts with the\n * chrome — and a value import of `@tiptap/core` / `@tiptap/suggestion` from\n * this module would pull the whole rich-text stack into every session.\n * The TipTap extension in `extensions/MergeTagSuggestion.ts` is the adapter\n * that maps ProseMirror `SuggestionProps` onto this controller, and it loads\n * only when a title or paragraph enters edit mode.\n */\nexport function createMergeTagPopup(\n emptyText: string,\n popoverRootRef?: Ref<HTMLElement | null> | null,\n): MergeTagPopupController {\n let app: App | null = null;\n let container: HTMLElement | null = null;\n let editableEl: HTMLElement | null = null;\n const itemsRef = ref<MergeTag[]>([]);\n const selectedIndex = ref(0);\n let currentCommand: ((item: MergeTag) => void) | null = null;\n const listId = `tpl-merge-tag-suggestion-${++POPUP_ID_SEQ}`;\n let latestClientRect: (() => DOMRect | null) | null = null;\n let scrollTargets: Array<EventTarget> = [];\n let pendingRaf: number | null = null;\n\n function reposition(): void {\n position(latestClientRect?.() ?? null);\n }\n\n /**\n * Reposition immediately and on the next animation frame.\n * After a keystroke triggers the suggestion, TipTap may run its\n * own `scrollIntoView` to keep the caret visible. That scroll\n * lands AFTER the current task's `position()` call but BEFORE\n * the browser's next paint, so we re-measure on rAF to catch\n * the post-scroll caret rect. Without this, the popup pins to\n * the pre-scroll caret position and ends up offset on slower\n * runners.\n */\n function repositionAfterPaint(): void {\n reposition();\n if (pendingRaf !== null) cancelAnimationFrame(pendingRaf);\n pendingRaf = requestAnimationFrame(() => {\n pendingRaf = null;\n reposition();\n });\n }\n\n function collectScrollAncestors(el: HTMLElement | null): HTMLElement[] {\n // Walk up the DOM finding scrollable ancestors. ProseMirror's\n // scrollIntoView fires on whichever ancestor scrolls — listening\n // to all of them ensures we reposition regardless of which one\n // moves.\n const result: HTMLElement[] = [];\n let node: HTMLElement | null = el?.parentElement ?? null;\n while (\n node &&\n // shadow-ok: ancestor walk terminator; not a mount target\n node !== document.body &&\n node !== document.documentElement\n ) {\n const style = window.getComputedStyle(node);\n const overflow = style.overflow + style.overflowX + style.overflowY;\n if (/(auto|scroll|overlay)/.test(overflow)) {\n result.push(node);\n }\n node = node.parentElement;\n }\n return result;\n }\n\n function attachScrollListeners(viewDom: HTMLElement | null): void {\n scrollTargets = [window, ...collectScrollAncestors(viewDom)];\n for (const target of scrollTargets) {\n target.addEventListener(\"scroll\", reposition, {\n passive: true,\n capture: true,\n });\n }\n window.addEventListener(\"resize\", reposition, { passive: true });\n }\n\n function detachScrollListeners(): void {\n for (const target of scrollTargets) {\n target.removeEventListener(\"scroll\", reposition, {\n capture: true,\n } as EventListenerOptions);\n }\n window.removeEventListener(\"resize\", reposition);\n scrollTargets = [];\n }\n\n function position(rect: DOMRect | null): void {\n if (!container || !rect) return;\n // If the caret has scrolled out of the viewport, freeze the\n // popup at its last on-screen position. Following the caret\n // off-screen produces an invisible popup the user can't reach,\n // and lets pathological scroll loops drag the popup further\n // each tick.\n if (rect.bottom < 0 || rect.top > window.innerHeight) return;\n // Position `absolute` relative to the mount target — the popover root\n // (`position: relative`) or the <body> fallback. The caret rect is in\n // viewport space, so subtract the offset parent's rect to get local\n // coords: a transformed ancestor of the editor becomes the containing\n // block for a `fixed` popup and would offset it, but not for an\n // `absolute` popup anchored inside the positioned popover root. The\n // off-screen freeze and flip-above decisions still use viewport metrics.\n container.style.position = \"absolute\";\n container.style.zIndex = \"9999\";\n const origin = container.offsetParent?.getBoundingClientRect();\n const originTop = origin?.top ?? 0;\n const originLeft = origin?.left ?? 0;\n container.style.left = `${rect.left - originLeft}px`;\n // Place below caret first; offsetHeight is sync-readable after\n // the Vue app has mounted (or after onUpdate's reactive flush).\n container.style.top = `${rect.bottom - originTop}px`;\n const popupHeight = container.offsetHeight;\n if (popupHeight === 0) return;\n const spaceBelow = window.innerHeight - rect.bottom;\n if (spaceBelow < popupHeight) {\n // Not enough room below — flip above. Clamp to 0 so the\n // popup never positions off the top of the viewport.\n const flippedTop = Math.max(0, rect.top - popupHeight);\n container.style.top = `${flippedTop - originTop}px`;\n }\n }\n\n function applyThemeContext(\n target: HTMLElement,\n editorEl: HTMLElement | null | undefined,\n ): void {\n // When the popup mounts to document.body its `position: fixed`\n // resolves against the viewport — any transform/filter on a\n // consumer-page ancestor (route transitions, reveal animations,\n // dark canvas inversion) creates a containing block and moves\n // fixed descendants with it. Body ancestors don't transform.\n //\n // When mounting inside the editor's popover root, the popup is a\n // descendant of the `.tpl[data-tpl-theme]` root so CSS vars + font\n // would inherit — but the snapshot below is still emitted inline\n // because inline declarations win and the cost is negligible. This\n // keeps a single behavior across both mount paths.\n //\n // CSS vars (--tpl-bg-elevated, --tpl-border, etc.) are scoped to\n // `.tpl` and `.tpl[data-tpl-theme=\"dark\"]` in the editor's\n // stylesheet, so the popup wouldn't inherit them at body root.\n // Adding `class=\"tpl\"` would also pull base rules (min-height,\n // flex, full-page bg) we don't want on a popup. Instead, snapshot\n // every --tpl-* custom property from the editor's theme root and\n // re-emit them inline on the popup wrapper.\n const themeRoot = editorEl?.closest<HTMLElement>(\"[data-tpl-theme]\");\n if (!themeRoot) return;\n const themeValue = themeRoot.getAttribute(\"data-tpl-theme\");\n if (themeValue) target.setAttribute(\"data-tpl-theme\", themeValue);\n const computed = window.getComputedStyle(themeRoot);\n for (let i = 0; i < computed.length; i++) {\n const prop = computed[i];\n if (prop.startsWith(\"--tpl-\")) {\n target.style.setProperty(prop, computed.getPropertyValue(prop));\n }\n }\n // The popup is teleported out of the editor wrapper, so it inherits no\n // font from it: without this its content renders in the page's default font\n // and ends up at a different height — which changes the flip-above decision\n // and shifts the popup off the caret. Copy typography too.\n target.style.fontFamily = computed.fontFamily;\n target.style.fontSize = computed.fontSize;\n target.style.lineHeight = computed.lineHeight;\n }\n\n function setEditableAria(active: boolean): void {\n if (!editableEl) return;\n if (active) {\n editableEl.setAttribute(\"role\", \"combobox\");\n editableEl.setAttribute(\"aria-haspopup\", \"listbox\");\n editableEl.setAttribute(\"aria-expanded\", \"true\");\n editableEl.setAttribute(\"aria-controls\", listId);\n } else {\n editableEl.removeAttribute(\"aria-expanded\");\n editableEl.removeAttribute(\"aria-controls\");\n editableEl.removeAttribute(\"aria-activedescendant\");\n editableEl.removeAttribute(\"aria-haspopup\");\n editableEl.removeAttribute(\"role\");\n }\n }\n\n function setActiveDescendant(): void {\n if (!editableEl) return;\n if (itemsRef.value.length === 0) {\n editableEl.removeAttribute(\"aria-activedescendant\");\n return;\n }\n editableEl.setAttribute(\n \"aria-activedescendant\",\n `${listId}-opt-${selectedIndex.value}`,\n );\n }\n\n function select(item: MergeTag): void {\n currentCommand?.(item);\n }\n\n return {\n open: ({ items, getRect, anchorEl, onCommand }) => {\n itemsRef.value = items;\n selectedIndex.value = 0;\n currentCommand = onCommand;\n\n container = document.createElement(\"div\");\n container.setAttribute(\"data-testid\", \"merge-tag-suggestion-popup\");\n editableEl = anchorEl;\n applyThemeContext(container, anchorEl);\n // Prefer the editor's popover root (shadow-aware) when wired by\n // the consumer. Falls back to document.body for headless callers\n // that don't pass `popoverRoot` to configure().\n // shadow-ok: fallback when popoverRoot wasn't provided (e.g., headless caller); editor mounts pass the shadow-aware root\n const mountTarget = popoverRootRef?.value ?? document.body;\n mountTarget.appendChild(container);\n\n app = createApp({\n render() {\n return h(MergeTagSuggestionList, {\n items: itemsRef.value,\n selectedIndex: selectedIndex.value,\n emptyText,\n listId,\n onSelect: (item: MergeTag) => select(item),\n onHover: (index: number) => {\n selectedIndex.value = index;\n setActiveDescendant();\n },\n });\n },\n });\n app.mount(container);\n setEditableAria(true);\n setActiveDescendant();\n latestClientRect = getRect;\n repositionAfterPaint();\n attachScrollListeners(anchorEl);\n },\n update: ({ items, getRect, onCommand }) => {\n itemsRef.value = items;\n // Reset selection when item set changes (query changed).\n if (selectedIndex.value >= items.length) {\n selectedIndex.value = 0;\n }\n currentCommand = onCommand;\n setActiveDescendant();\n latestClientRect = getRect;\n repositionAfterPaint();\n },\n handleKeyDown: (event: KeyboardEvent): boolean => {\n const handled = handleSuggestionKeyDown(\n event,\n itemsRef.value,\n selectedIndex,\n select,\n );\n if (handled) setActiveDescendant();\n return handled;\n },\n close: () => {\n // Idempotent: nothing to tear down (and no window listeners to detach)\n // when the popup was never opened. Keeps repeated close() / close-before-\n // open cheap and side-effect-free.\n if (container === null) return;\n if (pendingRaf !== null) {\n cancelAnimationFrame(pendingRaf);\n pendingRaf = null;\n }\n detachScrollListeners();\n setEditableAria(false);\n app?.unmount();\n container.remove();\n app = null;\n container = null;\n editableEl = null;\n currentCommand = null;\n latestClientRect = null;\n },\n isOpen: () => container !== null,\n };\n}\n","/**\n * Result of a merge-tag trigger scan — the caret sits inside an open tag.\n */\nexport interface OpenMergeTagTrigger {\n /** Index in the field value where the trigger opener starts. */\n triggerStart: number;\n /** Caret index the scan ran at (also the end of `query`). */\n caret: number;\n /** Text typed between the opener and the caret — the autocomplete filter. */\n query: string;\n}\n\n/**\n * Detects whether the caret sits inside an *open* merge-tag trigger: the user\n * has typed the syntax opener (e.g. `{{`) and is mid-typing the tag name with\n * no closing delimiter yet. Returns the opener position + query so the caller\n * can drive the autocomplete popup, or `null` when the caret is not inside an\n * open trigger.\n *\n * Mirrors `@tiptap/suggestion`'s behavior with `allowSpaces: false`: the run\n * between the opener and the caret ends at the first whitespace, and a closing\n * delimiter inside that run means the tag is already complete (`{{first_name}}`)\n * so the popup must not reappear over it.\n */\nexport function findOpenMergeTagTrigger(\n value: string,\n caret: number,\n triggerChar: string,\n closingChar: string | null,\n): OpenMergeTagTrigger | null {\n if (!triggerChar) return null;\n\n const before = value.slice(0, caret);\n const triggerStart = before.lastIndexOf(triggerChar);\n if (triggerStart === -1) return null;\n\n const query = before.slice(triggerStart + triggerChar.length);\n\n // Whitespace ends the query (allowSpaces: false parity).\n if (/\\s/.test(query)) return null;\n\n // A closing delimiter between the opener and the caret means the tag is\n // already complete — don't reopen the popup over a finished tag.\n if (closingChar && query.includes(closingChar)) return null;\n\n return { triggerStart, caret, query };\n}\n","/**\n * Viewport-space caret geometry for `<input>` / `<textarea>` elements.\n *\n * Native text controls expose no caret coordinates (unlike ProseMirror, which\n * hands the merge-tag popup a `clientRect()`). The standard workaround is a\n * hidden \"mirror\" element that clones the field's text metrics; the caret's\n * pixel position is read from a marker span placed at the caret offset. This\n * lets the input/textarea autocomplete anchor its popup at the caret exactly\n * like the rich-text editor does.\n *\n * Adapted from component/textarea-caret-position (MIT). Returns a viewport\n * `DOMRect` (same coordinate space as `Range.getBoundingClientRect`) so the\n * shared merge-tag popup controller positions it identically to the TipTap path.\n */\n\n// Computed-style properties copied onto the mirror so its text wraps and\n// measures exactly like the source field.\nconst MIRRORED_PROPERTIES = [\n \"boxSizing\",\n \"width\",\n \"borderTopWidth\",\n \"borderRightWidth\",\n \"borderBottomWidth\",\n \"borderLeftWidth\",\n \"borderStyle\",\n \"paddingTop\",\n \"paddingRight\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"fontStyle\",\n \"fontVariant\",\n \"fontWeight\",\n \"fontStretch\",\n \"fontSize\",\n \"lineHeight\",\n \"fontFamily\",\n \"textAlign\",\n \"textTransform\",\n \"textIndent\",\n \"letterSpacing\",\n \"wordSpacing\",\n \"tabSize\",\n \"whiteSpace\",\n \"wordWrap\",\n \"wordBreak\",\n] as const;\n\nfunction toRect(left: number, top: number, bottom: number): DOMRect {\n const height = bottom - top;\n return {\n left,\n top,\n bottom,\n right: left,\n width: 0,\n height,\n x: left,\n y: top,\n toJSON: () => ({}),\n } as DOMRect;\n}\n\n/**\n * Returns the viewport rect of the caret at `position` in a text field, or\n * `null` when the field isn't laid out (detached / no view). The rect is a\n * zero-width vertical strip at the caret, one line tall — matching the shape\n * `Range.getBoundingClientRect()` produces for a collapsed selection.\n */\nexport function getCaretRect(\n el: HTMLInputElement | HTMLTextAreaElement,\n position: number,\n): DOMRect | null {\n const doc = el.ownerDocument;\n const win = doc.defaultView;\n if (!win) return null;\n\n const isInput = el.tagName === \"INPUT\";\n const style = win.getComputedStyle(el);\n\n const mirror = doc.createElement(\"div\");\n mirror.setAttribute(\"aria-hidden\", \"true\");\n const s = mirror.style;\n s.position = \"absolute\";\n s.visibility = \"hidden\";\n s.top = \"0\";\n s.left = \"0\";\n s.overflow = \"hidden\";\n s.whiteSpace = isInput ? \"pre\" : \"pre-wrap\";\n s.wordWrap = isInput ? \"normal\" : \"break-word\";\n const target = s as unknown as Record<string, string>;\n const source = style as unknown as Record<string, string>;\n for (const prop of MIRRORED_PROPERTIES) {\n target[prop] = source[prop];\n }\n // A single-line input never wraps; let the mirror grow with the text so the\n // marker's horizontal offset tracks the caret.\n if (isInput) {\n s.width = \"auto\";\n s.whiteSpace = \"pre\";\n }\n\n const value = el.value;\n let head = value.slice(0, position);\n if (isInput) {\n // <input> collapses whitespace runs to a single rendered space; replace\n // with non-breaking spaces so the mirror's width matches the field.\n head = head.replace(/\\s/g, \" \");\n }\n mirror.textContent = head;\n\n const marker = doc.createElement(\"span\");\n // Remaining text (or a placeholder) gives the marker the caret line's height.\n marker.textContent = value.slice(position) || \".\";\n mirror.appendChild(marker);\n\n doc.body.appendChild(mirror);\n const markerTop = marker.offsetTop;\n const markerLeft = marker.offsetLeft;\n doc.body.removeChild(mirror);\n\n const elRect = el.getBoundingClientRect();\n const borderTop = parseFloat(style.borderTopWidth) || 0;\n const borderLeft = parseFloat(style.borderLeftWidth) || 0;\n const lineHeight =\n parseFloat(style.lineHeight) || parseFloat(style.fontSize) || 0;\n\n const left = elRect.left + markerLeft + borderLeft - el.scrollLeft;\n\n // A single-line input vertically centers its text, which the block-flow\n // mirror can't replicate — anchor vertically to the field itself so the\n // popup sits directly under the input. Textareas use the measured caret line.\n if (isInput) {\n return toRect(left, elRect.top, elRect.bottom);\n }\n\n const top = elRect.top + markerTop + borderTop - el.scrollTop;\n return toRect(left, top, top + lineHeight);\n}\n","import {\n getSyntaxClosingChar,\n getSyntaxTriggerChar,\n type MergeTag,\n type SyntaxPreset,\n} from \"@templatical/types\";\nimport {\n computed,\n getCurrentScope,\n inject,\n isRef,\n nextTick,\n onScopeDispose,\n type ComputedRef,\n type Ref,\n type ShallowRef,\n} from \"vue\";\nimport type { Translations } from \"../i18n\";\nimport { TRANSLATIONS_KEY } from \"../keys\";\nimport { createMergeTagPopup, filterMergeTags } from \"../utils/mergeTagPopup\";\nimport {\n findOpenMergeTagTrigger,\n type OpenMergeTagTrigger,\n} from \"../utils/mergeTagTrigger\";\nimport { getCaretRect } from \"../utils/textFieldCaret\";\nimport { usePopoverRoot } from \"./usePopoverRoot\";\n\nexport interface UseMergeTagAutocompleteOptions {\n /** The `<input>` / `<textarea>` the popup anchors to. */\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n /** Reads the current field value. */\n modelValue: () => string;\n /** Emits a new field value. */\n emit: (value: string) => void;\n /** Available merge tags (the autocomplete filter source). */\n mergeTags: ShallowRef<MergeTag[]>;\n /** Resolved merge-tag syntax. */\n syntax: SyntaxPreset;\n /** Consumer's `mergeTags.autocomplete` flag. */\n enabled: boolean;\n /**\n * Invoked right before a picked tag is emitted, so the field can keep edit\n * mode and not flip to the chip display while the caret is still active.\n */\n onInsert?: () => void;\n}\n\nexport interface UseMergeTagAutocompleteReturn {\n /**\n * Whether autocomplete can ever fire. Mirrors the rich-text gate exactly:\n * enabled + a built-in trigger syntax + at least one tag to filter.\n */\n available: ComputedRef<boolean>;\n /** Recompute the popup after the value or caret changed. */\n refresh: () => void;\n /**\n * Field `keydown` handler. Returns `true` when the popup consumed the key\n * (the caller must then `preventDefault`); `false` to let the field handle it.\n */\n handleKeydown: (event: KeyboardEvent) => boolean;\n /** Close the popup (e.g. on blur). Idempotent. */\n close: () => void;\n /** Whether the popup is currently open. */\n isOpen: () => boolean;\n}\n\n// Horizontal caret moves take the caret out of the query run — dismiss the\n// popup (like navigating away in the rich-text editor) and let the caret move.\nconst CARET_MOVE_KEYS = new Set([\"ArrowLeft\", \"ArrowRight\", \"Home\", \"End\"]);\n\n/**\n * Type-ahead merge-tag autocomplete for a native `<input>` / `<textarea>`.\n *\n * Drives the SAME popup controller as the rich-text (`@tiptap/suggestion`)\n * path — see `createMergeTagPopup` — so the popup's rendering, positioning,\n * keyboard behavior, and ARIA are identical across both surfaces. The only\n * field-specific work is here: detecting the open trigger in the raw string\n * (`findOpenMergeTagTrigger`), measuring the caret rect without ProseMirror\n * (`getCaretRect`), and replacing the typed fragment with the picked tag.\n */\nexport function useMergeTagAutocomplete(\n options: UseMergeTagAutocompleteOptions,\n): UseMergeTagAutocompleteReturn {\n const { elementRef, modelValue, emit, mergeTags, syntax, enabled, onInsert } =\n options;\n\n const triggerChar = getSyntaxTriggerChar(syntax);\n const closingChar = getSyntaxClosingChar(syntax);\n // Reactive: `setMergeTags` can take the list from empty to populated, and a\n // boolean captured here would leave type-ahead dead for the rest of the\n // field's life.\n const available = computed(\n () => enabled && triggerChar !== null && mergeTags.value.length > 0,\n );\n\n // Resolve the empty-state label the same way the rich-text path does —\n // inject with a null default so headless callers (no translations provider)\n // don't throw, falling back to the English string.\n const injectedTranslations = inject(TRANSLATIONS_KEY, null) as\n Translations | Ref<Translations> | null;\n const resolvedTranslations = isRef(injectedTranslations)\n ? injectedTranslations.value\n : injectedTranslations;\n const emptyText =\n resolvedTranslations?.mergeTag?.suggestionEmpty ?? \"No matching merge tags\";\n\n const popoverRoot = usePopoverRoot();\n const popup = createMergeTagPopup(emptyText, popoverRoot);\n\n let disposed = false;\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disposed = true;\n popup.close();\n });\n }\n\n function currentMatch(el: HTMLInputElement | HTMLTextAreaElement) {\n const value = el.value ?? \"\";\n const caret = el.selectionStart ?? value.length;\n return findOpenMergeTagTrigger(\n value,\n caret,\n triggerChar as string,\n closingChar,\n );\n }\n\n function applySelection(\n el: HTMLInputElement | HTMLTextAreaElement,\n match: OpenMergeTagTrigger,\n tag: MergeTag,\n ): void {\n const value = el.value ?? modelValue();\n // Absorb a matching closer sitting immediately after the caret so\n // `{{fir}}` → pick → `{{first_name}}`, never `{{first_name}}}}`.\n const afterCaret = value.slice(match.caret);\n const trailing =\n closingChar && afterCaret.startsWith(closingChar)\n ? closingChar.length\n : 0;\n const before = value.slice(0, match.triggerStart);\n const after = value.slice(match.caret + trailing);\n const newValue = before + tag.value + after;\n const newPos = before.length + tag.value.length;\n\n // Keep the field in edit mode BEFORE emitting: once the value gains a tag,\n // `hasMergeTags` flips true, and without this the field would swap to the\n // chip display and unmount the input mid-insert.\n onInsert?.();\n emit(newValue);\n popup.close();\n\n nextTick(() => {\n if (disposed) return;\n const node = elementRef.value;\n node?.focus();\n node?.setSelectionRange(newPos, newPos);\n });\n }\n\n function refresh(): void {\n if (!available.value || disposed) return;\n const el = elementRef.value;\n if (!el) {\n popup.close();\n return;\n }\n const match = currentMatch(el);\n if (!match) {\n popup.close();\n return;\n }\n\n const items = filterMergeTags(mergeTags.value, match.query);\n const getRect = () => getCaretRect(el, match.triggerStart);\n const onCommand = (tag: MergeTag) => applySelection(el, match, tag);\n\n if (popup.isOpen()) {\n popup.update({ items, getRect, onCommand });\n } else {\n popup.open({ items, getRect, anchorEl: el, onCommand });\n }\n }\n\n function handleKeydown(event: KeyboardEvent): boolean {\n if (!popup.isOpen()) return false;\n if (event.key === \"Escape\") {\n popup.close();\n return true;\n }\n if (CARET_MOVE_KEYS.has(event.key)) {\n popup.close();\n return false;\n }\n return popup.handleKeyDown(event);\n }\n\n return {\n available,\n refresh,\n handleKeydown,\n close: () => popup.close(),\n isOpen: () => popup.isOpen(),\n };\n}\n","import {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport {\n computed,\n getCurrentScope,\n nextTick,\n onScopeDispose,\n ref,\n type ComputedRef,\n type Ref,\n} from \"vue\";\nimport { useMergeTag } from \"./useMergeTag\";\nimport { useLogicTag } from \"./useLogicTag\";\nimport { useMergeTagAutocomplete } from \"./useMergeTagAutocomplete\";\n\nexport type MergeTagSegment =\n | { type: \"text\"; value: string }\n | { type: \"mergeTag\"; value: string; label: string }\n | { type: \"logicMergeTag\"; value: string; keyword: string };\n\nexport interface UseMergeTagFieldOptions {\n modelValue: () => string;\n emit: (value: string) => void;\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n}\n\nexport interface UseMergeTagFieldReturn {\n segments: ComputedRef<MergeTagSegment[]>;\n hasMergeTags: ComputedRef<boolean>;\n canRequestMergeTag: ComputedRef<boolean>;\n isRequestingMergeTag: Ref<boolean>;\n canInsertLogicTag: boolean;\n isRequestingLogicTag: Ref<boolean>;\n isEditing: Ref<boolean>;\n startEditing: () => void;\n stopEditing: () => void;\n handleInput: (event: Event) => void;\n /**\n * Returns whether the autocomplete popup consumed the event. A host with\n * keys of its own (the link dialog submits on Enter) acts only on `false`.\n */\n handleKeydown: (event: KeyboardEvent) => boolean;\n handleClick: () => void;\n handleBlur: () => void;\n clearValue: () => void;\n insertMergeTag: () => Promise<void>;\n /**\n * Swap the merge tag at `index` in {@link segments} for another one, chosen\n * through the same chooser insertion uses. No-op when the index isn't a\n * merge-tag segment, or when the user cancels.\n */\n replaceMergeTagAt: (index: number) => Promise<void>;\n insertLogicTag: () => Promise<void>;\n /** Whether type-ahead autocomplete is active for this field. */\n autocompleteAvailable: ComputedRef<boolean>;\n}\n\nexport function useMergeTagField(\n options: UseMergeTagFieldOptions,\n): UseMergeTagFieldReturn {\n const { modelValue, emit, elementRef } = options;\n\n const {\n mergeTags,\n canRequestMergeTag,\n isRequesting: isRequestingMergeTag,\n isMergeTagValue,\n getMergeTagDisplayLabel,\n findMergeTag,\n requestMergeTag,\n syntax,\n autocomplete,\n } = useMergeTag();\n\n const {\n canInsertLogicTag,\n isRequesting: isRequestingLogicTag,\n requestLogicTag,\n } = useLogicTag();\n\n const isEditing = ref(false);\n let insertingMergeTag = false;\n let disposed = false;\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disposed = true;\n });\n }\n\n // Type-ahead autocomplete for the raw <input>/<textarea>, sharing the exact\n // popup used by the rich-text editor. `onInsert` keeps the field in edit mode\n // so the freshly inserted tag stays visible instead of flipping to chips.\n const typeahead = useMergeTagAutocomplete({\n elementRef,\n modelValue,\n emit,\n mergeTags,\n syntax,\n enabled: autocomplete,\n onInsert: () => {\n isEditing.value = true;\n },\n });\n\n const segments = computed((): MergeTagSegment[] => {\n const val = modelValue();\n if (!val) return [];\n\n const result: MergeTagSegment[] = [];\n const combinedSource = `(${syntax.value.source}|${syntax.logic.source})`;\n const regex = new RegExp(combinedSource, \"g\");\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(val)) !== null) {\n if (match.index > lastIndex) {\n result.push({\n type: \"text\",\n value: val.slice(lastIndex, match.index),\n });\n }\n\n const matched = match[0];\n if (isMergeTagValue(matched)) {\n result.push({\n type: \"mergeTag\",\n value: matched,\n // A field value is a plain string, so there is no stored label to\n // fall back to — an undeclared token resolves to the placeholder\n // when the consumer hid raw tokens.\n label: getMergeTagDisplayLabel(matched),\n });\n } else if (isLogicMergeTagValue(matched, syntax)) {\n result.push({\n type: \"logicMergeTag\",\n value: matched,\n keyword: getLogicMergeTagKeyword(matched, syntax),\n });\n } else {\n result.push({ type: \"text\", value: matched });\n }\n\n lastIndex = match.index + matched.length;\n }\n\n if (lastIndex < val.length) {\n result.push({ type: \"text\", value: val.slice(lastIndex) });\n }\n\n return result;\n });\n\n const hasMergeTags = computed(() =>\n segments.value.some(\n (s) => s.type === \"mergeTag\" || s.type === \"logicMergeTag\",\n ),\n );\n\n function startEditing(): void {\n isEditing.value = true;\n nextTick(() => {\n elementRef.value?.focus();\n const len = modelValue()?.length || 0;\n elementRef.value?.setSelectionRange(len, len);\n });\n }\n\n function stopEditing(): void {\n if (insertingMergeTag) return;\n isEditing.value = false;\n }\n\n function handleInput(event: Event): void {\n emit((event.target as HTMLInputElement | HTMLTextAreaElement).value);\n typeahead.refresh();\n }\n\n /**\n * Field keydown: let the autocomplete popup consume navigation/selection\n * keys first (Arrow/Enter/Tab/Escape). If it doesn't, Escape falls through\n * to leaving edit mode.\n *\n * Escape is reported as NOT consumed even though it acts here: a host\n * dialog should close on the same press rather than swallow the first one.\n */\n function handleKeydown(event: KeyboardEvent): boolean {\n if (typeahead.handleKeydown(event)) {\n event.preventDefault();\n return true;\n }\n if (event.key === \"Escape\") {\n stopEditing();\n }\n return false;\n }\n\n /** A click may move the caret into or out of a trigger — re-evaluate. */\n function handleClick(): void {\n typeahead.refresh();\n }\n\n /** Blur closes the popup, then leaves edit mode. */\n function handleBlur(): void {\n typeahead.close();\n stopEditing();\n }\n\n function clearValue(): void {\n emit(\"\");\n }\n\n async function insertMergeTag(): Promise<void> {\n const cursorPos =\n isEditing.value && elementRef.value\n ? (elementRef.value.selectionStart ?? modelValue().length)\n : modelValue().length;\n\n insertingMergeTag = true;\n let mergeTag: Awaited<ReturnType<typeof requestMergeTag>>;\n try {\n mergeTag = await requestMergeTag();\n } finally {\n insertingMergeTag = false;\n }\n\n if (disposed) return;\n\n if (mergeTag) {\n const before = modelValue().slice(0, cursorPos);\n const after = modelValue().slice(cursorPos);\n const newValue = before + mergeTag.value + after;\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n if (disposed) return;\n const newPos = cursorPos + mergeTag.value.length;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n }\n\n async function replaceMergeTagAt(index: number): Promise<void> {\n // Snapshot before the await: `segments` is a computed over modelValue(),\n // and a collaborator edit or a parallel write can change it while the\n // chooser is open. Rebuilding from the snapshot keeps the swap addressed\n // to the tag the user actually clicked.\n const snapshot = segments.value;\n const target = snapshot[index];\n if (target?.type !== \"mergeTag\") return;\n\n // Same flag insertMergeTag uses: the chooser mounts outside the field, so\n // without it the blur handler leaves edit mode mid-pick.\n insertingMergeTag = true;\n let replacement: Awaited<ReturnType<typeof requestMergeTag>>;\n try {\n replacement = await requestMergeTag({\n reason: \"edit\",\n current: findMergeTag(target.value),\n });\n } finally {\n insertingMergeTag = false;\n }\n\n if (disposed || !replacement) return;\n\n emit(\n snapshot\n .map((segment, i) => (i === index ? replacement.value : segment.value))\n .join(\"\"),\n );\n }\n\n async function insertLogicTag(): Promise<void> {\n // Read the selection straight from the element (kept focused via the\n // button's mousedown.prevent). Null only in display mode → append at end.\n const el = elementRef.value;\n const start = el?.selectionStart ?? modelValue().length;\n const end = el?.selectionEnd ?? start;\n\n insertingMergeTag = true;\n let item: Awaited<ReturnType<typeof requestLogicTag>>;\n try {\n item = await requestLogicTag();\n } finally {\n insertingMergeTag = false;\n }\n\n if (disposed || !item) return;\n\n const val = modelValue();\n let newValue: string;\n let newPos: number;\n if (\"before\" in item) {\n // Pair: wrap the selection with before/after. With no selection\n // (start === end) the caret lands between them so the user types the\n // wrapped content — mirrors the rich-text behavior.\n newValue =\n val.slice(0, start) +\n item.before +\n val.slice(start, end) +\n item.after +\n val.slice(end);\n newPos = start + item.before.length + (end - start);\n } else {\n // Tag: insert at the cursor.\n newValue = val.slice(0, start) + item.value + val.slice(start);\n newPos = start + item.value.length;\n }\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n if (disposed) return;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n\n return {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n canInsertLogicTag,\n isRequestingLogicTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n handleKeydown,\n handleClick,\n handleBlur,\n clearValue,\n insertMergeTag,\n replaceMergeTagAt,\n insertLogicTag,\n autocompleteAvailable: typeahead.available,\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { Pencil, X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n /** Index into `segments` of the merge tag the user wants to swap. */\n (e: \"repick\", index: number): void;\n}>();\n\nconst { t, format } = useI18n();\nconst { canRepickMergeTag, showRawValue } = useMergeTag();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <!--\n role=\"group\", never role=\"button\" — the field holds its own controls (a\n button per merge tag, Edit, Clear), and a button may not contain focusable\n descendants: it announces as a control inside a control and the tab order\n reads as if you stepped into the element you just landed on. The click\n handler here is a redundant mouse convenience over the Edit button, which\n is the real, keyboard-reachable control.\n -->\n <div\n role=\"group\"\n :aria-label=\"t.mergeTag.fieldGroup\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <button\n v-if=\"seg.type === 'mergeTag' && canRepickMergeTag(seg.value)\"\n type=\"button\"\n :class=\"[\n 'tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:rounded tpl:border-none tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium',\n showRawValue ? 'tpl-tooltip' : '',\n ]\"\n :data-tooltip=\"showRawValue ? seg.value : undefined\"\n :aria-label=\"format(t.mergeTag.changeTag, { label: seg.label })\"\n data-testid=\"merge-tag-field-chip\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n @click.stop=\"emit('repick', i)\"\n >\n {{ seg.label }}\n </button>\n <!--\n No chooser can resolve this token, so it stays a plain span and the\n click falls through to the wrapper, which opens the raw editor — the\n only way left to repair it. A disabled button would swallow the click\n instead. Keyboard users reach the same editor through the Edit button.\n -->\n <span\n v-else-if=\"seg.type === 'mergeTag'\"\n :class=\"[\n 'tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium',\n showRawValue ? 'tpl-tooltip' : '',\n ]\"\n :data-tooltip=\"showRawValue ? seg.value : undefined\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-primary)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.editAsText\"\n :title=\"t.mergeTag.editAsText\"\n data-testid=\"merge-tag-field-edit\"\n @click.stop=\"onEdit\"\n >\n <Pencil :size=\"12\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-danger)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n data-testid=\"merge-tag-field-clear\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useMergeTag } from \"../composables/useMergeTag\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { Pencil, X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n /** Index into `segments` of the merge tag the user wants to swap. */\n (e: \"repick\", index: number): void;\n}>();\n\nconst { t, format } = useI18n();\nconst { canRepickMergeTag, showRawValue } = useMergeTag();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <!--\n role=\"group\", never role=\"button\" — the field holds its own controls (a\n button per merge tag, Edit, Clear), and a button may not contain focusable\n descendants: it announces as a control inside a control and the tab order\n reads as if you stepped into the element you just landed on. The click\n handler here is a redundant mouse convenience over the Edit button, which\n is the real, keyboard-reachable control.\n -->\n <div\n role=\"group\"\n :aria-label=\"t.mergeTag.fieldGroup\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <button\n v-if=\"seg.type === 'mergeTag' && canRepickMergeTag(seg.value)\"\n type=\"button\"\n :class=\"[\n 'tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:rounded tpl:border-none tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium',\n showRawValue ? 'tpl-tooltip' : '',\n ]\"\n :data-tooltip=\"showRawValue ? seg.value : undefined\"\n :aria-label=\"format(t.mergeTag.changeTag, { label: seg.label })\"\n data-testid=\"merge-tag-field-chip\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n @click.stop=\"emit('repick', i)\"\n >\n {{ seg.label }}\n </button>\n <!--\n No chooser can resolve this token, so it stays a plain span and the\n click falls through to the wrapper, which opens the raw editor — the\n only way left to repair it. A disabled button would swallow the click\n instead. Keyboard users reach the same editor through the Edit button.\n -->\n <span\n v-else-if=\"seg.type === 'mergeTag'\"\n :class=\"[\n 'tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium',\n showRawValue ? 'tpl-tooltip' : '',\n ]\"\n :data-tooltip=\"showRawValue ? seg.value : undefined\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-primary)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.editAsText\"\n :title=\"t.mergeTag.editAsText\"\n data-testid=\"merge-tag-field-edit\"\n @click.stop=\"onEdit\"\n >\n <Pencil :size=\"12\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-danger)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n data-testid=\"merge-tag-field-clear\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Braces } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst logicBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[logicBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n :disabled=\"disabled\"\n data-testid=\"field-insert-logic-button\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <Braces :size=\"12\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Braces } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst logicBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[logicBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n :disabled=\"disabled\"\n data-testid=\"field-insert-logic-button\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <Braces :size=\"12\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n</template>\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { B as e, E as t, F as n, H as r, I as i, L as a, N as o, O as s, Q as c, R as l, S as u, U as d, at as f, b as p, ct as m, d as h, f as g, g as _, h as v, l as y, lt as ee, m as b, n as te, o as ne, p as x, s as re, u as ie, ut as S, y as ae } from "./draggable-IB4x6w2O.js";
2
2
  import { t as oe } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
- import { a as se, n as ce, o as le, r as ue, t as de } from "./editor-modal-CKu8HhiB.js";
3
+ import { a as se, n as ce, o as le, r as ue, t as de } from "./editor-modal-CZQArGNj.js";
4
4
  //#region ../media-library/src/composables/useImageCrop.ts
5
5
  var fe = {
6
6
  free: void 0,
@@ -3526,4 +3526,4 @@ var Vt = ["data-tpl-theme"], Ht = { class: "tpl:shrink-0 tpl:p-5 tpl:pb-4" }, Ut
3526
3526
  //#endregion
3527
3527
  export { xn as default };
3528
3528
 
3529
- //# sourceMappingURL=MediaEditModal-Cw9MhjVz.js.map
3529
+ //# sourceMappingURL=MediaEditModal-CzUtXkpk.js.map