@templatical/editor 0.43.1 → 0.43.3

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 (256) hide show
  1. package/README.md +1 -1
  2. package/dist/{AiChatSidebar-DOCvzGBe.js → AiChatSidebar-BAAypLEP.js} +7 -7
  3. package/dist/{AiFeatureMenu-xCJeD07B.js → AiFeatureMenu-CQcrDR-J.js} +4 -4
  4. package/dist/{BlockIssueBadge-BJ3A6mqh.js → BlockIssueBadge-DYI7ic1j.js} +2 -2
  5. package/dist/{BlockPreviewCanvas-BQXfqhzu.js → BlockPreviewCanvas-x5W-Sdhg.js} +2 -2
  6. package/dist/{CloudHeaderExtras-TFmBJ0Ry.js → CloudHeaderExtras-DnLaX4ea.js} +2 -2
  7. package/dist/{CloudPanels-BnSBbYnw.js → CloudPanels-BHPG3d1o.js} +4 -4
  8. package/dist/{CollaboratorBar-7ry8EdUh.js → CollaboratorBar-DG6KroMt.js} +2 -2
  9. package/dist/{CommentsPanel-CGEhbjx0.js → CommentsPanel-Bzk6VhJg.js} +1 -1
  10. package/dist/{CommentsSidebar-CUpst9ou.js → CommentsSidebar-CaRC3c1b.js} +10 -10
  11. package/dist/{CountdownToolbar-B1a_UIuB.js → CountdownToolbar-B-pAigUh.js} +3 -3
  12. package/dist/{DesignReferenceSidebar-DkAmsBgM.js → DesignReferenceSidebar-DfuJyDBz.js} +5 -5
  13. package/dist/{IssuesPanel-lTpsCV4a.js → IssuesPanel-vWhcDIvb.js} +5 -5
  14. package/dist/{LogicTagInsertButton-B5YymR4U.js → LogicTagInsertButton-D2Fjo444.js} +3 -3
  15. package/dist/{MediaEditModal-B1X8qtJb.js → MediaEditModal-BuP8SJrv.js} +270 -230
  16. package/dist/{MediaPanels-DO30L1KS.js → MediaPanels-BkcF0r_r.js} +1 -1
  17. package/dist/{MergeTagInput-B0M_-X0K.js → MergeTagInput-fEOOY7OU.js} +1 -1
  18. package/dist/{MergeTagModeToggle-B-8W1tK7.js → MergeTagModeToggle-BOeEL52u.js} +3 -3
  19. package/dist/{ParagraphEditor-DVGeU0Am.js → ParagraphEditor-BlJGeBRY.js} +22 -22
  20. package/dist/{RestoreVersionDialog-DOhgYkiA.js → RestoreVersionDialog-DYaoFk5G.js} +3 -3
  21. package/dist/{RichTextEditorContent-AaLAArA0.js → RichTextEditorContent-GacK8qwu.js} +4 -4
  22. package/dist/{SaveBlockDialog-36MzPD2S.js → SaveBlockDialog-DR1UCtQY.js} +8 -8
  23. package/dist/{SavedBlocksBrowserModal-B5nFKszf.js → SavedBlocksBrowserModal-q_-qYW9a.js} +9 -9
  24. package/dist/{SavedBlocksPanels-BZkbP7eB.js → SavedBlocksPanels-nTH8NTAT.js} +1 -1
  25. package/dist/{SavedBlocksPickBar-ChoCMdu7.js → SavedBlocksPickBar-C7vHmc5c.js} +2 -2
  26. package/dist/{TemplateScoringPanel-C6ellAcs.js → TemplateScoringPanel-D9jeQCYl.js} +11 -11
  27. package/dist/{TemplateSettings-C_0qK-zs.js → TemplateSettings-ClfHwxUa.js} +5 -5
  28. package/dist/{TestEmailModal-CKW-VArm.js → TestEmailModal-DEnvrv38.js} +6 -6
  29. package/dist/{TestEmailPanel-BylYzjyM.js → TestEmailPanel-Ca9t6KG7.js} +1 -1
  30. package/dist/{TitleEditor-DoQ286_n.js → TitleEditor-D7mNs3s5.js} +9 -9
  31. package/dist/{ToggleSwitch-CtZ6adlX.js → ToggleSwitch-Z-rqEcig.js} +1 -1
  32. package/dist/{Toolbar-B6dG5_IP.js → Toolbar-DUgyDBkz.js} +28 -28
  33. package/dist/{TplModal-DHwQXySQ.js → TplModal-CYyFAScB.js} +1 -1
  34. package/dist/{VersionHistoryMenu-DdspCRSA.js → VersionHistoryMenu-D42tGKM6.js} +5 -5
  35. package/dist/{VersionHistoryPanels-C6QxmRUH.js → VersionHistoryPanels-o0HGvQJX.js} +1 -1
  36. package/dist/{VersionPreviewBanner-XUuLe36h.js → VersionPreviewBanner-D5RZtvUf.js} +1 -1
  37. package/dist/{WrapperBlock-BumDudbj.js → WrapperBlock-B0_vSfSD.js} +1 -1
  38. package/dist/{blockTypeIcons-Sg8SY1NQ.js → blockTypeIcons-CEk_GHmU.js} +2 -2
  39. package/dist/bundle-stats.json +6 -6
  40. package/dist/{ca-BTf1Zbqb.js → ca-CYQH3J0b.js} +6 -2
  41. package/dist/{ca--zGWn9AU.js → ca-DSwai-th.js} +1 -4
  42. package/dist/cdn/chunks/{AiChatSidebar-DiG-EQ-E.js → AiChatSidebar-oFwacME4.js} +2 -2
  43. package/dist/cdn/chunks/{AiChatSidebar-DiG-EQ-E.js.map → AiChatSidebar-oFwacME4.js.map} +1 -1
  44. package/dist/cdn/chunks/{AiFeatureMenu-4TgzmLb3.js → AiFeatureMenu-BiHFJf9i.js} +3 -3
  45. package/dist/cdn/chunks/{AiFeatureMenu-4TgzmLb3.js.map → AiFeatureMenu-BiHFJf9i.js.map} +1 -1
  46. package/dist/cdn/chunks/{BlockIssueBadge-ChQ0mPf5.js → BlockIssueBadge-CWlcDBFm.js} +2 -2
  47. package/dist/cdn/chunks/{BlockIssueBadge-ChQ0mPf5.js.map → BlockIssueBadge-CWlcDBFm.js.map} +1 -1
  48. package/dist/cdn/chunks/{BlockPreviewCanvas-BpvK3Sh9.js → BlockPreviewCanvas-CIwIA3zq.js} +3 -3
  49. package/dist/cdn/chunks/{BlockPreviewCanvas-BpvK3Sh9.js.map → BlockPreviewCanvas-CIwIA3zq.js.map} +1 -1
  50. package/dist/cdn/chunks/{CloudHeaderExtras-DqgOaWym.js → CloudHeaderExtras-BgsjwWUQ.js} +3 -3
  51. package/dist/cdn/chunks/{CloudHeaderExtras-DqgOaWym.js.map → CloudHeaderExtras-BgsjwWUQ.js.map} +1 -1
  52. package/dist/cdn/chunks/{CloudPanels-INOiwkET.js → CloudPanels-DFCwq8_J.js} +4 -4
  53. package/dist/cdn/chunks/{CloudPanels-INOiwkET.js.map → CloudPanels-DFCwq8_J.js.map} +1 -1
  54. package/dist/cdn/chunks/{CollaboratorBar-CQ9EpYKQ.js → CollaboratorBar-CiMZfMwN.js} +3 -3
  55. package/dist/cdn/chunks/{CollaboratorBar-CQ9EpYKQ.js.map → CollaboratorBar-CiMZfMwN.js.map} +1 -1
  56. package/dist/cdn/chunks/{CommentsPanel-CjhxGMjL.js → CommentsPanel-CL4grbMp.js} +2 -2
  57. package/dist/cdn/chunks/{CommentsPanel-CjhxGMjL.js.map → CommentsPanel-CL4grbMp.js.map} +1 -1
  58. package/dist/cdn/chunks/{CommentsSidebar-D9bqMnlt.js → CommentsSidebar-tWYYKv1s.js} +2 -2
  59. package/dist/cdn/chunks/{CommentsSidebar-D9bqMnlt.js.map → CommentsSidebar-tWYYKv1s.js.map} +1 -1
  60. package/dist/cdn/chunks/{CountdownToolbar-CIRx5w1U.js → CountdownToolbar-DfyqIppF.js} +4 -4
  61. package/dist/cdn/chunks/{CountdownToolbar-CIRx5w1U.js.map → CountdownToolbar-DfyqIppF.js.map} +1 -1
  62. package/dist/cdn/chunks/{DesignReferenceSidebar-C1-SbE_R.js → DesignReferenceSidebar-C6O12hvt.js} +2 -2
  63. package/dist/cdn/chunks/{DesignReferenceSidebar-C1-SbE_R.js.map → DesignReferenceSidebar-C6O12hvt.js.map} +1 -1
  64. package/dist/cdn/chunks/{IssuesPanel-qKY6UDd-.js → IssuesPanel-DYJXIwnw.js} +2 -2
  65. package/dist/cdn/chunks/{IssuesPanel-qKY6UDd-.js.map → IssuesPanel-DYJXIwnw.js.map} +1 -1
  66. package/dist/cdn/chunks/{LogicTagInsertButton-CItDS_U1.js → LogicTagInsertButton-FNMVxm_V.js} +2 -2
  67. package/dist/cdn/chunks/{LogicTagInsertButton-CItDS_U1.js.map → LogicTagInsertButton-FNMVxm_V.js.map} +1 -1
  68. package/dist/cdn/chunks/{MediaEditModal-CzUtXkpk.js → MediaEditModal-C9yZ0AJv.js} +272 -232
  69. package/dist/cdn/chunks/MediaEditModal-C9yZ0AJv.js.map +1 -0
  70. package/dist/cdn/chunks/{MediaPanels-DOLsh7WC.js → MediaPanels-DiKU1r9P.js} +2 -2
  71. package/dist/cdn/chunks/{MediaPanels-DOLsh7WC.js.map → MediaPanels-DiKU1r9P.js.map} +1 -1
  72. package/dist/cdn/chunks/{MergeTagInput-B33u3nxj.js → MergeTagInput-CSC64xNL.js} +2 -2
  73. package/dist/cdn/chunks/{MergeTagInput-B33u3nxj.js.map → MergeTagInput-CSC64xNL.js.map} +1 -1
  74. package/dist/cdn/chunks/{MergeTagModeToggle-Bz1CH3NT.js → MergeTagModeToggle-027RMXDP.js} +2 -2
  75. package/dist/cdn/chunks/{MergeTagModeToggle-Bz1CH3NT.js.map → MergeTagModeToggle-027RMXDP.js.map} +1 -1
  76. package/dist/cdn/chunks/{ParagraphEditor-B6A4yQ0A.js → ParagraphEditor-0Z26-N5u.js} +19 -19
  77. package/dist/cdn/chunks/{ParagraphEditor-B6A4yQ0A.js.map → ParagraphEditor-0Z26-N5u.js.map} +1 -1
  78. package/dist/cdn/chunks/{RestoreVersionDialog-B9rujl9w.js → RestoreVersionDialog-Dd1v1KLK.js} +3 -3
  79. package/dist/cdn/chunks/{RestoreVersionDialog-B9rujl9w.js.map → RestoreVersionDialog-Dd1v1KLK.js.map} +1 -1
  80. package/dist/cdn/chunks/{RichTextEditorContent-dOg1xFqv.js → RichTextEditorContent-BOZ_5I76.js} +4 -4
  81. package/dist/cdn/chunks/{RichTextEditorContent-dOg1xFqv.js.map → RichTextEditorContent-BOZ_5I76.js.map} +1 -1
  82. package/dist/cdn/chunks/{SaveBlockDialog-BNAeb6--.js → SaveBlockDialog-B6WQwNJD.js} +6 -6
  83. package/dist/cdn/chunks/{SaveBlockDialog-BNAeb6--.js.map → SaveBlockDialog-B6WQwNJD.js.map} +1 -1
  84. package/dist/cdn/chunks/{SavedBlocksBrowserModal--FoVqEnT.js → SavedBlocksBrowserModal-C015fVMV.js} +6 -6
  85. package/dist/cdn/chunks/{SavedBlocksBrowserModal--FoVqEnT.js.map → SavedBlocksBrowserModal-C015fVMV.js.map} +1 -1
  86. package/dist/cdn/chunks/{SavedBlocksPanels-qKyPnVgk.js → SavedBlocksPanels-DZmfXW4x.js} +2 -2
  87. package/dist/cdn/chunks/{SavedBlocksPanels-qKyPnVgk.js.map → SavedBlocksPanels-DZmfXW4x.js.map} +1 -1
  88. package/dist/cdn/chunks/{SavedBlocksPickBar-BvLwca4B.js → SavedBlocksPickBar-DtCQFCCW.js} +3 -3
  89. package/dist/cdn/chunks/{SavedBlocksPickBar-BvLwca4B.js.map → SavedBlocksPickBar-DtCQFCCW.js.map} +1 -1
  90. package/dist/cdn/chunks/{TemplateScoringPanel-ebsrVge3.js → TemplateScoringPanel-CeCVyNV1.js} +2 -2
  91. package/dist/cdn/chunks/{TemplateScoringPanel-ebsrVge3.js.map → TemplateScoringPanel-CeCVyNV1.js.map} +1 -1
  92. package/dist/cdn/chunks/{TemplateSettings-BaEVdUTM.js → TemplateSettings-CuXwthI6.js} +4 -4
  93. package/dist/cdn/chunks/{TemplateSettings-BaEVdUTM.js.map → TemplateSettings-CuXwthI6.js.map} +1 -1
  94. package/dist/cdn/chunks/{TestEmailModal-DpYirvEL.js → TestEmailModal-DV1XKWRh.js} +6 -6
  95. package/dist/cdn/chunks/{TestEmailModal-DpYirvEL.js.map → TestEmailModal-DV1XKWRh.js.map} +1 -1
  96. package/dist/cdn/chunks/{TestEmailPanel-PAT70a2g.js → TestEmailPanel-CnVW00RI.js} +2 -2
  97. package/dist/cdn/chunks/{TestEmailPanel-PAT70a2g.js.map → TestEmailPanel-CnVW00RI.js.map} +1 -1
  98. package/dist/cdn/chunks/{TitleEditor-Dc7YB6Uy.js → TitleEditor-B9sKbRd4.js} +8 -8
  99. package/dist/cdn/chunks/{TitleEditor-Dc7YB6Uy.js.map → TitleEditor-B9sKbRd4.js.map} +1 -1
  100. package/dist/cdn/chunks/{ToggleSwitch-D-8dCEbI.js → ToggleSwitch-eHkFBx4o.js} +2 -2
  101. package/dist/cdn/chunks/{ToggleSwitch-D-8dCEbI.js.map → ToggleSwitch-eHkFBx4o.js.map} +1 -1
  102. package/dist/cdn/chunks/{Toolbar-BDzhrmGV.js → Toolbar-DeKbYejp.js} +15 -15
  103. package/dist/cdn/chunks/{Toolbar-BDzhrmGV.js.map → Toolbar-DeKbYejp.js.map} +1 -1
  104. package/dist/cdn/chunks/{TplModal-BbHRUc1Y.js → TplModal-Dqq-Ermd.js} +2 -2
  105. package/dist/cdn/chunks/{TplModal-BbHRUc1Y.js.map → TplModal-Dqq-Ermd.js.map} +1 -1
  106. package/dist/cdn/chunks/{VersionHistoryMenu-C_bBKOYU.js → VersionHistoryMenu-Kf4QFm7m.js} +2 -2
  107. package/dist/cdn/chunks/{VersionHistoryMenu-C_bBKOYU.js.map → VersionHistoryMenu-Kf4QFm7m.js.map} +1 -1
  108. package/dist/cdn/chunks/{VersionHistoryPanels-BoJsPIKa.js → VersionHistoryPanels-Cz8rde-s.js} +2 -2
  109. package/dist/cdn/chunks/{VersionHistoryPanels-BoJsPIKa.js.map → VersionHistoryPanels-Cz8rde-s.js.map} +1 -1
  110. package/dist/cdn/chunks/{VersionPreviewBanner-CKyrcgxJ.js → VersionPreviewBanner-DNYHqbj9.js} +2 -2
  111. package/dist/cdn/chunks/{VersionPreviewBanner-CKyrcgxJ.js.map → VersionPreviewBanner-DNYHqbj9.js.map} +1 -1
  112. package/dist/cdn/chunks/{WrapperBlock-CGg6rBnx.js → WrapperBlock-DIZHUwFn.js} +2 -2
  113. package/dist/cdn/chunks/{WrapperBlock-CGg6rBnx.js.map → WrapperBlock-DIZHUwFn.js.map} +1 -1
  114. package/dist/cdn/chunks/{blockTypeIcons-CdfCfqHE.js → blockTypeIcons-hgzxMFWX.js} +2 -2
  115. package/dist/cdn/chunks/{blockTypeIcons-CdfCfqHE.js.map → blockTypeIcons-hgzxMFWX.js.map} +1 -1
  116. package/dist/cdn/chunks/{ca-BTf1Zbqb.js → ca-CYQH3J0b.js} +7 -3
  117. package/dist/cdn/chunks/ca-CYQH3J0b.js.map +1 -0
  118. package/dist/cdn/chunks/{ca--zGWn9AU.js → ca-DSwai-th.js} +2 -5
  119. package/dist/cdn/chunks/ca-DSwai-th.js.map +1 -0
  120. package/dist/cdn/chunks/{de-BRNLKcDl.js → de-DOhKuphf.js} +2 -5
  121. package/dist/cdn/chunks/de-DOhKuphf.js.map +1 -0
  122. package/dist/cdn/chunks/{de-xzrh9PA3.js → de-JvCTMJPy.js} +7 -3
  123. package/dist/cdn/chunks/de-JvCTMJPy.js.map +1 -0
  124. package/dist/cdn/chunks/dist-BRZhN8qJ.js.map +1 -1
  125. package/dist/cdn/chunks/dist-CXB2_kBa.js.map +1 -1
  126. package/dist/cdn/chunks/draggable-IB4x6w2O.js.map +1 -1
  127. package/dist/cdn/chunks/{editor-modal-CZQArGNj.js → editor-modal-cKeJ4wC2.js} +472 -428
  128. package/dist/cdn/chunks/editor-modal-cKeJ4wC2.js.map +1 -0
  129. package/dist/cdn/chunks/{en-JJjqHMjy.js → en-BE28ZcUF.js} +2 -5
  130. package/dist/cdn/chunks/en-BE28ZcUF.js.map +1 -0
  131. package/dist/cdn/chunks/en-CoNe1psX.js +2 -0
  132. package/dist/cdn/chunks/{es-BT4LSpE2.js → es-CAv-Vldw.js} +7 -3
  133. package/dist/cdn/chunks/es-CAv-Vldw.js.map +1 -0
  134. package/dist/cdn/chunks/{es-BIh8BxV9.js → es-oQI-dfx2.js} +2 -5
  135. package/dist/cdn/chunks/es-oQI-dfx2.js.map +1 -0
  136. package/dist/cdn/chunks/{extensions-Dy251lPd.js → extensions-rPP7qRRm.js} +3 -3
  137. package/dist/cdn/chunks/{extensions-Dy251lPd.js.map → extensions-rPP7qRRm.js.map} +1 -1
  138. package/dist/{fr-CI7OJoql.js → cdn/chunks/fr-DVPtxpR6.js} +8 -2
  139. package/dist/cdn/chunks/fr-DVPtxpR6.js.map +1 -0
  140. package/dist/cdn/chunks/{fr-C5VUZogP.js → fr-rzLdCMSG.js} +2 -5
  141. package/dist/cdn/chunks/fr-rzLdCMSG.js.map +1 -0
  142. package/dist/cdn/chunks/htmlparser-ClUZ6waR.js.map +1 -1
  143. package/dist/cdn/chunks/{icons-CQnjugjy.js → icons-BF0mJX0V.js} +18 -18
  144. package/dist/cdn/chunks/{icons-CQnjugjy.js.map → icons-BF0mJX0V.js.map} +1 -1
  145. package/dist/{ja-DhlWUfke.js → cdn/chunks/ja-dcWzjStz.js} +8 -2
  146. package/dist/cdn/chunks/ja-dcWzjStz.js.map +1 -0
  147. package/dist/{ja-x5JYxaCW.js → cdn/chunks/ja-dyFc6Ghz.js} +3 -4
  148. package/dist/cdn/chunks/ja-dyFc6Ghz.js.map +1 -0
  149. package/dist/cdn/chunks/{liquid.browser-BypTT2oc.js → liquid.browser-DXPjcePu.js} +758 -719
  150. package/dist/cdn/chunks/liquid.browser-DXPjcePu.js.map +1 -0
  151. package/dist/cdn/chunks/{nl-UXxFyXV6.js → nl-BcQhRqKR.js} +2 -5
  152. package/dist/cdn/chunks/nl-BcQhRqKR.js.map +1 -0
  153. package/dist/{nl-D8y5OWGB.js → cdn/chunks/nl-LZNqQgE4.js} +8 -2
  154. package/dist/cdn/chunks/nl-LZNqQgE4.js.map +1 -0
  155. package/dist/cdn/chunks/{pt-BR-DMCbUBvJ.js → pt-BR-D6P_mNwA.js} +7 -3
  156. package/dist/cdn/chunks/pt-BR-D6P_mNwA.js.map +1 -0
  157. package/dist/cdn/chunks/{pt-BR-DHm3SP6W.js → pt-BR-UombhTMO.js} +2 -5
  158. package/dist/cdn/chunks/pt-BR-UombhTMO.js.map +1 -0
  159. package/dist/cdn/chunks/pusher-BbIHJKf3.js.map +1 -1
  160. package/dist/cdn/chunks/{src-BwsQ27UT.js → src-V9_ODH_1.js} +3 -3
  161. package/dist/cdn/chunks/{src-BwsQ27UT.js.map → src-V9_ODH_1.js.map} +1 -1
  162. package/dist/cdn/chunks/{tiptap-D1u8JIUW.js → tiptap-02G9FGkl.js} +3726 -3552
  163. package/dist/cdn/chunks/tiptap-02G9FGkl.js.map +1 -0
  164. package/dist/cdn/chunks/{useEditorCore-DoX2RgTl.js → useEditorCore-DWvAia8X.js} +14 -14
  165. package/dist/cdn/chunks/{useEditorCore-DoX2RgTl.js.map → useEditorCore-DWvAia8X.js.map} +1 -1
  166. package/dist/cdn/editor.css +1 -1
  167. package/dist/cdn/editor.js +337 -359
  168. package/dist/cdn/editor.js.map +1 -1
  169. package/dist/{check-BMx_TLAS.js → check-DEjsanPt.js} +1 -1
  170. package/dist/{chevron-down-KZf5mh9_.js → chevron-down-Cv11TeKQ.js} +1 -1
  171. package/dist/{chevron-right-BLZ60Vxw.js → chevron-right-R8dOpzBZ.js} +1 -1
  172. package/dist/{chevron-up-B9ynB8eU.js → chevron-up-DsDEytX1.js} +1 -1
  173. package/dist/{circle-alert-pjvFFDrF.js → circle-alert-B_NqWETg.js} +1 -1
  174. package/dist/{clock-CiiTBfF3.js → clock-CDGKPx_X.js} +1 -1
  175. package/dist/{copy-BF0_pQjH.js → copy-Dv2VltLv.js} +1 -1
  176. package/dist/{createLucideIcon-C2YOVVWx.js → createLucideIcon-MrFrvtkF.js} +5 -5
  177. package/dist/{de-BRNLKcDl.js → de-DOhKuphf.js} +1 -4
  178. package/dist/{de-xzrh9PA3.js → de-JvCTMJPy.js} +6 -2
  179. package/dist/{dist-CxmLZQso.js → dist-8By0p8ah.js} +5 -5
  180. package/dist/{dist-W_ERo6c_.js → dist-BYg1EL5g.js} +5 -5
  181. package/dist/{dist-C2Ge44fD.js → dist-BgSLW3Hn.js} +1704 -1303
  182. package/dist/{dist-B0dkvy7z.js → dist-CWc7nIkA.js} +5 -5
  183. package/dist/{dist-CIxmOOSG.js → dist-Ceew_u40.js} +448 -699
  184. package/dist/{dist-BwoEME8p.js → dist-CmwBvyW0.js} +172 -171
  185. package/dist/{dist-CJ7RZARo.js → dist-Czzst9ng.js} +302 -279
  186. package/dist/dist-D2m55Lvs.js +5 -0
  187. package/dist/{dist-p-LkiWIQ.js → dist-D4HP93ZF.js} +13 -13
  188. package/dist/{dist-BPWuzECC.js → dist-DdSWMtEP.js} +6 -6
  189. package/dist/{dist-DRmafAUz.js → dist-DmQckB4p.js} +29 -29
  190. package/dist/dist-Dw2_11LA.js +5 -0
  191. package/dist/{editor-modal-99FB40dl.js → editor-modal-DmgxKgII.js} +484 -440
  192. package/dist/{en-JJjqHMjy.js → en-BE28ZcUF.js} +1 -4
  193. package/dist/en-DXYNty6E.js +2 -0
  194. package/dist/{es-BT4LSpE2.js → es-CAv-Vldw.js} +6 -2
  195. package/dist/{es-BIh8BxV9.js → es-oQI-dfx2.js} +1 -4
  196. package/dist/{extensions-DqQXxrvP.js → extensions-CkwL1oWn.js} +61 -61
  197. package/dist/{eye-RdXYgrQZ.js → eye-BVxcDaZZ.js} +1 -1
  198. package/dist/{cdn/chunks/fr-CI7OJoql.js → fr-DVPtxpR6.js} +6 -4
  199. package/dist/{fr-C5VUZogP.js → fr-rzLdCMSG.js} +1 -4
  200. package/dist/{image-up-BwdoReQt.js → image-up-CnC4n-8q.js} +1 -1
  201. package/dist/index.d.ts +7 -1
  202. package/dist/{info-zFMSGrL1.js → info-q7gVjMBC.js} +1 -1
  203. package/dist/{cdn/chunks/ja-DhlWUfke.js → ja-dcWzjStz.js} +6 -4
  204. package/dist/{cdn/chunks/ja-x5JYxaCW.js → ja-dyFc6Ghz.js} +1 -6
  205. package/dist/{link-D7orqFBT.js → link-B1-UqOP6.js} +1 -1
  206. package/dist/{liquid.browser-wPG0n4Cs.js → liquid.browser-Cs4gOjeX.js} +757 -718
  207. package/dist/{list-BGt4Pgxc.js → list-BvlNrd6M.js} +1 -1
  208. package/dist/{loader-circle-BVBgkRGz.js → loader-circle-CJ8klGfl.js} +1 -1
  209. package/dist/{message-circle-D8YLsarv.js → message-circle-Bgepipq_.js} +1 -1
  210. package/dist/{nl-UXxFyXV6.js → nl-BcQhRqKR.js} +1 -4
  211. package/dist/{cdn/chunks/nl-D8y5OWGB.js → nl-LZNqQgE4.js} +6 -4
  212. package/dist/{package-CuFvk0L5.js → package-BYIdon2Q.js} +1 -1
  213. package/dist/{pencil-DRYbmZW7.js → pencil-Bhs9MNTT.js} +1 -1
  214. package/dist/{plus-Dv0b3kh0.js → plus-C0eGj8vT.js} +1 -1
  215. package/dist/{pt-BR-DMCbUBvJ.js → pt-BR-D6P_mNwA.js} +6 -2
  216. package/dist/{pt-BR-DHm3SP6W.js → pt-BR-UombhTMO.js} +1 -4
  217. package/dist/{refresh-cw-gYTIS5bJ.js → refresh-cw-BQTOIZKD.js} +1 -1
  218. package/dist/{search-D84_Ki_a.js → search-CeT7yRBa.js} +1 -1
  219. package/dist/{send-Cn-8_H_U.js → send-D7ck2xEq.js} +1 -1
  220. package/dist/{shield-check-BOoVWJtD.js → shield-check-CF_VbIbH.js} +1 -1
  221. package/dist/{smartphone-4Ej3PBw5.js → smartphone-CkVOj7Qn.js} +1 -1
  222. package/dist/{socialIcons-CzRJskEk.js → socialIcons-QYtdurnO.js} +1 -1
  223. package/dist/{sparkles-D3vnNe7O.js → sparkles-Cfb-Kkgv.js} +1 -1
  224. package/dist/style.css +1 -1
  225. package/dist/templatical-editor.js +475 -497
  226. package/dist/{text-align-start-Ci2h_Xly.js → text-align-start-CkFs2Zig.js} +13 -13
  227. package/dist/{trash-wmi4UKvv.js → trash-C2LPw5pa.js} +1 -1
  228. package/dist/{triangle-alert-DJ0r2wmQ.js → triangle-alert-gaUz4rum.js} +1 -1
  229. package/dist/{upload-XMde-TXf.js → upload-C_DYZD_p.js} +1 -1
  230. package/dist/{useEditorCore-CAhRAya8.js → useEditorCore-Cmv5ll-D.js} +20 -20
  231. package/dist/{useScrollToBlock-B4F7KCrf.js → useScrollToBlock-BsPYYJ8h.js} +1 -1
  232. package/dist/{x-BWrtpVl-.js → x-JhKma5YO.js} +1 -1
  233. package/package.json +25 -25
  234. package/dist/cdn/chunks/MediaEditModal-CzUtXkpk.js.map +0 -1
  235. package/dist/cdn/chunks/ca--zGWn9AU.js.map +0 -1
  236. package/dist/cdn/chunks/ca-BTf1Zbqb.js.map +0 -1
  237. package/dist/cdn/chunks/de-BRNLKcDl.js.map +0 -1
  238. package/dist/cdn/chunks/de-xzrh9PA3.js.map +0 -1
  239. package/dist/cdn/chunks/editor-modal-CZQArGNj.js.map +0 -1
  240. package/dist/cdn/chunks/en-9unxBjV8.js +0 -2
  241. package/dist/cdn/chunks/en-JJjqHMjy.js.map +0 -1
  242. package/dist/cdn/chunks/es-BIh8BxV9.js.map +0 -1
  243. package/dist/cdn/chunks/es-BT4LSpE2.js.map +0 -1
  244. package/dist/cdn/chunks/fr-C5VUZogP.js.map +0 -1
  245. package/dist/cdn/chunks/fr-CI7OJoql.js.map +0 -1
  246. package/dist/cdn/chunks/ja-DhlWUfke.js.map +0 -1
  247. package/dist/cdn/chunks/ja-x5JYxaCW.js.map +0 -1
  248. package/dist/cdn/chunks/liquid.browser-BypTT2oc.js.map +0 -1
  249. package/dist/cdn/chunks/nl-D8y5OWGB.js.map +0 -1
  250. package/dist/cdn/chunks/nl-UXxFyXV6.js.map +0 -1
  251. package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +0 -1
  252. package/dist/cdn/chunks/pt-BR-DMCbUBvJ.js.map +0 -1
  253. package/dist/cdn/chunks/tiptap-D1u8JIUW.js.map +0 -1
  254. package/dist/dist-BUHovM4k.js +0 -5
  255. package/dist/dist-DdOAotfz.js +0 -5
  256. package/dist/en-BvOjRLiw.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"Toolbar-BDzhrmGV.js","names":["_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeStyle","_createCommentVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_createBlock","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_createVNode","LinkToggle","_normalizeClass","SlidingPillSelect","_createCommentVNode","NumberWithSuffix","_Fragment","ColorPicker","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","LinkToggle","_unref","_createBlock","NumberWithSuffix","_Fragment","_renderList","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","DEFAULT_CUSTOM_WIDTH","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","MergeTagInput","_createBlock","ToggleSwitch","_createCommentVNode","ColorPicker","BorderControl","RadiusControl","SlidingPillSelect","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_normalizeClass","_createVNode","_createBlock","_sfc_main","$emit","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createVNode","_unref","_toDisplayString","_withDirectives","_renderSlot","_vShow","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_openBlock","_createElementBlock","_normalizeClass","_createVNode","CollapsibleSection","_unref","_withCtx","SpacingControl","_createElementVNode","_toDisplayString","ColorPicker","_Fragment","_renderList","ToggleSwitch","_createBlock","_resolveDynamicComponent","_createTextVNode","_createCommentVNode","_withDirectives","_vModelText","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_unref","_createVNode","ToggleSwitch","_withCtx","_createTextVNode","_toDisplayString","_createBlock","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_createTextVNode","_toDisplayString","_createVNode","_createCommentVNode","_renderSlot","_sfc_main","_openBlock","_createBlock","FieldWrapper","_withCtx","_createVNode","ColorPicker","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_unref","_createElementBlock","_Fragment","_createVNode","_createTextVNode","_toDisplayString","_createCommentVNode","_normalizeClass","inputClass","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_normalizeClass","_unref","inputClass","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_createElementBlock","_Fragment","_renderList","_toDisplayString","_unref","_createVNode","Trash2","_createCommentVNode","_resolveDynamicComponent","_normalizeClass","_createTextVNode","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_normalizeClass","_unref","inputClass","_createElementBlock","_Fragment","_renderList","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementBlock","_normalizeClass","_unref","inputClass","MergeTagInput","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementBlock","_unref","_normalizeClass","MergeTagTextarea","_sfc_main","TextField","TextareaField","ImageField","ColorField","NumberField","SelectField","BooleanField","RepeatableField","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_createCommentVNode","_createVNode","_createTextVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","SlidingPillSelect","ColorPicker","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","_createTextVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","DEFAULT_CUSTOM_HEIGHT","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","_createTextVNode","_createCommentVNode","MergeTagInput","inputClass","ToggleSwitch","_withCtx","RadiusControl","BorderControl","SlidingPillSelect","_createBlock","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_unref","_toDisplayString","_createCommentVNode","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","AlignLeft","AlignCenter","AlignRight","_openBlock","_createElementBlock","_Fragment","_createVNode","FieldRow","_unref","_withCtx","_createElementVNode","_renderList","_normalizeClass","inputClass","MergeTagInput","_createBlock","ToggleSwitch","_toDisplayString","ColorPicker","_createTextVNode","NumberWithSuffix","SlidingPillSelect","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","ToggleSwitch","_createCommentVNode","RadiusControl","BorderControl","ColorPicker","SpacingControl","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_renderList","inputClass","_createVNode","MergeTagInput","_createTextVNode","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","ToggleSwitch","ColorPicker","_createCommentVNode","inputClass","_renderList","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","ColorPicker","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","MergeTagInput","_createBlock","ToggleSwitch","_createCommentVNode","_createTextVNode","inputClass","SlidingPillSelect","_sfc_main","_openBlock","_createElementBlock","_unref","_createElementVNode","_createBlock","_resolveDynamicComponent","_createCommentVNode","_toDisplayString","_createVNode","Trash2","CustomBlockToolbar","SectionToolbar","TitleToolbar","ParagraphToolbar","ImageToolbar","VideoToolbar","ButtonToolbar","DividerToolbar","SocialToolbar","MenuToolbar","TableToolbar","SpacerToolbar","HtmlToolbar","CommonBlockSettings","_sfc_main","__exportAll","watch","inject","renderList","withCtx","renderSlot","openBlock","ref","resolveDynamicComponent","defineComponent","withDirectives","reactive","unref","createVNode","normalizeClass","computed","createCommentVNode","normalizeStyle","createBlock","vModelText","createBaseVNode","vShow","Fragment","toDisplayString","defineAsyncComponent","createTextVNode","layoutWrapsSlot","generateId","isCustomBlock","BORDER_CORNERS","RICH_TEXT_SPACING","containsMergeTag","BORDER_SIDES","SYNTAX_PRESETS","toBorderRadiusCss","uniformBorder","resolveFieldColors","useDataSourceFetch","useImageDrop","socialPlatformOptions","getBlockTypeLabel","useTimeoutFn","requireInject","LAYOUT_KEY","SECTION_WRAPPER_KEY","ON_REQUEST_MEDIA_KEY","MERGE_TAG_SYNTAX_KEY","CAN_DROP_MEDIA_KEY","COLORS_KEY","DISPLAY_CONDITIONS_KEY","ALLOW_CUSTOM_CONDITIONS_KEY","CUSTOM_BLOCK_DEFINITIONS_KEY","FONTS_MANAGER_KEY","useI18n","DEFAULT_RESOLVED_COLORS","RefreshCw","Copy","ChevronDown","CircleAlert","Smartphone","Plus","TextAlignEnd","LoaderCircle","Unlink","Monitor","Trash","Info","TextAlignStart","LockOpen","Code","Link","X","Image","Lock","Upload","Minus","TextAlignCenter","_plugin_vue_export_helper_default","useAliveFlag","monoTextareaClass","removeItemBtnClass","addItemBtnClass","labelClass","inputSuffixClass","inputGroupInputClass","inputClass$1","DEFAULT_TABLE_ROW_BG","primaryBtnClass","blockTypeIcons","ColorPicker_default","MergeTagTextarea_default","ToggleSwitch_default","MergeTagInput_default","_hoisted_1$30","_hoisted_2$24","SlidingPillSelect_default","__emit","props","__props","emit","selectedIndex","o","pillOffset","createElementBlock","option","_hoisted_1$29","_hoisted_2$23","NumberWithSuffix_default","onInput","el","event","_hoisted_1$28","LinkToggle_default","_cache","_hoisted_1$27","_hoisted_2$22","_hoisted_3$20","_hoisted_4$17","_hoisted_5$16","_hoisted_6$13","BorderControl_default","DEFAULT_SIDE","sameSide","a","b","isUniform","border","side","linked","uniform","activeSide","current","isDrawn","setSide","value","next","anyDrawn","updateWidth","width","updateField","patch","toggleLinked","source","sideOptions","t","$event","_hoisted_1$26","_hoisted_2$21","_hoisted_3$19","_hoisted_4$16","_hoisted_5$15","RadiusControl_default","CORNER_LABEL_KEYS","GRID_ORDER","corners","radius","corner","isValid","updateAll","updateCorner","_hoisted_1$25","_hoisted_2$20","_hoisted_3$18","_hoisted_4$15","_hoisted_5$14","_hoisted_6$12","_hoisted_7$11","_hoisted_8$10","_hoisted_9$8","_hoisted_10$8","_hoisted_11$7","_hoisted_12$7","_hoisted_13$6","_hoisted_14$6","_hoisted_15$6","_hoisted_16$6","_hoisted_17$6","_hoisted_18$5","_hoisted_19$3","DEFAULT_CUSTOM_WIDTH$1","ButtonToolbar_default","widthMode","field","updateWidthMode","mode","updateCustomWidth","n","raw","font","_hoisted_1$24","_hoisted_2$19","_hoisted_3$17","_hoisted_4$14","_hoisted_5$13","_hoisted_6$11","_hoisted_7$10","_hoisted_8$9","_hoisted_9$7","_hoisted_10$7","_hoisted_11$6","_hoisted_12$6","_hoisted_13$5","_hoisted_14$5","_hoisted_15$5","_hoisted_16$5","_hoisted_17$5","_hoisted_18$4","_hoisted_19$2","_hoisted_20$2","_hoisted_21$2","stepperBtnClass","inputClass","SpacingControl_default","locked","updateValue","newValue","direction","delta","setDirectValue","toggleLock","_hoisted_1$23","_hoisted_2$18","CollapsibleSection_default","_ctx","_hoisted_1$22","_hoisted_2$17","_hoisted_3$16","_hoisted_4$13","_hoisted_5$12","_hoisted_6$10","_hoisted_7$9","_hoisted_8$8","_hoisted_9$6","_hoisted_10$6","_hoisted_11$5","_hoisted_12$5","_hoisted_13$4","_hoisted_14$4","_hoisted_15$4","_hoisted_16$4","_hoisted_17$4","_hoisted_18$3","CommonBlockSettings_default","displayConditions","allowCustomConditions","openSections","customConditionMode","customBefore","customAfter","VISIBILITY_ITEMS","toggleSection","key","hasDisplayConditions","isCustomCondition","c","startCustomCondition","applyCustomCondition","condition","groupedDisplayConditions","groups","group","updateStyle","isVisible","toggleVisibility","item","label","conditions","_hoisted_1$21","BooleanField_default","_hoisted_1$20","_hoisted_2$16","_hoisted_3$15","_hoisted_4$12","_hoisted_5$11","FieldWrapper_default","ColorField_default","editorColors","colors","_hoisted_1$19","_hoisted_2$15","_hoisted_3$14","ImageField_default","onRequestMedia","canDropMedia","aliveFlag","canBrowseMedia","browseMedia","result","dropZoneRef","isUploading","dropEnabled","uploadDroppedFiles","isOver","_hoisted_1$18","NumberField_default","_hoisted_1$17","_hoisted_2$14","_hoisted_3$13","_hoisted_4$11","_hoisted_5$10","_hoisted_6$9","RepeatableField_default","items","canAdd","canRemove","addItem","newItem","subField","removeItem","updated","index","updateItemField","resolveFieldComponent","format","_hoisted_1$16","_hoisted_2$13","SelectField_default","_hoisted_1$15","TextField_default","_hoisted_1$14","readOnlyTextareaClass","fieldComponentMap","type","_hoisted_1$13","_hoisted_2$12","_hoisted_3$12","_hoisted_4$10","_hoisted_5$9","_hoisted_6$8","_hoisted_7$8","_hoisted_8$7","CustomBlockToolbar_default","customBlockDefinitions","definition","d","blockRef","fetchData","fieldValues","fetched","isFieldReadOnly","hasDataSource","needsFetch","isFetching","args","fetchError","_hoisted_1$12","_hoisted_2$11","_hoisted_3$11","_hoisted_4$9","_hoisted_5$8","DividerToolbar_default","_hoisted_1$11","_hoisted_2$10","_hoisted_3$10","HtmlToolbar_default","_hoisted_1$10","_hoisted_2$9","_hoisted_3$9","_hoisted_4$8","_hoisted_5$7","_hoisted_6$7","_hoisted_7$7","_hoisted_8$6","_hoisted_9$5","_hoisted_10$5","_hoisted_11$4","_hoisted_12$4","_hoisted_13$3","_hoisted_14$3","_hoisted_15$3","_hoisted_16$3","_hoisted_17$3","_hoisted_18$2","_hoisted_19$1","_hoisted_20$1","_hoisted_21$1","DEFAULT_CUSTOM_WIDTH","DEFAULT_CUSTOM_HEIGHT$1","ImageToolbar_default","mergeTagSyntax","pulseSrc","pulseAlt","startPulseSrc","WIDTH_PRESETS","w","heightMode","updateHeightMode","updateCustomHeight","updateBorderRadius","openMediaBrowser","hasMergeTagSrc","\"color\"","_hoisted_1$9","FieldRow_default","_hoisted_1$8","_hoisted_2$8","_hoisted_3$8","_hoisted_4$7","_hoisted_5$6","_hoisted_6$6","_hoisted_7$6","_hoisted_8$5","_hoisted_9$4","_hoisted_10$4","MenuToolbar_default","ITEM_TOGGLES","ALIGN_OPTIONS","addMenuItem","updateMenuItem","updatedItems","itemId","removeMenuItem","toggle","_hoisted_1$7","_hoisted_2$7","_hoisted_3$7","ParagraphToolbar_default","expectedColumnCount","layout","rebalanceColumnChildren","target","padding","kept","overflow","merged","_hoisted_1$6","_hoisted_2$6","_hoisted_3$6","_hoisted_4$6","_hoisted_5$5","_hoisted_6$5","_hoisted_7$5","SectionToolbar_default","sectionWrapper","wrapsSlot","showWrapperPanel","disableTurnOn","showNote","columnOptions","handleColumnsChange","columns","children","handleStackOnMobileChange","checked","handleBorderRadiusChange","setWrapperEnabled","enabled","updateWrapper","handleWrapperPadding","handleWrapperRadius","_hoisted_1$5","_hoisted_2$5","_hoisted_3$5","_hoisted_4$5","_hoisted_5$4","_hoisted_6$4","_hoisted_7$4","_hoisted_8$4","_hoisted_9$3","_hoisted_10$3","_hoisted_11$3","_hoisted_12$3","_hoisted_13$2","_hoisted_14$2","_hoisted_15$2","_hoisted_16$2","_hoisted_17$2","_hoisted_18$1","SocialToolbar_default","addSocialIcon","newIcon","updateSocialIcon","updatedIcons","icon","iconId","removeSocialIcon","platform","_hoisted_1$4","_hoisted_2$4","_hoisted_3$4","_hoisted_4$4","SpacerToolbar_default","_hoisted_1$3","_hoisted_2$3","_hoisted_3$3","_hoisted_4$3","_hoisted_5$3","_hoisted_6$3","_hoisted_7$3","_hoisted_8$3","_hoisted_9$2","_hoisted_10$2","_hoisted_11$2","_hoisted_12$2","_hoisted_13$1","_hoisted_14$1","_hoisted_15$1","_hoisted_16$1","_hoisted_17$1","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_30","TableToolbar_default","tableColumnCount","addTableRow","columnCount","newRow","removeTableRow","row","rowId","addTableColumn","updatedRows","removeTableColumn","i","colIndex","_hoisted_1$2","_hoisted_2$2","_hoisted_3$2","_hoisted_4$2","_hoisted_5$2","_hoisted_6$2","_hoisted_7$2","_hoisted_8$2","_hoisted_9$1","_hoisted_10$1","_hoisted_11$1","_hoisted_12$1","TitleToolbar_default","_hoisted_1$1","_hoisted_2$1","_hoisted_3$1","_hoisted_4$1","_hoisted_5$1","_hoisted_6$1","_hoisted_7$1","_hoisted_8$1","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","DEFAULT_CUSTOM_HEIGHT","VideoToolbar_default","urlHasMergeTag","pulseThumbnail","startPulseThumbnail","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","Toolbar_vue_vue_type_script_setup_true_lang_default","CountdownToolbar","fontsManager","blockType","isCustom","customBlockDefinition","blockTypeLabel","fontFamilies","handleUpdate","updates","Toolbar_exports","Toolbar_default"],"sources":["../../../src/components/SlidingPillSelect.vue","../../../src/components/SlidingPillSelect.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/LinkToggle.vue","../../../src/components/toolbar/LinkToggle.vue","../../../src/components/toolbar/BorderControl.vue","../../../src/components/toolbar/BorderControl.vue","../../../src/components/toolbar/RadiusControl.vue","../../../src/components/toolbar/RadiusControl.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/SpacingControl.vue","../../../src/components/SpacingControl.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/index.ts","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/ParagraphToolbar.vue","../../../src/components/toolbar/ParagraphToolbar.vue","../../../src/utils/rebalanceColumnChildren.ts","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/Toolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n /**\n * `data-testid` for the `<input>`. A plain attribute on this component falls\n * through to the wrapping `<div>`, which is not the element a test reads a\n * value from.\n */\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nfunction onInput(event: Event): void {\n const el = event.target as HTMLInputElement;\n // A number input reports in-progress unparseable content as an EMPTY string\n // with `badInput` set, so `Number(el.value)` is 0 and indistinguishable from\n // a cleared field. Emitting here overwrites the real value with 0 before the\n // number being typed ever arrives. Typing \"-\" is the everyday case: measured\n // in Chrome, it fires `input` with value \"\" + badInput true, and only the\n // following \"5\" fires again with \"-5\". `min` does not help — it drives\n // constraint validation, not the reported value.\n if (el.validity?.badInput) return;\n emit(\"update:modelValue\", Number(el.value));\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :data-testid=\"testid\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"onInput\"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n /**\n * `data-testid` for the `<input>`. A plain attribute on this component falls\n * through to the wrapping `<div>`, which is not the element a test reads a\n * value from.\n */\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nfunction onInput(event: Event): void {\n const el = event.target as HTMLInputElement;\n // A number input reports in-progress unparseable content as an EMPTY string\n // with `badInput` set, so `Number(el.value)` is 0 and indistinguishable from\n // a cleared field. Emitting here overwrites the real value with 0 before the\n // number being typed ever arrives. Typing \"-\" is the everyday case: measured\n // in Chrome, it fires `input` with value \"\" + badInput true, and only the\n // following \"5\" fires again with \"-5\". `min` does not help — it drives\n // constraint validation, not the reported value.\n if (el.validity?.badInput) return;\n emit(\"update:modelValue\", Number(el.value));\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :data-testid=\"testid\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"onInput\"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Link, Unlink } from \"@lucide/vue\";\n\ndefineProps<{\n linked: boolean;\n /** Label while unlinked — what clicking will do. */\n linkLabel: string;\n /** Label while linked — what clicking will do. */\n unlinkLabel: string;\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-7 tpl:w-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n linked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-pressed=\"linked ? 'true' : 'false'\"\n :aria-label=\"linked ? unlinkLabel : linkLabel\"\n :title=\"linked ? unlinkLabel : linkLabel\"\n :data-testid=\"testid\"\n @click=\"emit('toggle')\"\n >\n <Link v-if=\"linked\" :size=\"14\" :stroke-width=\"2\" />\n <Unlink v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { Link, Unlink } from \"@lucide/vue\";\n\ndefineProps<{\n linked: boolean;\n /** Label while unlinked — what clicking will do. */\n linkLabel: string;\n /** Label while linked — what clicking will do. */\n unlinkLabel: string;\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-7 tpl:w-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n linked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-pressed=\"linked ? 'true' : 'false'\"\n :aria-label=\"linked ? unlinkLabel : linkLabel\"\n :title=\"linked ? unlinkLabel : linkLabel\"\n :data-testid=\"testid\"\n @click=\"emit('toggle')\"\n >\n <Link v-if=\"linked\" :size=\"14\" :stroke-width=\"2\" />\n <Unlink v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass } from \"../../constants/styleConstants\";\nimport type {\n BorderSide,\n BorderSideValue,\n BorderStyle,\n BorderValue,\n} from \"@templatical/types\";\nimport { BORDER_SIDES, uniformBorder } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderValue | undefined;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderValue | undefined): void;\n}>();\n\nconst { t } = useI18n();\n\n// What a side starts as the first time a width is entered on it.\nconst DEFAULT_SIDE: BorderSideValue = {\n width: 0,\n style: \"solid\",\n color: \"#000000\",\n};\n\nfunction sameSide(a: BorderSideValue, b: BorderSideValue): boolean {\n return a.width === b.width && a.style === b.style && a.color === b.color;\n}\n\nconst isUniform = computed(() => {\n const border = props.modelValue;\n if (!border) return true;\n return BORDER_SIDES.every((side) => sameSide(border[side], border.top));\n});\n\n// Mirrors SpacingControl: linked while every side matches, and a value whose\n// sides differ unlinks it so each side stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\nconst activeSide = ref<BorderSide>(\"top\");\n\nconst current = computed<BorderSideValue>(() => {\n const side = linked.value ? \"top\" : activeSide.value;\n return props.modelValue?.[side] ?? DEFAULT_SIDE;\n});\n\nconst isDrawn = (side: BorderSideValue | undefined): boolean =>\n !!side && side.width > 0;\n\nfunction setSide(value: BorderSideValue): void {\n if (linked.value) {\n // 0 removes the border entirely rather than storing four `0px` sides.\n emit(\n \"update:modelValue\",\n isDrawn(value) ? uniformBorder(value) : undefined,\n );\n return;\n }\n const next: BorderValue = {\n ...(props.modelValue ?? uniformBorder(DEFAULT_SIDE)),\n [activeSide.value]: value,\n };\n const anyDrawn = BORDER_SIDES.some((side) => isDrawn(next[side]));\n emit(\"update:modelValue\", anyDrawn ? next : undefined);\n}\n\nfunction updateWidth(width: number): void {\n setSide({ ...current.value, width: width > 0 ? width : 0 });\n}\n\nfunction updateField(patch: Partial<BorderSideValue>): void {\n setSide({ ...current.value, ...patch });\n}\n\nfunction toggleLinked(): void {\n if (linked.value) {\n linked.value = false;\n return;\n }\n linked.value = true;\n const border = props.modelValue;\n if (!border) return;\n // Linking copies one side to all four: the side being edited if it is\n // drawn, otherwise the first drawn one.\n const source = isDrawn(border[activeSide.value])\n ? border[activeSide.value]\n : BORDER_SIDES.map((side) => border[side]).find(isDrawn);\n emit(\"update:modelValue\", source ? uniformBorder(source) : undefined);\n}\n\nconst sideOptions = computed(() =>\n BORDER_SIDES.map((side) => ({ value: side, label: t.spacingControl[side] })),\n);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.border }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkSides\"\n :unlink-label=\"t.blockSettings.unlinkSides\"\n :testid=\"`${testidPrefix}-border-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <div v-if=\"!linked\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderSide }}</label>\n <SlidingPillSelect\n :options=\"sideOptions\"\n :model-value=\"activeSide\"\n @update:model-value=\"activeSide = $event as BorderSide\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderWidth }}</label>\n <NumberWithSuffix\n :model-value=\"current.width\"\n :min=\"0\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-width-input`\"\n @update:model-value=\"updateWidth\"\n />\n </div>\n <template v-if=\"current.width > 0\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderStyle }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"current.style\"\n @update:model-value=\"updateField({ style: $event as BorderStyle })\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.borderColor }}</label>\n <ColorPicker\n :model-value=\"current.color\"\n @update:model-value=\"updateField({ color: $event })\"\n />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass } from \"../../constants/styleConstants\";\nimport type {\n BorderSide,\n BorderSideValue,\n BorderStyle,\n BorderValue,\n} from \"@templatical/types\";\nimport { BORDER_SIDES, uniformBorder } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderValue | undefined;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderValue | undefined): void;\n}>();\n\nconst { t } = useI18n();\n\n// What a side starts as the first time a width is entered on it.\nconst DEFAULT_SIDE: BorderSideValue = {\n width: 0,\n style: \"solid\",\n color: \"#000000\",\n};\n\nfunction sameSide(a: BorderSideValue, b: BorderSideValue): boolean {\n return a.width === b.width && a.style === b.style && a.color === b.color;\n}\n\nconst isUniform = computed(() => {\n const border = props.modelValue;\n if (!border) return true;\n return BORDER_SIDES.every((side) => sameSide(border[side], border.top));\n});\n\n// Mirrors SpacingControl: linked while every side matches, and a value whose\n// sides differ unlinks it so each side stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\nconst activeSide = ref<BorderSide>(\"top\");\n\nconst current = computed<BorderSideValue>(() => {\n const side = linked.value ? \"top\" : activeSide.value;\n return props.modelValue?.[side] ?? DEFAULT_SIDE;\n});\n\nconst isDrawn = (side: BorderSideValue | undefined): boolean =>\n !!side && side.width > 0;\n\nfunction setSide(value: BorderSideValue): void {\n if (linked.value) {\n // 0 removes the border entirely rather than storing four `0px` sides.\n emit(\n \"update:modelValue\",\n isDrawn(value) ? uniformBorder(value) : undefined,\n );\n return;\n }\n const next: BorderValue = {\n ...(props.modelValue ?? uniformBorder(DEFAULT_SIDE)),\n [activeSide.value]: value,\n };\n const anyDrawn = BORDER_SIDES.some((side) => isDrawn(next[side]));\n emit(\"update:modelValue\", anyDrawn ? next : undefined);\n}\n\nfunction updateWidth(width: number): void {\n setSide({ ...current.value, width: width > 0 ? width : 0 });\n}\n\nfunction updateField(patch: Partial<BorderSideValue>): void {\n setSide({ ...current.value, ...patch });\n}\n\nfunction toggleLinked(): void {\n if (linked.value) {\n linked.value = false;\n return;\n }\n linked.value = true;\n const border = props.modelValue;\n if (!border) return;\n // Linking copies one side to all four: the side being edited if it is\n // drawn, otherwise the first drawn one.\n const source = isDrawn(border[activeSide.value])\n ? border[activeSide.value]\n : BORDER_SIDES.map((side) => border[side]).find(isDrawn);\n emit(\"update:modelValue\", source ? uniformBorder(source) : undefined);\n}\n\nconst sideOptions = computed(() =>\n BORDER_SIDES.map((side) => ({ value: side, label: t.spacingControl[side] })),\n);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.border }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkSides\"\n :unlink-label=\"t.blockSettings.unlinkSides\"\n :testid=\"`${testidPrefix}-border-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <div v-if=\"!linked\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderSide }}</label>\n <SlidingPillSelect\n :options=\"sideOptions\"\n :model-value=\"activeSide\"\n @update:model-value=\"activeSide = $event as BorderSide\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderWidth }}</label>\n <NumberWithSuffix\n :model-value=\"current.width\"\n :min=\"0\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-width-input`\"\n @update:model-value=\"updateWidth\"\n />\n </div>\n <template v-if=\"current.width > 0\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderStyle }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"current.style\"\n @update:model-value=\"updateField({ style: $event as BorderStyle })\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.borderColor }}</label>\n <ColorPicker\n :model-value=\"current.color\"\n @update:model-value=\"updateField({ color: $event })\"\n />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport type {\n BorderCorner,\n BorderRadiusValue,\n CornerRadius,\n} from \"@templatical/types\";\nimport { BORDER_CORNERS } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderRadiusValue | undefined;\n label: string;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n max?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderRadiusValue): void;\n}>();\n\nconst { t } = useI18n();\n\nconst CORNER_LABEL_KEYS = {\n topLeft: \"cornerTopLeft\",\n topRight: \"cornerTopRight\",\n bottomRight: \"cornerBottomRight\",\n bottomLeft: \"cornerBottomLeft\",\n} as const satisfies Record<BorderCorner, string>;\n\n// Laid out as the box reads: top row, then bottom row.\nconst GRID_ORDER: readonly BorderCorner[] = [\n \"topLeft\",\n \"topRight\",\n \"bottomLeft\",\n \"bottomRight\",\n];\n\nconst corners = computed<CornerRadius>(() => {\n const radius = props.modelValue ?? 0;\n if (typeof radius === \"number\") {\n return {\n topLeft: radius,\n topRight: radius,\n bottomRight: radius,\n bottomLeft: radius,\n };\n }\n return radius;\n});\n\nconst isUniform = computed(() =>\n BORDER_CORNERS.every(\n (corner) => corners.value[corner] === corners.value.topLeft,\n ),\n);\n\n// Mirrors SpacingControl: linked while every corner matches, and a value\n// whose corners differ unlinks it so each corner stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\n// A negative (or unparseable) entry is ignored rather than clamped, so the\n// stored radius survives until the user types a real one.\nfunction isValid(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\nfunction updateAll(value: number): void {\n if (!isValid(value)) return;\n // Linked corners store a plain number — the shape every existing template\n // already has.\n emit(\"update:modelValue\", value);\n}\n\nfunction updateCorner(corner: BorderCorner, value: number): void {\n if (!isValid(value)) return;\n emit(\"update:modelValue\", { ...corners.value, [corner]: value });\n}\n\nfunction toggleLinked(): void {\n linked.value = !linked.value;\n if (linked.value) {\n emit(\"update:modelValue\", corners.value.topLeft);\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ label }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkCorners\"\n :unlink-label=\"t.blockSettings.unlinkCorners\"\n :testid=\"`${testidPrefix}-border-radius-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <NumberWithSuffix\n v-if=\"linked\"\n :model-value=\"corners.topLeft\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-input`\"\n @update:model-value=\"updateAll\"\n />\n <div v-else class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <div v-for=\"corner in GRID_ORDER\" :key=\"corner\">\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[10px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings[CORNER_LABEL_KEYS[corner]] }}</label\n >\n <NumberWithSuffix\n :model-value=\"corners[corner]\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-${corner}-input`\"\n @update:model-value=\"updateCorner(corner, $event)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport type {\n BorderCorner,\n BorderRadiusValue,\n CornerRadius,\n} from \"@templatical/types\";\nimport { BORDER_CORNERS } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderRadiusValue | undefined;\n label: string;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n max?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderRadiusValue): void;\n}>();\n\nconst { t } = useI18n();\n\nconst CORNER_LABEL_KEYS = {\n topLeft: \"cornerTopLeft\",\n topRight: \"cornerTopRight\",\n bottomRight: \"cornerBottomRight\",\n bottomLeft: \"cornerBottomLeft\",\n} as const satisfies Record<BorderCorner, string>;\n\n// Laid out as the box reads: top row, then bottom row.\nconst GRID_ORDER: readonly BorderCorner[] = [\n \"topLeft\",\n \"topRight\",\n \"bottomLeft\",\n \"bottomRight\",\n];\n\nconst corners = computed<CornerRadius>(() => {\n const radius = props.modelValue ?? 0;\n if (typeof radius === \"number\") {\n return {\n topLeft: radius,\n topRight: radius,\n bottomRight: radius,\n bottomLeft: radius,\n };\n }\n return radius;\n});\n\nconst isUniform = computed(() =>\n BORDER_CORNERS.every(\n (corner) => corners.value[corner] === corners.value.topLeft,\n ),\n);\n\n// Mirrors SpacingControl: linked while every corner matches, and a value\n// whose corners differ unlinks it so each corner stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\n// A negative (or unparseable) entry is ignored rather than clamped, so the\n// stored radius survives until the user types a real one.\nfunction isValid(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\nfunction updateAll(value: number): void {\n if (!isValid(value)) return;\n // Linked corners store a plain number — the shape every existing template\n // already has.\n emit(\"update:modelValue\", value);\n}\n\nfunction updateCorner(corner: BorderCorner, value: number): void {\n if (!isValid(value)) return;\n emit(\"update:modelValue\", { ...corners.value, [corner]: value });\n}\n\nfunction toggleLinked(): void {\n linked.value = !linked.value;\n if (linked.value) {\n emit(\"update:modelValue\", corners.value.topLeft);\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ label }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkCorners\"\n :unlink-label=\"t.blockSettings.unlinkCorners\"\n :testid=\"`${testidPrefix}-border-radius-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <NumberWithSuffix\n v-if=\"linked\"\n :model-value=\"corners.topLeft\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-input`\"\n @update:model-value=\"updateAll\"\n />\n <div v-else class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <div v-for=\"corner in GRID_ORDER\" :key=\"corner\">\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[10px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings[CORNER_LABEL_KEYS[corner]] }}</label\n >\n <NumberWithSuffix\n :model-value=\"corners[corner]\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-${corner}-input`\"\n @update:model-value=\"updateCorner(corner, $event)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst DEFAULT_CUSTOM_WIDTH = 200;\n\nconst widthMode = computed<\"auto\" | \"full\" | \"custom\">(() => {\n if (props.block.width === \"full\") return \"full\";\n if (typeof props.block.width === \"number\") return \"custom\";\n return \"auto\";\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nfunction updateWidthMode(mode: string): void {\n if (mode === \"full\") {\n updateField(\"width\", \"full\");\n return;\n }\n if (mode === \"custom\") {\n updateField(\n \"width\",\n typeof props.block.width === \"number\"\n ? props.block.width\n : DEFAULT_CUSTOM_WIDTH,\n );\n return;\n }\n updateField(\"width\", undefined);\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render a 0px\n // button (#260). Ignore invalid values and keep the last valid width;\n // the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.button.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n allow-transparent\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.button.borderRadius\"\n :max=\"50\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('borderRadius', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.button.widthAuto }}</option>\n <option value=\"full\">{{ t.button.fullWidth }}</option>\n <option value=\"custom\">{{ t.button.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align ?? 'center'\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst DEFAULT_CUSTOM_WIDTH = 200;\n\nconst widthMode = computed<\"auto\" | \"full\" | \"custom\">(() => {\n if (props.block.width === \"full\") return \"full\";\n if (typeof props.block.width === \"number\") return \"custom\";\n return \"auto\";\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nfunction updateWidthMode(mode: string): void {\n if (mode === \"full\") {\n updateField(\"width\", \"full\");\n return;\n }\n if (mode === \"custom\") {\n updateField(\n \"width\",\n typeof props.block.width === \"number\"\n ? props.block.width\n : DEFAULT_CUSTOM_WIDTH,\n );\n return;\n }\n updateField(\"width\", undefined);\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render a 0px\n // button (#260). Ignore invalid values and keep the last valid width;\n // the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.button.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n allow-transparent\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.button.borderRadius\"\n :max=\"50\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('borderRadius', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.button.widthAuto }}</option>\n <option value=\"full\">{{ t.button.fullWidth }}</option>\n <option value=\"custom\">{{ t.button.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align ?? 'center'\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all 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)] tpl:active:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all 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)] tpl:active:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n /**\n * Optional test hook on the header button. The title is the only other way to\n * address a section, and it is localized — which the e2e selector rules ban.\n */\n headerTestId?: string;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n :data-testid=\"headerTestId\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n /**\n * Optional test hook on the header button. The title is the only other way to\n * address a section, and it is localized — which the e2e selector rules ban.\n */\n headerTestId?: string;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n :data-testid=\"headerTestId\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n primaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.styles.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <ToggleSwitch\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"isVisible(item.key)\"\n :label=\"t.blockSettings[item.labelKey]\"\n @update:model-value=\"toggleVisibility(item.key)\"\n >\n <span class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </span>\n </ToggleSwitch>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n header-test-id=\"display-condition-section\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n data-testid=\"display-condition-select\"\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre>\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre>\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n primaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.styles.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <ToggleSwitch\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"isVisible(item.key)\"\n :label=\"t.blockSettings[item.labelKey]\"\n @update:model-value=\"toggleVisibility(item.key)\"\n >\n <span class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </span>\n </ToggleSwitch>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n header-test-id=\"display-condition-section\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n data-testid=\"display-condition-select\"\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre>\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre>\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\nimport ToggleSwitch from \"../../ToggleSwitch.vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"modelValue\"\n :label=\"field.label\"\n :required=\"field.required\"\n :disabled=\"readOnly\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n </ToggleSwitch>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\nimport ToggleSwitch from \"../../ToggleSwitch.vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"modelValue\"\n :label=\"field.label\"\n :required=\"field.required\"\n :disabled=\"readOnly\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n </ToggleSwitch>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <!-- Same treatment as the asterisk below, and the same string the seven\n field components put on the input itself: `readOnly` here is only ever\n `field.readOnly && block.dataSourceFetched`, so \"loaded from your data\n source\" is the reason, not a generic lock. -->\n <span v-if=\"readOnly\" :title=\"t.customBlocks.dataSource.readOnlyTooltip\">\n <Lock\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n aria-hidden=\"true\"\n />\n <span class=\"tpl-sr-only\">{{\n t.customBlocks.dataSource.readOnlyTooltip\n }}</span>\n </span>\n <!-- The asterisk is a sighted convention, not a label: on its own it\n announces as \"asterisk\" or as nothing at all, so the requirement is\n invisible to a screen reader. The glyph is therefore hidden from the\n accessibility tree and the text carried alongside it, which is also\n what makes the state translatable rather than punctuation. -->\n <span\n v-if=\"required\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.required\"\n >\n <span aria-hidden=\"true\">*</span>\n <span class=\"tpl-sr-only\">{{ t.customBlocks.fields.required }}</span>\n </span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <!-- Same treatment as the asterisk below, and the same string the seven\n field components put on the input itself: `readOnly` here is only ever\n `field.readOnly && block.dataSourceFetched`, so \"loaded from your data\n source\" is the reason, not a generic lock. -->\n <span v-if=\"readOnly\" :title=\"t.customBlocks.dataSource.readOnlyTooltip\">\n <Lock\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n aria-hidden=\"true\"\n />\n <span class=\"tpl-sr-only\">{{\n t.customBlocks.dataSource.readOnlyTooltip\n }}</span>\n </span>\n <!-- The asterisk is a sighted convention, not a label: on its own it\n announces as \"asterisk\" or as nothing at all, so the requirement is\n invisible to a screen reader. The glyph is therefore hidden from the\n accessibility tree and the text carried alongside it, which is also\n what makes the state translatable rather than punctuation. -->\n <span\n v-if=\"required\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.required\"\n >\n <span aria-hidden=\"true\">*</span>\n <span class=\"tpl-sr-only\">{{ t.customBlocks.fields.required }}</span>\n </span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\nimport { COLORS_KEY } from \"../../../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../../../utils/resolveColorsConfig\";\nimport { resolveFieldColors } from \"../../../utils/resolveFieldColors\";\n\nconst props = defineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\n// Narrow the injected editor-wide palette by this field's own `presets` /\n// `allowCustom`. Presentational only: the field config is audited once per\n// block definition at registration time (`useEditorCore` →\n// `collectColorFieldIssues`), never here — this component mounts again on every\n// block selection and once per repeater item, so warning from here would repeat\n// the same message indefinitely.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\nconst colors = computed(() => resolveFieldColors(props.field, editorColors));\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :disabled=\"readOnly\"\n :presets=\"colors.presets\"\n :allow-custom=\"colors.allowCustom\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\nimport { COLORS_KEY } from \"../../../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../../../utils/resolveColorsConfig\";\nimport { resolveFieldColors } from \"../../../utils/resolveFieldColors\";\n\nconst props = defineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\n// Narrow the injected editor-wide palette by this field's own `presets` /\n// `allowCustom`. Presentational only: the field config is audited once per\n// block definition at registration time (`useEditorCore` →\n// `collectColorFieldIssues`), never here — this component mounts again on every\n// block selection and once per repeater item, so warning from here would repeat\n// the same message indefinitely.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\nconst colors = computed(() => resolveFieldColors(props.field, editorColors));\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :disabled=\"readOnly\"\n :presets=\"colors.presets\"\n :allow-custom=\"colors.allowCustom\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { CAN_DROP_MEDIA_KEY, ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport { useAliveFlag } from \"../../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../../composables/useImageDrop\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\nconst props = defineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !props.readOnly,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) emit(\"update:modelValue\", result.url);\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { CAN_DROP_MEDIA_KEY, ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport { useAliveFlag } from \"../../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../../composables/useImageDrop\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\nconst props = defineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !props.readOnly,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) emit(\"update:modelValue\", result.url);\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t, format } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n\n <!-- The counterpart to the message above: each explains one control that\n is absent rather than disabled, and a missing control with no\n explanation reads as a bug. Both can be true at once for a\n fixed-length list (`minItems === maxItems`), where the two together\n are what say the length is fixed. -->\n <p\n v-if=\"!canRemove && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n format(t.customBlocks.fields.minItemsRequired, {\n count: field.minItems ?? 0,\n })\n }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t, format } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n\n <!-- The counterpart to the message above: each explains one control that\n is absent rather than disabled, and a missing control with no\n explanation reads as a bug. Both can be true at once for a\n fixed-length list (`minItems === maxItems`), where the two together\n are what say the length is fixed. -->\n <p\n v-if=\"!canRemove && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n format(t.customBlocks.fields.minItemsRequired, {\n count: field.minItems ?? 0,\n })\n }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","import type { CustomBlockFieldType } from \"@templatical/types\";\nimport type { Component } from \"vue\";\nimport BooleanField from \"./BooleanField.vue\";\nimport ColorField from \"./ColorField.vue\";\nimport ImageField from \"./ImageField.vue\";\nimport NumberField from \"./NumberField.vue\";\nimport RepeatableField from \"./RepeatableField.vue\";\nimport SelectField from \"./SelectField.vue\";\nimport TextField from \"./TextField.vue\";\nimport TextareaField from \"./TextareaField.vue\";\n\nexport const fieldComponentMap: Record<CustomBlockFieldType, Component> = {\n text: TextField,\n textarea: TextareaField,\n image: ImageField,\n color: ColorField,\n number: NumberField,\n select: SelectField,\n boolean: BooleanField,\n repeatable: RepeatableField,\n};\n\nexport function resolveFieldComponent(type: CustomBlockFieldType): Component {\n return fieldComponentMap[type] ?? TextField;\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-on-primary)] tpl:transition-all tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-on-primary)] tpl:transition-all tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { BorderRadiusValue, ImageBlock } from \"@templatical/types\";\nimport {\n containsMergeTag,\n SYNTAX_PRESETS,\n toBorderRadiusCss,\n} from \"@templatical/types\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport {\n CAN_DROP_MEDIA_KEY,\n ON_REQUEST_MEDIA_KEY,\n MERGE_TAG_SYNTAX_KEY,\n} from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../composables/useImageDrop\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nconst WIDTH_PRESETS = [300, 400, 500];\nconst DEFAULT_CUSTOM_WIDTH = 350;\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst widthMode = computed(() => {\n const w = props.block.width;\n if (w === \"full\") return \"full\";\n if (WIDTH_PRESETS.includes(w)) return String(w);\n return \"custom\";\n});\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nfunction updateWidthMode(value: string): void {\n if (value === \"custom\") {\n const w = props.block.width;\n if (typeof w !== \"number\" || WIDTH_PRESETS.includes(w)) {\n updateField(\"width\", DEFAULT_CUSTOM_WIDTH);\n }\n return;\n }\n updateField(\"width\", value === \"full\" ? \"full\" : Number(value));\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // Same guard as the custom width below: an empty number field yields\n // Number(\"\") === 0, which would collapse the image to nothing. Absent means\n // \"keep the aspect ratio\", so an invalid value must not overwrite the last\n // valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nfunction updateBorderRadius(value: BorderRadiusValue): void {\n // Here 0 is a real answer (square), so an emptied field clears the radius\n // rather than keeping the old one. `RadiusControl` ignores negatives, and\n // `NumberWithSuffix` withholds the in-progress \"-\".\n //\n // Absent, not 0: both render as square corners, but a stored 0 travels in\n // every exported template as though the author had chosen it. Matches\n // `createImageBlock`, and `updateHeightMode` above clears the same way.\n // All-square corners clear the same way.\n updateField(\n \"borderRadius\",\n toBorderRadiusCss(value) === null ? undefined : value,\n );\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render an\n // invisible image (#259). Ignore invalid values and keep the last valid\n // width; the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\n// A merge-tag src is a deliberate dynamic value — never clobber it via drop.\nconst hasMergeTagSrc = computed(() =>\n containsMergeTag(props.block.src, mergeTagSyntax),\n);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !hasMergeTagSrc.value,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n :title=\"t.image.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <ToggleSwitch\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.decorative === true\"\n :label=\"t.image.decorative\"\n @update:model-value=\"updateField('decorative', $event)\"\n >\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </ToggleSwitch>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n <option value=\"custom\">{{ t.image.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"image-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.image.heightAuto }}</option>\n <option value=\"custom\">{{ t.image.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"image-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <!-- No `max`: a circle needs a radius of at least half the rendered size,\n which the block cannot know at edit time. -->\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.image.borderRadius\"\n testid-prefix=\"image\"\n @update:model-value=\"updateBorderRadius\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"image\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.linkOpenInNewTab ?? false\"\n :label=\"t.image.openInNewTab\"\n @update:model-value=\"updateField('linkOpenInNewTab', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { BorderRadiusValue, ImageBlock } from \"@templatical/types\";\nimport {\n containsMergeTag,\n SYNTAX_PRESETS,\n toBorderRadiusCss,\n} from \"@templatical/types\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport {\n CAN_DROP_MEDIA_KEY,\n ON_REQUEST_MEDIA_KEY,\n MERGE_TAG_SYNTAX_KEY,\n} from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../composables/useImageDrop\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nconst WIDTH_PRESETS = [300, 400, 500];\nconst DEFAULT_CUSTOM_WIDTH = 350;\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst widthMode = computed(() => {\n const w = props.block.width;\n if (w === \"full\") return \"full\";\n if (WIDTH_PRESETS.includes(w)) return String(w);\n return \"custom\";\n});\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nfunction updateWidthMode(value: string): void {\n if (value === \"custom\") {\n const w = props.block.width;\n if (typeof w !== \"number\" || WIDTH_PRESETS.includes(w)) {\n updateField(\"width\", DEFAULT_CUSTOM_WIDTH);\n }\n return;\n }\n updateField(\"width\", value === \"full\" ? \"full\" : Number(value));\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // Same guard as the custom width below: an empty number field yields\n // Number(\"\") === 0, which would collapse the image to nothing. Absent means\n // \"keep the aspect ratio\", so an invalid value must not overwrite the last\n // valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nfunction updateBorderRadius(value: BorderRadiusValue): void {\n // Here 0 is a real answer (square), so an emptied field clears the radius\n // rather than keeping the old one. `RadiusControl` ignores negatives, and\n // `NumberWithSuffix` withholds the in-progress \"-\".\n //\n // Absent, not 0: both render as square corners, but a stored 0 travels in\n // every exported template as though the author had chosen it. Matches\n // `createImageBlock`, and `updateHeightMode` above clears the same way.\n // All-square corners clear the same way.\n updateField(\n \"borderRadius\",\n toBorderRadiusCss(value) === null ? undefined : value,\n );\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render an\n // invisible image (#259). Ignore invalid values and keep the last valid\n // width; the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\n// A merge-tag src is a deliberate dynamic value — never clobber it via drop.\nconst hasMergeTagSrc = computed(() =>\n containsMergeTag(props.block.src, mergeTagSyntax),\n);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !hasMergeTagSrc.value,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n :title=\"t.image.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <ToggleSwitch\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.decorative === true\"\n :label=\"t.image.decorative\"\n @update:model-value=\"updateField('decorative', $event)\"\n >\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </ToggleSwitch>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n <option value=\"custom\">{{ t.image.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"image-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.image.heightAuto }}</option>\n <option value=\"custom\">{{ t.image.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"image-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <!-- No `max`: a circle needs a radius of at least half the rendered size,\n which the block cannot know at edit time. -->\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.image.borderRadius\"\n testid-prefix=\"image\"\n @update:model-value=\"updateBorderRadius\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"image\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.linkOpenInNewTab ?? false\"\n :label=\"t.image.openInNewTab\"\n @update:model-value=\"updateField('linkOpenInNewTab', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-3 tpl:gap-y-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <ToggleSwitch\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n :model-value=\"item[toggle.key]\"\n :label=\"toggle.label\"\n @update:model-value=\"updateMenuItem(item.id, toggle.key, $event)\"\n />\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color || ''\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color || ''\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-3 tpl:gap-y-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <ToggleSwitch\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n :model-value=\"item[toggle.key]\"\n :label=\"toggle.label\"\n @update:model-value=\"updateMenuItem(item.id, toggle.key, $event)\"\n />\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color || ''\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color || ''\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ParagraphBlock } from \"@templatical/types\";\nimport { RICH_TEXT_SPACING } from \"@templatical/types\";\n\ndefineProps<{\n block: ParagraphBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ParagraphBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\" data-testid=\"paragraph-spacing\">\n <label :class=\"labelClass\">{{ t.paragraph.paragraphSpacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <!-- Shows the built-in gap when the block sets none, so the field always\n reads the gap actually in effect rather than an empty box. -->\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.paragraphSpacing ?? RICH_TEXT_SPACING.paragraphGap\"\n min=\"0\"\n max=\"64\"\n @input=\"\n emit('update', {\n paragraphSpacing: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ParagraphBlock } from \"@templatical/types\";\nimport { RICH_TEXT_SPACING } from \"@templatical/types\";\n\ndefineProps<{\n block: ParagraphBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ParagraphBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\" data-testid=\"paragraph-spacing\">\n <label :class=\"labelClass\">{{ t.paragraph.paragraphSpacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <!-- Shows the built-in gap when the block sets none, so the field always\n reads the gap actually in effect rather than an empty box. -->\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.paragraphSpacing ?? RICH_TEXT_SPACING.paragraphGap\"\n min=\"0\"\n max=\"64\"\n @input=\"\n emit('update', {\n paragraphSpacing: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","import type { Block, ColumnLayout } from \"@templatical/types\";\n\nfunction expectedColumnCount(layout: ColumnLayout): number {\n if (layout === \"1\") return 1;\n if (layout === \"3\") return 3;\n return 2;\n}\n\n/**\n * Resize a section's `children` array to match a new column layout, without\n * dropping any blocks. Growing pads with empty columns; shrinking merges\n * any excess columns' blocks into the last kept column so the user never\n * silently loses content.\n */\nexport function rebalanceColumnChildren(\n current: Block[][],\n layout: ColumnLayout,\n): Block[][] {\n const target = expectedColumnCount(layout);\n\n if (current.length === target) {\n return current;\n }\n\n if (current.length < target) {\n const padding = Array.from(\n { length: target - current.length },\n () => [] as Block[],\n );\n return [...current, ...padding];\n }\n\n const kept = current.slice(0, target);\n const overflow = current.slice(target).flat();\n const merged = [...kept[target - 1], ...overflow];\n return [...kept.slice(0, target - 1), merged];\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport {\n layoutWrapsSlot,\n type BorderRadiusValue,\n type ColumnLayout,\n type SectionBlock,\n type SectionWrapper,\n type SpacingValue,\n} from \"@templatical/types\";\nimport { computed, inject } from \"vue\";\nimport { LAYOUT_KEY, SECTION_WRAPPER_KEY } from \"../../keys\";\nimport { rebalanceColumnChildren } from \"../../utils/rebalanceColumnChildren\";\n\nconst props = defineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst layout = inject(LAYOUT_KEY, undefined);\nconst sectionWrapper = inject(SECTION_WRAPPER_KEY, undefined);\n\nconst wrapsSlot = layout !== undefined && layoutWrapsSlot(layout);\n\nconst showWrapperPanel = computed(\n () => sectionWrapper !== false || !!props.block.wrapper,\n);\nconst disableTurnOn = computed(() => wrapsSlot && !props.block.wrapper);\nconst showNote = computed(() => wrapsSlot && showWrapperPanel.value);\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n\nfunction handleColumnsChange(event: Event): void {\n const columns = (event.target as HTMLSelectElement).value as ColumnLayout;\n const children = rebalanceColumnChildren(props.block.children, columns);\n emit(\"update\", { columns, children });\n}\n\nfunction handleStackOnMobileChange(checked: boolean): void {\n emit(\"update\", { stackOnMobile: checked });\n}\n\nfunction handleBorderRadiusChange(borderRadius: BorderRadiusValue): void {\n emit(\"update\", { borderRadius });\n}\n\nfunction setWrapperEnabled(enabled: boolean): void {\n emit(\"update\", {\n wrapper: enabled\n ? { padding: { top: 20, right: 20, bottom: 20, left: 20 } }\n : undefined,\n });\n}\n\nfunction updateWrapper(patch: Partial<SectionWrapper>): void {\n emit(\"update\", { wrapper: { ...props.block.wrapper, ...patch } });\n}\n\nfunction handleWrapperPadding(value: SpacingValue): void {\n updateWrapper({ padding: value });\n}\n\nfunction handleWrapperRadius(borderRadius: BorderRadiusValue): void {\n updateWrapper({ borderRadius });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"handleColumnsChange\"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n <div v-if=\"block.columns !== '1'\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.stackOnMobile !== false\"\n :label=\"t.section.stackOnMobile\"\n @update:model-value=\"handleStackOnMobileChange($event)\"\n />\n </div>\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section\"\n @update:model-value=\"handleBorderRadiusChange\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"section\"\n @update:model-value=\"emit('update', { border: $event })\"\n />\n <div v-if=\"showWrapperPanel\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"!!block.wrapper\"\n :label=\"t.section.wrapperEnable\"\n :disabled=\"disableTurnOn\"\n @update:model-value=\"setWrapperEnabled($event)\"\n />\n <p\n v-if=\"showNote\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.section.wrapperLayoutConflict }}\n </p>\n <div\n v-if=\"block.wrapper\"\n class=\"tpl:mt-3 tpl:ml-0.5 tpl:space-y-3 tpl:border-l tpl:border-[var(--tpl-border)] tpl:pl-3\"\n >\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.wrapper.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateWrapper({ backgroundColor: $event })\"\n />\n </div>\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"\n block.wrapper.padding ?? { top: 0, right: 0, bottom: 0, left: 0 }\n \"\n @update:model-value=\"handleWrapperPadding\"\n />\n <RadiusControl\n :model-value=\"block.wrapper.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section-wrapper\"\n @update:model-value=\"handleWrapperRadius\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport {\n layoutWrapsSlot,\n type BorderRadiusValue,\n type ColumnLayout,\n type SectionBlock,\n type SectionWrapper,\n type SpacingValue,\n} from \"@templatical/types\";\nimport { computed, inject } from \"vue\";\nimport { LAYOUT_KEY, SECTION_WRAPPER_KEY } from \"../../keys\";\nimport { rebalanceColumnChildren } from \"../../utils/rebalanceColumnChildren\";\n\nconst props = defineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst layout = inject(LAYOUT_KEY, undefined);\nconst sectionWrapper = inject(SECTION_WRAPPER_KEY, undefined);\n\nconst wrapsSlot = layout !== undefined && layoutWrapsSlot(layout);\n\nconst showWrapperPanel = computed(\n () => sectionWrapper !== false || !!props.block.wrapper,\n);\nconst disableTurnOn = computed(() => wrapsSlot && !props.block.wrapper);\nconst showNote = computed(() => wrapsSlot && showWrapperPanel.value);\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n\nfunction handleColumnsChange(event: Event): void {\n const columns = (event.target as HTMLSelectElement).value as ColumnLayout;\n const children = rebalanceColumnChildren(props.block.children, columns);\n emit(\"update\", { columns, children });\n}\n\nfunction handleStackOnMobileChange(checked: boolean): void {\n emit(\"update\", { stackOnMobile: checked });\n}\n\nfunction handleBorderRadiusChange(borderRadius: BorderRadiusValue): void {\n emit(\"update\", { borderRadius });\n}\n\nfunction setWrapperEnabled(enabled: boolean): void {\n emit(\"update\", {\n wrapper: enabled\n ? { padding: { top: 20, right: 20, bottom: 20, left: 20 } }\n : undefined,\n });\n}\n\nfunction updateWrapper(patch: Partial<SectionWrapper>): void {\n emit(\"update\", { wrapper: { ...props.block.wrapper, ...patch } });\n}\n\nfunction handleWrapperPadding(value: SpacingValue): void {\n updateWrapper({ padding: value });\n}\n\nfunction handleWrapperRadius(borderRadius: BorderRadiusValue): void {\n updateWrapper({ borderRadius });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"handleColumnsChange\"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n <div v-if=\"block.columns !== '1'\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.stackOnMobile !== false\"\n :label=\"t.section.stackOnMobile\"\n @update:model-value=\"handleStackOnMobileChange($event)\"\n />\n </div>\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section\"\n @update:model-value=\"handleBorderRadiusChange\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"section\"\n @update:model-value=\"emit('update', { border: $event })\"\n />\n <div v-if=\"showWrapperPanel\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"!!block.wrapper\"\n :label=\"t.section.wrapperEnable\"\n :disabled=\"disableTurnOn\"\n @update:model-value=\"setWrapperEnabled($event)\"\n />\n <p\n v-if=\"showNote\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.section.wrapperLayoutConflict }}\n </p>\n <div\n v-if=\"block.wrapper\"\n class=\"tpl:mt-3 tpl:ml-0.5 tpl:space-y-3 tpl:border-l tpl:border-[var(--tpl-border)] tpl:pl-3\"\n >\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.wrapper.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateWrapper({ backgroundColor: $event })\"\n />\n </div>\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"\n block.wrapper.padding ?? { top: 0, right: 0, bottom: 0, left: 0 }\n \"\n @update:model-value=\"handleWrapperPadding\"\n />\n <RadiusControl\n :model-value=\"block.wrapper.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section-wrapper\"\n @update:model-value=\"handleWrapperRadius\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport { socialPlatformOptions } from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialIconStyle,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n data-testid=\"social-platform-select\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ t.social.platforms[platform] }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.iconStyle\"\n @change=\"\n updateField(\n 'iconStyle',\n ($event.target as HTMLSelectElement).value as SocialIconStyle,\n )\n \"\n >\n <option value=\"solid\">{{ t.social.styleSolid }}</option>\n <option value=\"outlined\">{{ t.social.styleOutlined }}</option>\n <option value=\"rounded\">{{ t.social.styleRounded }}</option>\n <option value=\"square\">{{ t.social.styleSquare }}</option>\n <option value=\"circle\">{{ t.social.styleCircle }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport { socialPlatformOptions } from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialIconStyle,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n data-testid=\"social-platform-select\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ t.social.platforms[platform] }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.iconStyle\"\n @change=\"\n updateField(\n 'iconStyle',\n ($event.target as HTMLSelectElement).value as SocialIconStyle,\n )\n \"\n >\n <option value=\"solid\">{{ t.social.styleSolid }}</option>\n <option value=\"outlined\">{{ t.social.styleOutlined }}</option>\n <option value=\"rounded\">{{ t.social.styleRounded }}</option>\n <option value=\"square\">{{ t.social.styleSquare }}</option>\n <option value=\"circle\">{{ t.social.styleCircle }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from({ length: columnCount }, (): TableCellData => ({\n id: generateId(),\n content: \"\",\n })),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.hasHeaderRow\"\n :label=\"t.table.hasHeaderRow\"\n @update:model-value=\"updateField('hasHeaderRow', $event)\"\n />\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor ?? ''\"\n :seed-color=\"DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from({ length: columnCount }, (): TableCellData => ({\n id: generateId(),\n content: \"\",\n })),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.hasHeaderRow\"\n :label=\"t.table.hasHeaderRow\"\n @update:model-value=\"updateField('hasHeaderRow', $event)\"\n />\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor ?? ''\"\n :seed-color=\"DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // An empty number field yields Number(\"\") === 0, which would collapse the\n // thumbnail. Absent means \"keep the aspect ratio\", so an invalid value must\n // not overwrite the last valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.video.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"video-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.video.heightAuto }}</option>\n <option value=\"custom\">{{ t.video.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"video-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // An empty number field yields Number(\"\") === 0, which would collapse the\n // thumbnail. Absent means \"keep the aspect ratio\", so an invalid value must\n // not overwrite the last valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.video.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"video-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.video.heightAuto }}</option>\n <option value=\"custom\">{{ t.video.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"video-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport ParagraphToolbar from \"./toolbar/ParagraphToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n ParagraphBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n data-testid=\"block-toolbar\"\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Inline formatting lives in the TipTap toolbar; this carries only\n what applies to the block as a whole. -->\n <ParagraphToolbar\n v-else-if=\"blockType === 'paragraph'\"\n :block=\"block as ParagraphBlock\"\n @update=\"handleUpdate\"\n />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport ParagraphToolbar from \"./toolbar/ParagraphToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n ParagraphBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n data-testid=\"block-toolbar\"\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Inline formatting lives in the TipTap toolbar; this carries only\n what applies to the block as a whole. -->\n <ParagraphToolbar\n v-else-if=\"blockType === 'paragraph'\"\n :block=\"block as ParagraphBlock\"\n @update=\"handleUpdate\"\n />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"Toolbar-DeKbYejp.js","names":["_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeStyle","_createCommentVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_createBlock","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_createVNode","LinkToggle","_normalizeClass","SlidingPillSelect","_createCommentVNode","NumberWithSuffix","_Fragment","ColorPicker","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","LinkToggle","_unref","_createBlock","NumberWithSuffix","_Fragment","_renderList","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","DEFAULT_CUSTOM_WIDTH","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","MergeTagInput","_createBlock","ToggleSwitch","_createCommentVNode","ColorPicker","BorderControl","RadiusControl","SlidingPillSelect","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_normalizeClass","_createVNode","_createBlock","_sfc_main","$emit","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createVNode","_unref","_toDisplayString","_withDirectives","_renderSlot","_vShow","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_openBlock","_createElementBlock","_normalizeClass","_createVNode","CollapsibleSection","_unref","_withCtx","SpacingControl","_createElementVNode","_toDisplayString","ColorPicker","_Fragment","_renderList","ToggleSwitch","_createBlock","_resolveDynamicComponent","_createTextVNode","_createCommentVNode","_withDirectives","_vModelText","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_unref","_createVNode","ToggleSwitch","_withCtx","_createTextVNode","_toDisplayString","_createBlock","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_createTextVNode","_toDisplayString","_createVNode","_createCommentVNode","_renderSlot","_sfc_main","_openBlock","_createBlock","FieldWrapper","_withCtx","_createVNode","ColorPicker","_unref","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_unref","_createElementBlock","_Fragment","_createVNode","_createTextVNode","_toDisplayString","_createCommentVNode","_normalizeClass","inputClass","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_normalizeClass","_unref","inputClass","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_createElementBlock","_Fragment","_renderList","_toDisplayString","_unref","_createVNode","Trash2","_createCommentVNode","_resolveDynamicComponent","_normalizeClass","_createTextVNode","_sfc_main","_hoisted_1","_hoisted_2","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementVNode","_normalizeClass","_unref","inputClass","_createElementBlock","_Fragment","_renderList","_toDisplayString","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementBlock","_normalizeClass","_unref","inputClass","MergeTagInput","_sfc_main","_hoisted_1","_openBlock","_createBlock","FieldWrapper","_withCtx","_createElementBlock","_unref","_normalizeClass","MergeTagTextarea","_sfc_main","TextField","TextareaField","ImageField","ColorField","NumberField","SelectField","BooleanField","RepeatableField","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_createCommentVNode","_createVNode","_createTextVNode","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","SlidingPillSelect","ColorPicker","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","_createTextVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","DEFAULT_CUSTOM_HEIGHT","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","_createTextVNode","_createCommentVNode","MergeTagInput","inputClass","ToggleSwitch","_withCtx","RadiusControl","BorderControl","SlidingPillSelect","_createBlock","_sfc_main","_hoisted_1","_openBlock","_createElementBlock","_normalizeClass","_unref","_toDisplayString","_createCommentVNode","_renderSlot","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","AlignLeft","AlignCenter","AlignRight","_openBlock","_createElementBlock","_Fragment","_createVNode","FieldRow","_unref","_withCtx","_createElementVNode","_renderList","_normalizeClass","inputClass","MergeTagInput","_createBlock","ToggleSwitch","_toDisplayString","ColorPicker","_createTextVNode","NumberWithSuffix","SlidingPillSelect","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","ToggleSwitch","_createCommentVNode","RadiusControl","BorderControl","ColorPicker","SpacingControl","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_renderList","inputClass","_createVNode","MergeTagInput","_createTextVNode","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_openBlock","_createElementBlock","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","ToggleSwitch","ColorPicker","_createCommentVNode","inputClass","_renderList","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","inputClass","_renderList","_createVNode","ColorPicker","SlidingPillSelect","AlignLeft","AlignCenter","AlignRight","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_openBlock","_createElementBlock","_Fragment","_createElementVNode","_normalizeClass","_unref","_toDisplayString","_createVNode","MergeTagInput","_createBlock","ToggleSwitch","_createCommentVNode","_createTextVNode","inputClass","SlidingPillSelect","_sfc_main","_openBlock","_createElementBlock","_unref","_createElementVNode","_createBlock","_resolveDynamicComponent","_createCommentVNode","_toDisplayString","_createVNode","Trash2","CustomBlockToolbar","SectionToolbar","TitleToolbar","ParagraphToolbar","ImageToolbar","VideoToolbar","ButtonToolbar","DividerToolbar","SocialToolbar","MenuToolbar","TableToolbar","SpacerToolbar","HtmlToolbar","CommonBlockSettings","_sfc_main","__exportAll","watch","inject","renderList","withCtx","renderSlot","openBlock","ref","resolveDynamicComponent","defineComponent","withDirectives","reactive","unref","createVNode","normalizeClass","computed","createCommentVNode","normalizeStyle","createBlock","vModelText","createBaseVNode","vShow","Fragment","toDisplayString","defineAsyncComponent","createTextVNode","layoutWrapsSlot","generateId","isCustomBlock","BORDER_CORNERS","RICH_TEXT_SPACING","containsMergeTag","BORDER_SIDES","SYNTAX_PRESETS","toBorderRadiusCss","uniformBorder","resolveFieldColors","useDataSourceFetch","useImageDrop","socialPlatformOptions","getBlockTypeLabel","useTimeoutFn","requireInject","LAYOUT_KEY","SECTION_WRAPPER_KEY","ON_REQUEST_MEDIA_KEY","MERGE_TAG_SYNTAX_KEY","CAN_DROP_MEDIA_KEY","COLORS_KEY","DISPLAY_CONDITIONS_KEY","ALLOW_CUSTOM_CONDITIONS_KEY","CUSTOM_BLOCK_DEFINITIONS_KEY","FONTS_MANAGER_KEY","useI18n","DEFAULT_RESOLVED_COLORS","RefreshCw","Copy","ChevronDown","CircleAlert","Smartphone","Plus","TextAlignCenter","LoaderCircle","Unlink","Monitor","Trash","Info","TextAlignStart","LockOpen","Code","Link","X","Image","Lock","Upload","Minus","TextAlignEnd","_plugin_vue_export_helper_default","useAliveFlag","monoTextareaClass","removeItemBtnClass","addItemBtnClass","labelClass","inputSuffixClass","inputGroupInputClass","inputClass$1","DEFAULT_TABLE_ROW_BG","primaryBtnClass","blockTypeIcons","ColorPicker_default","MergeTagTextarea_default","ToggleSwitch_default","MergeTagInput_default","_hoisted_1$30","_hoisted_2$24","SlidingPillSelect_default","__emit","props","__props","emit","selectedIndex","o","pillOffset","createElementBlock","option","_hoisted_1$29","_hoisted_2$23","NumberWithSuffix_default","onInput","el","event","_hoisted_1$28","LinkToggle_default","_cache","_hoisted_1$27","_hoisted_2$22","_hoisted_3$20","_hoisted_4$17","_hoisted_5$16","_hoisted_6$13","BorderControl_default","DEFAULT_SIDE","sameSide","a","b","isUniform","border","side","linked","uniform","activeSide","current","isDrawn","setSide","value","next","anyDrawn","updateWidth","width","updateField","patch","toggleLinked","source","sideOptions","t","$event","_hoisted_1$26","_hoisted_2$21","_hoisted_3$19","_hoisted_4$16","_hoisted_5$15","RadiusControl_default","CORNER_LABEL_KEYS","GRID_ORDER","corners","radius","corner","isValid","updateAll","updateCorner","_hoisted_1$25","_hoisted_2$20","_hoisted_3$18","_hoisted_4$15","_hoisted_5$14","_hoisted_6$12","_hoisted_7$11","_hoisted_8$10","_hoisted_9$8","_hoisted_10$8","_hoisted_11$7","_hoisted_12$7","_hoisted_13$6","_hoisted_14$6","_hoisted_15$6","_hoisted_16$6","_hoisted_17$6","_hoisted_18$5","_hoisted_19$3","DEFAULT_CUSTOM_WIDTH$1","ButtonToolbar_default","widthMode","field","updateWidthMode","mode","updateCustomWidth","n","raw","font","_hoisted_1$24","_hoisted_2$19","_hoisted_3$17","_hoisted_4$14","_hoisted_5$13","_hoisted_6$11","_hoisted_7$10","_hoisted_8$9","_hoisted_9$7","_hoisted_10$7","_hoisted_11$6","_hoisted_12$6","_hoisted_13$5","_hoisted_14$5","_hoisted_15$5","_hoisted_16$5","_hoisted_17$5","_hoisted_18$4","_hoisted_19$2","_hoisted_20$2","_hoisted_21$2","stepperBtnClass","inputClass","SpacingControl_default","locked","updateValue","newValue","direction","delta","setDirectValue","toggleLock","_hoisted_1$23","_hoisted_2$18","CollapsibleSection_default","_ctx","_hoisted_1$22","_hoisted_2$17","_hoisted_3$16","_hoisted_4$13","_hoisted_5$12","_hoisted_6$10","_hoisted_7$9","_hoisted_8$8","_hoisted_9$6","_hoisted_10$6","_hoisted_11$5","_hoisted_12$5","_hoisted_13$4","_hoisted_14$4","_hoisted_15$4","_hoisted_16$4","_hoisted_17$4","_hoisted_18$3","CommonBlockSettings_default","displayConditions","allowCustomConditions","openSections","customConditionMode","customBefore","customAfter","VISIBILITY_ITEMS","toggleSection","key","hasDisplayConditions","isCustomCondition","c","startCustomCondition","applyCustomCondition","condition","groupedDisplayConditions","groups","group","updateStyle","isVisible","toggleVisibility","item","label","conditions","_hoisted_1$21","BooleanField_default","_hoisted_1$20","_hoisted_2$16","_hoisted_3$15","_hoisted_4$12","_hoisted_5$11","FieldWrapper_default","ColorField_default","editorColors","colors","_hoisted_1$19","_hoisted_2$15","_hoisted_3$14","ImageField_default","onRequestMedia","canDropMedia","aliveFlag","canBrowseMedia","browseMedia","result","dropZoneRef","isUploading","dropEnabled","uploadDroppedFiles","isOver","_hoisted_1$18","NumberField_default","_hoisted_1$17","_hoisted_2$14","_hoisted_3$13","_hoisted_4$11","_hoisted_5$10","_hoisted_6$9","RepeatableField_default","items","canAdd","canRemove","addItem","newItem","subField","removeItem","updated","index","updateItemField","resolveFieldComponent","format","_hoisted_1$16","_hoisted_2$13","SelectField_default","_hoisted_1$15","TextField_default","_hoisted_1$14","readOnlyTextareaClass","fieldComponentMap","type","_hoisted_1$13","_hoisted_2$12","_hoisted_3$12","_hoisted_4$10","_hoisted_5$9","_hoisted_6$8","_hoisted_7$8","_hoisted_8$7","CustomBlockToolbar_default","customBlockDefinitions","definition","d","blockRef","fetchData","fieldValues","fetched","isFieldReadOnly","hasDataSource","needsFetch","isFetching","args","fetchError","_hoisted_1$12","_hoisted_2$11","_hoisted_3$11","_hoisted_4$9","_hoisted_5$8","DividerToolbar_default","_hoisted_1$11","_hoisted_2$10","_hoisted_3$10","HtmlToolbar_default","_hoisted_1$10","_hoisted_2$9","_hoisted_3$9","_hoisted_4$8","_hoisted_5$7","_hoisted_6$7","_hoisted_7$7","_hoisted_8$6","_hoisted_9$5","_hoisted_10$5","_hoisted_11$4","_hoisted_12$4","_hoisted_13$3","_hoisted_14$3","_hoisted_15$3","_hoisted_16$3","_hoisted_17$3","_hoisted_18$2","_hoisted_19$1","_hoisted_20$1","_hoisted_21$1","DEFAULT_CUSTOM_WIDTH","DEFAULT_CUSTOM_HEIGHT$1","ImageToolbar_default","mergeTagSyntax","pulseSrc","pulseAlt","startPulseSrc","WIDTH_PRESETS","w","heightMode","updateHeightMode","updateCustomHeight","updateBorderRadius","openMediaBrowser","hasMergeTagSrc","\"color\"","_hoisted_1$9","FieldRow_default","_hoisted_1$8","_hoisted_2$8","_hoisted_3$8","_hoisted_4$7","_hoisted_5$6","_hoisted_6$6","_hoisted_7$6","_hoisted_8$5","_hoisted_9$4","_hoisted_10$4","MenuToolbar_default","ITEM_TOGGLES","ALIGN_OPTIONS","addMenuItem","updateMenuItem","updatedItems","itemId","removeMenuItem","toggle","_hoisted_1$7","_hoisted_2$7","_hoisted_3$7","ParagraphToolbar_default","expectedColumnCount","layout","rebalanceColumnChildren","target","padding","kept","overflow","merged","_hoisted_1$6","_hoisted_2$6","_hoisted_3$6","_hoisted_4$6","_hoisted_5$5","_hoisted_6$5","_hoisted_7$5","SectionToolbar_default","sectionWrapper","wrapsSlot","showWrapperPanel","disableTurnOn","showNote","columnOptions","handleColumnsChange","columns","children","handleStackOnMobileChange","checked","handleBorderRadiusChange","setWrapperEnabled","enabled","updateWrapper","handleWrapperPadding","handleWrapperRadius","_hoisted_1$5","_hoisted_2$5","_hoisted_3$5","_hoisted_4$5","_hoisted_5$4","_hoisted_6$4","_hoisted_7$4","_hoisted_8$4","_hoisted_9$3","_hoisted_10$3","_hoisted_11$3","_hoisted_12$3","_hoisted_13$2","_hoisted_14$2","_hoisted_15$2","_hoisted_16$2","_hoisted_17$2","_hoisted_18$1","SocialToolbar_default","addSocialIcon","newIcon","updateSocialIcon","updatedIcons","icon","iconId","removeSocialIcon","platform","_hoisted_1$4","_hoisted_2$4","_hoisted_3$4","_hoisted_4$4","SpacerToolbar_default","_hoisted_1$3","_hoisted_2$3","_hoisted_3$3","_hoisted_4$3","_hoisted_5$3","_hoisted_6$3","_hoisted_7$3","_hoisted_8$3","_hoisted_9$2","_hoisted_10$2","_hoisted_11$2","_hoisted_12$2","_hoisted_13$1","_hoisted_14$1","_hoisted_15$1","_hoisted_16$1","_hoisted_17$1","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_30","TableToolbar_default","tableColumnCount","addTableRow","columnCount","newRow","removeTableRow","row","rowId","addTableColumn","updatedRows","removeTableColumn","i","colIndex","_hoisted_1$2","_hoisted_2$2","_hoisted_3$2","_hoisted_4$2","_hoisted_5$2","_hoisted_6$2","_hoisted_7$2","_hoisted_8$2","_hoisted_9$1","_hoisted_10$1","_hoisted_11$1","_hoisted_12$1","TitleToolbar_default","_hoisted_1$1","_hoisted_2$1","_hoisted_3$1","_hoisted_4$1","_hoisted_5$1","_hoisted_6$1","_hoisted_7$1","_hoisted_8$1","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","DEFAULT_CUSTOM_HEIGHT","VideoToolbar_default","urlHasMergeTag","pulseThumbnail","startPulseThumbnail","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","Toolbar_vue_vue_type_script_setup_true_lang_default","CountdownToolbar","fontsManager","blockType","isCustom","customBlockDefinition","blockTypeLabel","fontFamilies","handleUpdate","updates","Toolbar_exports","Toolbar_default"],"sources":["../../../src/components/SlidingPillSelect.vue","../../../src/components/SlidingPillSelect.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/LinkToggle.vue","../../../src/components/toolbar/LinkToggle.vue","../../../src/components/toolbar/BorderControl.vue","../../../src/components/toolbar/BorderControl.vue","../../../src/components/toolbar/RadiusControl.vue","../../../src/components/toolbar/RadiusControl.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/SpacingControl.vue","../../../src/components/SpacingControl.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/index.ts","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/ParagraphToolbar.vue","../../../src/components/toolbar/ParagraphToolbar.vue","../../../src/utils/rebalanceColumnChildren.ts","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/Toolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n /**\n * `data-testid` for the `<input>`. A plain attribute on this component falls\n * through to the wrapping `<div>`, which is not the element a test reads a\n * value from.\n */\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nfunction onInput(event: Event): void {\n const el = event.target as HTMLInputElement;\n // A number input reports in-progress unparseable content as an EMPTY string\n // with `badInput` set, so `Number(el.value)` is 0 and indistinguishable from\n // a cleared field. Emitting here overwrites the real value with 0 before the\n // number being typed ever arrives. Typing \"-\" is the everyday case: measured\n // in Chrome, it fires `input` with value \"\" + badInput true, and only the\n // following \"5\" fires again with \"-5\". `min` does not help — it drives\n // constraint validation, not the reported value.\n if (el.validity?.badInput) return;\n emit(\"update:modelValue\", Number(el.value));\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :data-testid=\"testid\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"onInput\"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n /**\n * `data-testid` for the `<input>`. A plain attribute on this component falls\n * through to the wrapping `<div>`, which is not the element a test reads a\n * value from.\n */\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nfunction onInput(event: Event): void {\n const el = event.target as HTMLInputElement;\n // A number input reports in-progress unparseable content as an EMPTY string\n // with `badInput` set, so `Number(el.value)` is 0 and indistinguishable from\n // a cleared field. Emitting here overwrites the real value with 0 before the\n // number being typed ever arrives. Typing \"-\" is the everyday case: measured\n // in Chrome, it fires `input` with value \"\" + badInput true, and only the\n // following \"5\" fires again with \"-5\". `min` does not help — it drives\n // constraint validation, not the reported value.\n if (el.validity?.badInput) return;\n emit(\"update:modelValue\", Number(el.value));\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :data-testid=\"testid\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"onInput\"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Link, Unlink } from \"@lucide/vue\";\n\ndefineProps<{\n linked: boolean;\n /** Label while unlinked — what clicking will do. */\n linkLabel: string;\n /** Label while linked — what clicking will do. */\n unlinkLabel: string;\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-7 tpl:w-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n linked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-pressed=\"linked ? 'true' : 'false'\"\n :aria-label=\"linked ? unlinkLabel : linkLabel\"\n :title=\"linked ? unlinkLabel : linkLabel\"\n :data-testid=\"testid\"\n @click=\"emit('toggle')\"\n >\n <Link v-if=\"linked\" :size=\"14\" :stroke-width=\"2\" />\n <Unlink v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { Link, Unlink } from \"@lucide/vue\";\n\ndefineProps<{\n linked: boolean;\n /** Label while unlinked — what clicking will do. */\n linkLabel: string;\n /** Label while linked — what clicking will do. */\n unlinkLabel: string;\n testid?: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-7 tpl:w-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n linked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-pressed=\"linked ? 'true' : 'false'\"\n :aria-label=\"linked ? unlinkLabel : linkLabel\"\n :title=\"linked ? unlinkLabel : linkLabel\"\n :data-testid=\"testid\"\n @click=\"emit('toggle')\"\n >\n <Link v-if=\"linked\" :size=\"14\" :stroke-width=\"2\" />\n <Unlink v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass } from \"../../constants/styleConstants\";\nimport type {\n BorderSide,\n BorderSideValue,\n BorderStyle,\n BorderValue,\n} from \"@templatical/types\";\nimport { BORDER_SIDES, uniformBorder } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderValue | undefined;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderValue | undefined): void;\n}>();\n\nconst { t } = useI18n();\n\n// What a side starts as the first time a width is entered on it.\nconst DEFAULT_SIDE: BorderSideValue = {\n width: 0,\n style: \"solid\",\n color: \"#000000\",\n};\n\nfunction sameSide(a: BorderSideValue, b: BorderSideValue): boolean {\n return a.width === b.width && a.style === b.style && a.color === b.color;\n}\n\nconst isUniform = computed(() => {\n const border = props.modelValue;\n if (!border) return true;\n return BORDER_SIDES.every((side) => sameSide(border[side], border.top));\n});\n\n// Mirrors SpacingControl: linked while every side matches, and a value whose\n// sides differ unlinks it so each side stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\nconst activeSide = ref<BorderSide>(\"top\");\n\nconst current = computed<BorderSideValue>(() => {\n const side = linked.value ? \"top\" : activeSide.value;\n return props.modelValue?.[side] ?? DEFAULT_SIDE;\n});\n\nconst isDrawn = (side: BorderSideValue | undefined): boolean =>\n !!side && side.width > 0;\n\nfunction setSide(value: BorderSideValue): void {\n if (linked.value) {\n // 0 removes the border entirely rather than storing four `0px` sides.\n emit(\n \"update:modelValue\",\n isDrawn(value) ? uniformBorder(value) : undefined,\n );\n return;\n }\n const next: BorderValue = {\n ...(props.modelValue ?? uniformBorder(DEFAULT_SIDE)),\n [activeSide.value]: value,\n };\n const anyDrawn = BORDER_SIDES.some((side) => isDrawn(next[side]));\n emit(\"update:modelValue\", anyDrawn ? next : undefined);\n}\n\nfunction updateWidth(width: number): void {\n setSide({ ...current.value, width: width > 0 ? width : 0 });\n}\n\nfunction updateField(patch: Partial<BorderSideValue>): void {\n setSide({ ...current.value, ...patch });\n}\n\nfunction toggleLinked(): void {\n if (linked.value) {\n linked.value = false;\n return;\n }\n linked.value = true;\n const border = props.modelValue;\n if (!border) return;\n // Linking copies one side to all four: the side being edited if it is\n // drawn, otherwise the first drawn one.\n const source = isDrawn(border[activeSide.value])\n ? border[activeSide.value]\n : BORDER_SIDES.map((side) => border[side]).find(isDrawn);\n emit(\"update:modelValue\", source ? uniformBorder(source) : undefined);\n}\n\nconst sideOptions = computed(() =>\n BORDER_SIDES.map((side) => ({ value: side, label: t.spacingControl[side] })),\n);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.border }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkSides\"\n :unlink-label=\"t.blockSettings.unlinkSides\"\n :testid=\"`${testidPrefix}-border-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <div v-if=\"!linked\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderSide }}</label>\n <SlidingPillSelect\n :options=\"sideOptions\"\n :model-value=\"activeSide\"\n @update:model-value=\"activeSide = $event as BorderSide\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderWidth }}</label>\n <NumberWithSuffix\n :model-value=\"current.width\"\n :min=\"0\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-width-input`\"\n @update:model-value=\"updateWidth\"\n />\n </div>\n <template v-if=\"current.width > 0\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderStyle }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"current.style\"\n @update:model-value=\"updateField({ style: $event as BorderStyle })\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.borderColor }}</label>\n <ColorPicker\n :model-value=\"current.color\"\n @update:model-value=\"updateField({ color: $event })\"\n />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass } from \"../../constants/styleConstants\";\nimport type {\n BorderSide,\n BorderSideValue,\n BorderStyle,\n BorderValue,\n} from \"@templatical/types\";\nimport { BORDER_SIDES, uniformBorder } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderValue | undefined;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderValue | undefined): void;\n}>();\n\nconst { t } = useI18n();\n\n// What a side starts as the first time a width is entered on it.\nconst DEFAULT_SIDE: BorderSideValue = {\n width: 0,\n style: \"solid\",\n color: \"#000000\",\n};\n\nfunction sameSide(a: BorderSideValue, b: BorderSideValue): boolean {\n return a.width === b.width && a.style === b.style && a.color === b.color;\n}\n\nconst isUniform = computed(() => {\n const border = props.modelValue;\n if (!border) return true;\n return BORDER_SIDES.every((side) => sameSide(border[side], border.top));\n});\n\n// Mirrors SpacingControl: linked while every side matches, and a value whose\n// sides differ unlinks it so each side stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\nconst activeSide = ref<BorderSide>(\"top\");\n\nconst current = computed<BorderSideValue>(() => {\n const side = linked.value ? \"top\" : activeSide.value;\n return props.modelValue?.[side] ?? DEFAULT_SIDE;\n});\n\nconst isDrawn = (side: BorderSideValue | undefined): boolean =>\n !!side && side.width > 0;\n\nfunction setSide(value: BorderSideValue): void {\n if (linked.value) {\n // 0 removes the border entirely rather than storing four `0px` sides.\n emit(\n \"update:modelValue\",\n isDrawn(value) ? uniformBorder(value) : undefined,\n );\n return;\n }\n const next: BorderValue = {\n ...(props.modelValue ?? uniformBorder(DEFAULT_SIDE)),\n [activeSide.value]: value,\n };\n const anyDrawn = BORDER_SIDES.some((side) => isDrawn(next[side]));\n emit(\"update:modelValue\", anyDrawn ? next : undefined);\n}\n\nfunction updateWidth(width: number): void {\n setSide({ ...current.value, width: width > 0 ? width : 0 });\n}\n\nfunction updateField(patch: Partial<BorderSideValue>): void {\n setSide({ ...current.value, ...patch });\n}\n\nfunction toggleLinked(): void {\n if (linked.value) {\n linked.value = false;\n return;\n }\n linked.value = true;\n const border = props.modelValue;\n if (!border) return;\n // Linking copies one side to all four: the side being edited if it is\n // drawn, otherwise the first drawn one.\n const source = isDrawn(border[activeSide.value])\n ? border[activeSide.value]\n : BORDER_SIDES.map((side) => border[side]).find(isDrawn);\n emit(\"update:modelValue\", source ? uniformBorder(source) : undefined);\n}\n\nconst sideOptions = computed(() =>\n BORDER_SIDES.map((side) => ({ value: side, label: t.spacingControl[side] })),\n);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.border }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkSides\"\n :unlink-label=\"t.blockSettings.unlinkSides\"\n :testid=\"`${testidPrefix}-border-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <div v-if=\"!linked\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderSide }}</label>\n <SlidingPillSelect\n :options=\"sideOptions\"\n :model-value=\"activeSide\"\n @update:model-value=\"activeSide = $event as BorderSide\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderWidth }}</label>\n <NumberWithSuffix\n :model-value=\"current.width\"\n :min=\"0\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-width-input`\"\n @update:model-value=\"updateWidth\"\n />\n </div>\n <template v-if=\"current.width > 0\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.blockSettings.borderStyle }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"current.style\"\n @update:model-value=\"updateField({ style: $event as BorderStyle })\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.borderColor }}</label>\n <ColorPicker\n :model-value=\"current.color\"\n @update:model-value=\"updateField({ color: $event })\"\n />\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport type {\n BorderCorner,\n BorderRadiusValue,\n CornerRadius,\n} from \"@templatical/types\";\nimport { BORDER_CORNERS } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderRadiusValue | undefined;\n label: string;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n max?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderRadiusValue): void;\n}>();\n\nconst { t } = useI18n();\n\nconst CORNER_LABEL_KEYS = {\n topLeft: \"cornerTopLeft\",\n topRight: \"cornerTopRight\",\n bottomRight: \"cornerBottomRight\",\n bottomLeft: \"cornerBottomLeft\",\n} as const satisfies Record<BorderCorner, string>;\n\n// Laid out as the box reads: top row, then bottom row.\nconst GRID_ORDER: readonly BorderCorner[] = [\n \"topLeft\",\n \"topRight\",\n \"bottomLeft\",\n \"bottomRight\",\n];\n\nconst corners = computed<CornerRadius>(() => {\n const radius = props.modelValue ?? 0;\n if (typeof radius === \"number\") {\n return {\n topLeft: radius,\n topRight: radius,\n bottomRight: radius,\n bottomLeft: radius,\n };\n }\n return radius;\n});\n\nconst isUniform = computed(() =>\n BORDER_CORNERS.every(\n (corner) => corners.value[corner] === corners.value.topLeft,\n ),\n);\n\n// Mirrors SpacingControl: linked while every corner matches, and a value\n// whose corners differ unlinks it so each corner stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\n// A negative (or unparseable) entry is ignored rather than clamped, so the\n// stored radius survives until the user types a real one.\nfunction isValid(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\nfunction updateAll(value: number): void {\n if (!isValid(value)) return;\n // Linked corners store a plain number — the shape every existing template\n // already has.\n emit(\"update:modelValue\", value);\n}\n\nfunction updateCorner(corner: BorderCorner, value: number): void {\n if (!isValid(value)) return;\n emit(\"update:modelValue\", { ...corners.value, [corner]: value });\n}\n\nfunction toggleLinked(): void {\n linked.value = !linked.value;\n if (linked.value) {\n emit(\"update:modelValue\", corners.value.topLeft);\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ label }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkCorners\"\n :unlink-label=\"t.blockSettings.unlinkCorners\"\n :testid=\"`${testidPrefix}-border-radius-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <NumberWithSuffix\n v-if=\"linked\"\n :model-value=\"corners.topLeft\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-input`\"\n @update:model-value=\"updateAll\"\n />\n <div v-else class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <div v-for=\"corner in GRID_ORDER\" :key=\"corner\">\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[10px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings[CORNER_LABEL_KEYS[corner]] }}</label\n >\n <NumberWithSuffix\n :model-value=\"corners[corner]\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-${corner}-input`\"\n @update:model-value=\"updateCorner(corner, $event)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport LinkToggle from \"./LinkToggle.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport type {\n BorderCorner,\n BorderRadiusValue,\n CornerRadius,\n} from \"@templatical/types\";\nimport { BORDER_CORNERS } from \"@templatical/types\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: BorderRadiusValue | undefined;\n label: string;\n /** Prefix for the inputs' `data-testid`s, e.g. `\"image\"`. */\n testidPrefix: string;\n max?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: BorderRadiusValue): void;\n}>();\n\nconst { t } = useI18n();\n\nconst CORNER_LABEL_KEYS = {\n topLeft: \"cornerTopLeft\",\n topRight: \"cornerTopRight\",\n bottomRight: \"cornerBottomRight\",\n bottomLeft: \"cornerBottomLeft\",\n} as const satisfies Record<BorderCorner, string>;\n\n// Laid out as the box reads: top row, then bottom row.\nconst GRID_ORDER: readonly BorderCorner[] = [\n \"topLeft\",\n \"topRight\",\n \"bottomLeft\",\n \"bottomRight\",\n];\n\nconst corners = computed<CornerRadius>(() => {\n const radius = props.modelValue ?? 0;\n if (typeof radius === \"number\") {\n return {\n topLeft: radius,\n topRight: radius,\n bottomRight: radius,\n bottomLeft: radius,\n };\n }\n return radius;\n});\n\nconst isUniform = computed(() =>\n BORDER_CORNERS.every(\n (corner) => corners.value[corner] === corners.value.topLeft,\n ),\n);\n\n// Mirrors SpacingControl: linked while every corner matches, and a value\n// whose corners differ unlinks it so each corner stays editable.\nconst linked = ref(isUniform.value);\nwatch(isUniform, (uniform) => {\n if (!uniform && linked.value) linked.value = false;\n});\n\n// A negative (or unparseable) entry is ignored rather than clamped, so the\n// stored radius survives until the user types a real one.\nfunction isValid(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\nfunction updateAll(value: number): void {\n if (!isValid(value)) return;\n // Linked corners store a plain number — the shape every existing template\n // already has.\n emit(\"update:modelValue\", value);\n}\n\nfunction updateCorner(corner: BorderCorner, value: number): void {\n if (!isValid(value)) return;\n emit(\"update:modelValue\", { ...corners.value, [corner]: value });\n}\n\nfunction toggleLinked(): void {\n linked.value = !linked.value;\n if (linked.value) {\n emit(\"update:modelValue\", corners.value.topLeft);\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ label }}</span\n >\n <LinkToggle\n :linked=\"linked\"\n :link-label=\"t.blockSettings.linkCorners\"\n :unlink-label=\"t.blockSettings.unlinkCorners\"\n :testid=\"`${testidPrefix}-border-radius-link`\"\n @toggle=\"toggleLinked\"\n />\n </div>\n <NumberWithSuffix\n v-if=\"linked\"\n :model-value=\"corners.topLeft\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-input`\"\n @update:model-value=\"updateAll\"\n />\n <div v-else class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <div v-for=\"corner in GRID_ORDER\" :key=\"corner\">\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[10px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings[CORNER_LABEL_KEYS[corner]] }}</label\n >\n <NumberWithSuffix\n :model-value=\"corners[corner]\"\n :min=\"0\"\n :max=\"max\"\n suffix=\"px\"\n :testid=\"`${testidPrefix}-border-radius-${corner}-input`\"\n @update:model-value=\"updateCorner(corner, $event)\"\n />\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst DEFAULT_CUSTOM_WIDTH = 200;\n\nconst widthMode = computed<\"auto\" | \"full\" | \"custom\">(() => {\n if (props.block.width === \"full\") return \"full\";\n if (typeof props.block.width === \"number\") return \"custom\";\n return \"auto\";\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nfunction updateWidthMode(mode: string): void {\n if (mode === \"full\") {\n updateField(\"width\", \"full\");\n return;\n }\n if (mode === \"custom\") {\n updateField(\n \"width\",\n typeof props.block.width === \"number\"\n ? props.block.width\n : DEFAULT_CUSTOM_WIDTH,\n );\n return;\n }\n updateField(\"width\", undefined);\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render a 0px\n // button (#260). Ignore invalid values and keep the last valid width;\n // the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.button.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n allow-transparent\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.button.borderRadius\"\n :max=\"50\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('borderRadius', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.button.widthAuto }}</option>\n <option value=\"full\">{{ t.button.fullWidth }}</option>\n <option value=\"custom\">{{ t.button.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align ?? 'center'\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst DEFAULT_CUSTOM_WIDTH = 200;\n\nconst widthMode = computed<\"auto\" | \"full\" | \"custom\">(() => {\n if (props.block.width === \"full\") return \"full\";\n if (typeof props.block.width === \"number\") return \"custom\";\n return \"auto\";\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nfunction updateWidthMode(mode: string): void {\n if (mode === \"full\") {\n updateField(\"width\", \"full\");\n return;\n }\n if (mode === \"custom\") {\n updateField(\n \"width\",\n typeof props.block.width === \"number\"\n ? props.block.width\n : DEFAULT_CUSTOM_WIDTH,\n );\n return;\n }\n updateField(\"width\", undefined);\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render a 0px\n // button (#260). Ignore invalid values and keep the last valid width;\n // the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.button.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n allow-transparent\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.button.borderRadius\"\n :max=\"50\"\n testid-prefix=\"button\"\n @update:model-value=\"updateField('borderRadius', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.button.widthAuto }}</option>\n <option value=\"full\">{{ t.button.fullWidth }}</option>\n <option value=\"custom\">{{ t.button.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align ?? 'center'\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all 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)] tpl:active:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all 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)] tpl:active:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n /**\n * Optional test hook on the header button. The title is the only other way to\n * address a section, and it is localized — which the e2e selector rules ban.\n */\n headerTestId?: string;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n :data-testid=\"headerTestId\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n /**\n * Optional test hook on the header button. The title is the only other way to\n * address a section, and it is localized — which the e2e selector rules ban.\n */\n headerTestId?: string;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n :data-testid=\"headerTestId\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n primaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.styles.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <ToggleSwitch\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"isVisible(item.key)\"\n :label=\"t.blockSettings[item.labelKey]\"\n @update:model-value=\"toggleVisibility(item.key)\"\n >\n <span class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </span>\n </ToggleSwitch>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n header-test-id=\"display-condition-section\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n data-testid=\"display-condition-select\"\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre>\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre>\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n primaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.styles.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <ToggleSwitch\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"isVisible(item.key)\"\n :label=\"t.blockSettings[item.labelKey]\"\n @update:model-value=\"toggleVisibility(item.key)\"\n >\n <span class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </span>\n </ToggleSwitch>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n header-test-id=\"display-condition-section\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n data-testid=\"display-condition-select\"\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n :class=\"primaryBtnClass\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre>\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre>\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\nimport ToggleSwitch from \"../../ToggleSwitch.vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"modelValue\"\n :label=\"field.label\"\n :required=\"field.required\"\n :disabled=\"readOnly\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n </ToggleSwitch>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\nimport ToggleSwitch from \"../../ToggleSwitch.vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"modelValue\"\n :label=\"field.label\"\n :required=\"field.required\"\n :disabled=\"readOnly\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n </ToggleSwitch>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <!-- Same treatment as the asterisk below, and the same string the seven\n field components put on the input itself: `readOnly` here is only ever\n `field.readOnly && block.dataSourceFetched`, so \"loaded from your data\n source\" is the reason, not a generic lock. -->\n <span v-if=\"readOnly\" :title=\"t.customBlocks.dataSource.readOnlyTooltip\">\n <Lock\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n aria-hidden=\"true\"\n />\n <span class=\"tpl-sr-only\">{{\n t.customBlocks.dataSource.readOnlyTooltip\n }}</span>\n </span>\n <!-- The asterisk is a sighted convention, not a label: on its own it\n announces as \"asterisk\" or as nothing at all, so the requirement is\n invisible to a screen reader. The glyph is therefore hidden from the\n accessibility tree and the text carried alongside it, which is also\n what makes the state translatable rather than punctuation. -->\n <span\n v-if=\"required\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.required\"\n >\n <span aria-hidden=\"true\">*</span>\n <span class=\"tpl-sr-only\">{{ t.customBlocks.fields.required }}</span>\n </span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <!-- Same treatment as the asterisk below, and the same string the seven\n field components put on the input itself: `readOnly` here is only ever\n `field.readOnly && block.dataSourceFetched`, so \"loaded from your data\n source\" is the reason, not a generic lock. -->\n <span v-if=\"readOnly\" :title=\"t.customBlocks.dataSource.readOnlyTooltip\">\n <Lock\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n aria-hidden=\"true\"\n />\n <span class=\"tpl-sr-only\">{{\n t.customBlocks.dataSource.readOnlyTooltip\n }}</span>\n </span>\n <!-- The asterisk is a sighted convention, not a label: on its own it\n announces as \"asterisk\" or as nothing at all, so the requirement is\n invisible to a screen reader. The glyph is therefore hidden from the\n accessibility tree and the text carried alongside it, which is also\n what makes the state translatable rather than punctuation. -->\n <span\n v-if=\"required\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.required\"\n >\n <span aria-hidden=\"true\">*</span>\n <span class=\"tpl-sr-only\">{{ t.customBlocks.fields.required }}</span>\n </span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\nimport { COLORS_KEY } from \"../../../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../../../utils/resolveColorsConfig\";\nimport { resolveFieldColors } from \"../../../utils/resolveFieldColors\";\n\nconst props = defineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\n// Narrow the injected editor-wide palette by this field's own `presets` /\n// `allowCustom`. Presentational only: the field config is audited once per\n// block definition at registration time (`useEditorCore` →\n// `collectColorFieldIssues`), never here — this component mounts again on every\n// block selection and once per repeater item, so warning from here would repeat\n// the same message indefinitely.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\nconst colors = computed(() => resolveFieldColors(props.field, editorColors));\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :disabled=\"readOnly\"\n :presets=\"colors.presets\"\n :allow-custom=\"colors.allowCustom\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\nimport { COLORS_KEY } from \"../../../keys\";\nimport { DEFAULT_RESOLVED_COLORS } from \"../../../utils/resolveColorsConfig\";\nimport { resolveFieldColors } from \"../../../utils/resolveFieldColors\";\n\nconst props = defineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\n// Narrow the injected editor-wide palette by this field's own `presets` /\n// `allowCustom`. Presentational only: the field config is audited once per\n// block definition at registration time (`useEditorCore` →\n// `collectColorFieldIssues`), never here — this component mounts again on every\n// block selection and once per repeater item, so warning from here would repeat\n// the same message indefinitely.\nconst editorColors = inject(COLORS_KEY, DEFAULT_RESOLVED_COLORS);\nconst colors = computed(() => resolveFieldColors(props.field, editorColors));\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :disabled=\"readOnly\"\n :presets=\"colors.presets\"\n :allow-custom=\"colors.allowCustom\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { CAN_DROP_MEDIA_KEY, ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport { useAliveFlag } from \"../../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../../composables/useImageDrop\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\nconst props = defineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !props.readOnly,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) emit(\"update:modelValue\", result.url);\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { CAN_DROP_MEDIA_KEY, ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport { useAliveFlag } from \"../../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../../composables/useImageDrop\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\nconst props = defineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !props.readOnly,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) emit(\"update:modelValue\", result.url);\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t, format } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n\n <!-- The counterpart to the message above: each explains one control that\n is absent rather than disabled, and a missing control with no\n explanation reads as a bug. Both can be true at once for a\n fixed-length list (`minItems === maxItems`), where the two together\n are what say the length is fixed. -->\n <p\n v-if=\"!canRemove && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n format(t.customBlocks.fields.minItemsRequired, {\n count: field.minItems ?? 0,\n })\n }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t, format } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n\n <!-- The counterpart to the message above: each explains one control that\n is absent rather than disabled, and a missing control with no\n explanation reads as a bug. Both can be true at once for a\n fixed-length list (`minItems === maxItems`), where the two together\n are what say the length is fixed. -->\n <p\n v-if=\"!canRemove && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n format(t.customBlocks.fields.minItemsRequired, {\n count: field.minItems ?? 0,\n })\n }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","import type { CustomBlockFieldType } from \"@templatical/types\";\nimport type { Component } from \"vue\";\nimport BooleanField from \"./BooleanField.vue\";\nimport ColorField from \"./ColorField.vue\";\nimport ImageField from \"./ImageField.vue\";\nimport NumberField from \"./NumberField.vue\";\nimport RepeatableField from \"./RepeatableField.vue\";\nimport SelectField from \"./SelectField.vue\";\nimport TextField from \"./TextField.vue\";\nimport TextareaField from \"./TextareaField.vue\";\n\nexport const fieldComponentMap: Record<CustomBlockFieldType, Component> = {\n text: TextField,\n textarea: TextareaField,\n image: ImageField,\n color: ColorField,\n number: NumberField,\n select: SelectField,\n boolean: BooleanField,\n repeatable: RepeatableField,\n};\n\nexport function resolveFieldComponent(type: CustomBlockFieldType): Component {\n return fieldComponentMap[type] ?? TextField;\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-on-primary)] tpl:transition-all tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-on-primary)] tpl:transition-all tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { BorderRadiusValue, ImageBlock } from \"@templatical/types\";\nimport {\n containsMergeTag,\n SYNTAX_PRESETS,\n toBorderRadiusCss,\n} from \"@templatical/types\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport {\n CAN_DROP_MEDIA_KEY,\n ON_REQUEST_MEDIA_KEY,\n MERGE_TAG_SYNTAX_KEY,\n} from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../composables/useImageDrop\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nconst WIDTH_PRESETS = [300, 400, 500];\nconst DEFAULT_CUSTOM_WIDTH = 350;\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst widthMode = computed(() => {\n const w = props.block.width;\n if (w === \"full\") return \"full\";\n if (WIDTH_PRESETS.includes(w)) return String(w);\n return \"custom\";\n});\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nfunction updateWidthMode(value: string): void {\n if (value === \"custom\") {\n const w = props.block.width;\n if (typeof w !== \"number\" || WIDTH_PRESETS.includes(w)) {\n updateField(\"width\", DEFAULT_CUSTOM_WIDTH);\n }\n return;\n }\n updateField(\"width\", value === \"full\" ? \"full\" : Number(value));\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // Same guard as the custom width below: an empty number field yields\n // Number(\"\") === 0, which would collapse the image to nothing. Absent means\n // \"keep the aspect ratio\", so an invalid value must not overwrite the last\n // valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nfunction updateBorderRadius(value: BorderRadiusValue): void {\n // Here 0 is a real answer (square), so an emptied field clears the radius\n // rather than keeping the old one. `RadiusControl` ignores negatives, and\n // `NumberWithSuffix` withholds the in-progress \"-\".\n //\n // Absent, not 0: both render as square corners, but a stored 0 travels in\n // every exported template as though the author had chosen it. Matches\n // `createImageBlock`, and `updateHeightMode` above clears the same way.\n // All-square corners clear the same way.\n updateField(\n \"borderRadius\",\n toBorderRadiusCss(value) === null ? undefined : value,\n );\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render an\n // invisible image (#259). Ignore invalid values and keep the last valid\n // width; the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\n// A merge-tag src is a deliberate dynamic value — never clobber it via drop.\nconst hasMergeTagSrc = computed(() =>\n containsMergeTag(props.block.src, mergeTagSyntax),\n);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !hasMergeTagSrc.value,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n :title=\"t.image.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <ToggleSwitch\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.decorative === true\"\n :label=\"t.image.decorative\"\n @update:model-value=\"updateField('decorative', $event)\"\n >\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </ToggleSwitch>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n <option value=\"custom\">{{ t.image.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"image-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.image.heightAuto }}</option>\n <option value=\"custom\">{{ t.image.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"image-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <!-- No `max`: a circle needs a radius of at least half the rendered size,\n which the block cannot know at edit time. -->\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.image.borderRadius\"\n testid-prefix=\"image\"\n @update:model-value=\"updateBorderRadius\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"image\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.linkOpenInNewTab ?? false\"\n :label=\"t.image.openInNewTab\"\n @update:model-value=\"updateField('linkOpenInNewTab', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { BorderRadiusValue, ImageBlock } from \"@templatical/types\";\nimport {\n containsMergeTag,\n SYNTAX_PRESETS,\n toBorderRadiusCss,\n} from \"@templatical/types\";\nimport { Image, Upload, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport {\n CAN_DROP_MEDIA_KEY,\n ON_REQUEST_MEDIA_KEY,\n MERGE_TAG_SYNTAX_KEY,\n} from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useImageDrop } from \"../../composables/useImageDrop\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst canDropMedia = inject(CAN_DROP_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nconst WIDTH_PRESETS = [300, 400, 500];\nconst DEFAULT_CUSTOM_WIDTH = 350;\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst widthMode = computed(() => {\n const w = props.block.width;\n if (w === \"full\") return \"full\";\n if (WIDTH_PRESETS.includes(w)) return String(w);\n return \"custom\";\n});\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nfunction updateWidthMode(value: string): void {\n if (value === \"custom\") {\n const w = props.block.width;\n if (typeof w !== \"number\" || WIDTH_PRESETS.includes(w)) {\n updateField(\"width\", DEFAULT_CUSTOM_WIDTH);\n }\n return;\n }\n updateField(\"width\", value === \"full\" ? \"full\" : Number(value));\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // Same guard as the custom width below: an empty number field yields\n // Number(\"\") === 0, which would collapse the image to nothing. Absent means\n // \"keep the aspect ratio\", so an invalid value must not overwrite the last\n // valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nfunction updateBorderRadius(value: BorderRadiusValue): void {\n // Here 0 is a real answer (square), so an emptied field clears the radius\n // rather than keeping the old one. `RadiusControl` ignores negatives, and\n // `NumberWithSuffix` withholds the in-progress \"-\".\n //\n // Absent, not 0: both render as square corners, but a stored 0 travels in\n // every exported template as though the author had chosen it. Matches\n // `createImageBlock`, and `updateHeightMode` above clears the same way.\n // All-square corners clear the same way.\n updateField(\n \"borderRadius\",\n toBorderRadiusCss(value) === null ? undefined : value,\n );\n}\n\nfunction updateCustomWidth(raw: string): void {\n // Guard against empty / NaN / non-positive input. An empty number field\n // yields Number(\"\") === 0, which would emit width: 0 and render an\n // invisible image (#259). Ignore invalid values and keep the last valid\n // width; the canvas still updates live on each valid keystroke.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"width\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n\n// --- Drag-and-drop upload (#229) ---\nconst dropZoneRef = ref<HTMLElement>();\nconst isUploading = ref(false);\n// A merge-tag src is a deliberate dynamic value — never clobber it via drop.\nconst hasMergeTagSrc = computed(() =>\n containsMergeTag(props.block.src, mergeTagSyntax),\n);\nconst dropEnabled = computed(\n () =>\n (canDropMedia?.value ?? !!onRequestMedia) &&\n !isUploading.value &&\n !hasMergeTagSrc.value,\n);\n\nasync function uploadDroppedFiles(files: File[]): Promise<void> {\n if (!onRequestMedia) return;\n isUploading.value = true;\n try {\n const result = await onRequestMedia({ accept: [\"images\"], files });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n } finally {\n if (aliveFlag.alive) isUploading.value = false;\n }\n}\n\nconst { isOver } = useImageDrop({\n target: dropZoneRef,\n enabled: dropEnabled,\n onFiles: uploadDroppedFiles,\n});\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <div ref=\"dropZoneRef\" class=\"tpl:relative\">\n <!-- Drag-over / uploading overlay (#229) -->\n <div\n v-if=\"dropEnabled && (isOver || isUploading)\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-10 tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-2 tpl:border-dashed tpl:text-xs tpl:font-medium tpl:border-[var(--tpl-primary)] tpl:text-[var(--tpl-primary)]\"\n style=\"\n background-color: color-mix(in srgb, var(--tpl-bg) 90%, transparent);\n \"\n >\n <template v-if=\"isUploading\">\n <LoaderCircle class=\"tpl-spinner\" :size=\"18\" :stroke-width=\"2\" />\n {{ t.image.uploading }}\n </template>\n <template v-else>\n <Upload :size=\"18\" :stroke-width=\"1.5\" />\n {{ t.image.dropToUpload }}\n </template>\n </div>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n :title=\"t.image.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <ToggleSwitch\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.decorative === true\"\n :label=\"t.image.decorative\"\n @update:model-value=\"updateField('decorative', $event)\"\n >\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </ToggleSwitch>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"widthMode\"\n @change=\"updateWidthMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n <option value=\"custom\">{{ t.image.widthCustom }}</option>\n </select>\n <div\n v-if=\"widthMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"\n typeof block.width === 'number' ? block.width : DEFAULT_CUSTOM_WIDTH\n \"\n min=\"20\"\n @input=\"updateCustomWidth(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"image-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.image.heightAuto }}</option>\n <option value=\"custom\">{{ t.image.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"image-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <!-- No `max`: a circle needs a radius of at least half the rendered size,\n which the block cannot know at edit time. -->\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.image.borderRadius\"\n testid-prefix=\"image\"\n @update:model-value=\"updateBorderRadius\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"image\"\n @update:model-value=\"updateField('border', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.linkOpenInNewTab ?? false\"\n :label=\"t.image.openInNewTab\"\n @update:model-value=\"updateField('linkOpenInNewTab', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-3 tpl:gap-y-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <ToggleSwitch\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n :model-value=\"item[toggle.key]\"\n :label=\"toggle.label\"\n @update:model-value=\"updateMenuItem(item.id, toggle.key, $event)\"\n />\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color || ''\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color || ''\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-3 tpl:gap-y-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <ToggleSwitch\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n :model-value=\"item[toggle.key]\"\n :label=\"toggle.label\"\n @update:model-value=\"updateMenuItem(item.id, toggle.key, $event)\"\n />\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color || ''\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color || ''\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ParagraphBlock } from \"@templatical/types\";\nimport { RICH_TEXT_SPACING } from \"@templatical/types\";\n\ndefineProps<{\n block: ParagraphBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ParagraphBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\" data-testid=\"paragraph-spacing\">\n <label :class=\"labelClass\">{{ t.paragraph.paragraphSpacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <!-- Shows the built-in gap when the block sets none, so the field always\n reads the gap actually in effect rather than an empty box. -->\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.paragraphSpacing ?? RICH_TEXT_SPACING.paragraphGap\"\n min=\"0\"\n max=\"64\"\n @input=\"\n emit('update', {\n paragraphSpacing: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ParagraphBlock } from \"@templatical/types\";\nimport { RICH_TEXT_SPACING } from \"@templatical/types\";\n\ndefineProps<{\n block: ParagraphBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ParagraphBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\" data-testid=\"paragraph-spacing\">\n <label :class=\"labelClass\">{{ t.paragraph.paragraphSpacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <!-- Shows the built-in gap when the block sets none, so the field always\n reads the gap actually in effect rather than an empty box. -->\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.paragraphSpacing ?? RICH_TEXT_SPACING.paragraphGap\"\n min=\"0\"\n max=\"64\"\n @input=\"\n emit('update', {\n paragraphSpacing: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","import type { Block, ColumnLayout } from \"@templatical/types\";\n\nfunction expectedColumnCount(layout: ColumnLayout): number {\n if (layout === \"1\") return 1;\n if (layout === \"3\") return 3;\n return 2;\n}\n\n/**\n * Resize a section's `children` array to match a new column layout, without\n * dropping any blocks. Growing pads with empty columns; shrinking merges\n * any excess columns' blocks into the last kept column so the user never\n * silently loses content.\n */\nexport function rebalanceColumnChildren(\n current: Block[][],\n layout: ColumnLayout,\n): Block[][] {\n const target = expectedColumnCount(layout);\n\n if (current.length === target) {\n return current;\n }\n\n if (current.length < target) {\n const padding = Array.from(\n { length: target - current.length },\n () => [] as Block[],\n );\n return [...current, ...padding];\n }\n\n const kept = current.slice(0, target);\n const overflow = current.slice(target).flat();\n const merged = [...kept[target - 1], ...overflow];\n return [...kept.slice(0, target - 1), merged];\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport {\n layoutWrapsSlot,\n type BorderRadiusValue,\n type ColumnLayout,\n type SectionBlock,\n type SectionWrapper,\n type SpacingValue,\n} from \"@templatical/types\";\nimport { computed, inject } from \"vue\";\nimport { LAYOUT_KEY, SECTION_WRAPPER_KEY } from \"../../keys\";\nimport { rebalanceColumnChildren } from \"../../utils/rebalanceColumnChildren\";\n\nconst props = defineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst layout = inject(LAYOUT_KEY, undefined);\nconst sectionWrapper = inject(SECTION_WRAPPER_KEY, undefined);\n\nconst wrapsSlot = layout !== undefined && layoutWrapsSlot(layout);\n\nconst showWrapperPanel = computed(\n () => sectionWrapper !== false || !!props.block.wrapper,\n);\nconst disableTurnOn = computed(() => wrapsSlot && !props.block.wrapper);\nconst showNote = computed(() => wrapsSlot && showWrapperPanel.value);\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n\nfunction handleColumnsChange(event: Event): void {\n const columns = (event.target as HTMLSelectElement).value as ColumnLayout;\n const children = rebalanceColumnChildren(props.block.children, columns);\n emit(\"update\", { columns, children });\n}\n\nfunction handleStackOnMobileChange(checked: boolean): void {\n emit(\"update\", { stackOnMobile: checked });\n}\n\nfunction handleBorderRadiusChange(borderRadius: BorderRadiusValue): void {\n emit(\"update\", { borderRadius });\n}\n\nfunction setWrapperEnabled(enabled: boolean): void {\n emit(\"update\", {\n wrapper: enabled\n ? { padding: { top: 20, right: 20, bottom: 20, left: 20 } }\n : undefined,\n });\n}\n\nfunction updateWrapper(patch: Partial<SectionWrapper>): void {\n emit(\"update\", { wrapper: { ...props.block.wrapper, ...patch } });\n}\n\nfunction handleWrapperPadding(value: SpacingValue): void {\n updateWrapper({ padding: value });\n}\n\nfunction handleWrapperRadius(borderRadius: BorderRadiusValue): void {\n updateWrapper({ borderRadius });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"handleColumnsChange\"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n <div v-if=\"block.columns !== '1'\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.stackOnMobile !== false\"\n :label=\"t.section.stackOnMobile\"\n @update:model-value=\"handleStackOnMobileChange($event)\"\n />\n </div>\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section\"\n @update:model-value=\"handleBorderRadiusChange\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"section\"\n @update:model-value=\"emit('update', { border: $event })\"\n />\n <div v-if=\"showWrapperPanel\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"!!block.wrapper\"\n :label=\"t.section.wrapperEnable\"\n :disabled=\"disableTurnOn\"\n @update:model-value=\"setWrapperEnabled($event)\"\n />\n <p\n v-if=\"showNote\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.section.wrapperLayoutConflict }}\n </p>\n <div\n v-if=\"block.wrapper\"\n class=\"tpl:mt-3 tpl:ml-0.5 tpl:space-y-3 tpl:border-l tpl:border-[var(--tpl-border)] tpl:pl-3\"\n >\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.wrapper.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateWrapper({ backgroundColor: $event })\"\n />\n </div>\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"\n block.wrapper.padding ?? { top: 0, right: 0, bottom: 0, left: 0 }\n \"\n @update:model-value=\"handleWrapperPadding\"\n />\n <RadiusControl\n :model-value=\"block.wrapper.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section-wrapper\"\n @update:model-value=\"handleWrapperRadius\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport BorderControl from \"./BorderControl.vue\";\nimport RadiusControl from \"./RadiusControl.vue\";\nimport {\n layoutWrapsSlot,\n type BorderRadiusValue,\n type ColumnLayout,\n type SectionBlock,\n type SectionWrapper,\n type SpacingValue,\n} from \"@templatical/types\";\nimport { computed, inject } from \"vue\";\nimport { LAYOUT_KEY, SECTION_WRAPPER_KEY } from \"../../keys\";\nimport { rebalanceColumnChildren } from \"../../utils/rebalanceColumnChildren\";\n\nconst props = defineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst layout = inject(LAYOUT_KEY, undefined);\nconst sectionWrapper = inject(SECTION_WRAPPER_KEY, undefined);\n\nconst wrapsSlot = layout !== undefined && layoutWrapsSlot(layout);\n\nconst showWrapperPanel = computed(\n () => sectionWrapper !== false || !!props.block.wrapper,\n);\nconst disableTurnOn = computed(() => wrapsSlot && !props.block.wrapper);\nconst showNote = computed(() => wrapsSlot && showWrapperPanel.value);\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n\nfunction handleColumnsChange(event: Event): void {\n const columns = (event.target as HTMLSelectElement).value as ColumnLayout;\n const children = rebalanceColumnChildren(props.block.children, columns);\n emit(\"update\", { columns, children });\n}\n\nfunction handleStackOnMobileChange(checked: boolean): void {\n emit(\"update\", { stackOnMobile: checked });\n}\n\nfunction handleBorderRadiusChange(borderRadius: BorderRadiusValue): void {\n emit(\"update\", { borderRadius });\n}\n\nfunction setWrapperEnabled(enabled: boolean): void {\n emit(\"update\", {\n wrapper: enabled\n ? { padding: { top: 20, right: 20, bottom: 20, left: 20 } }\n : undefined,\n });\n}\n\nfunction updateWrapper(patch: Partial<SectionWrapper>): void {\n emit(\"update\", { wrapper: { ...props.block.wrapper, ...patch } });\n}\n\nfunction handleWrapperPadding(value: SpacingValue): void {\n updateWrapper({ padding: value });\n}\n\nfunction handleWrapperRadius(borderRadius: BorderRadiusValue): void {\n updateWrapper({ borderRadius });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"handleColumnsChange\"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n <div v-if=\"block.columns !== '1'\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.stackOnMobile !== false\"\n :label=\"t.section.stackOnMobile\"\n @update:model-value=\"handleStackOnMobileChange($event)\"\n />\n </div>\n <RadiusControl\n :model-value=\"block.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section\"\n @update:model-value=\"handleBorderRadiusChange\"\n />\n <BorderControl\n :model-value=\"block.border\"\n testid-prefix=\"section\"\n @update:model-value=\"emit('update', { border: $event })\"\n />\n <div v-if=\"showWrapperPanel\" class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\"\n :model-value=\"!!block.wrapper\"\n :label=\"t.section.wrapperEnable\"\n :disabled=\"disableTurnOn\"\n @update:model-value=\"setWrapperEnabled($event)\"\n />\n <p\n v-if=\"showNote\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.section.wrapperLayoutConflict }}\n </p>\n <div\n v-if=\"block.wrapper\"\n class=\"tpl:mt-3 tpl:ml-0.5 tpl:space-y-3 tpl:border-l tpl:border-[var(--tpl-border)] tpl:pl-3\"\n >\n <div>\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n :model-value=\"block.wrapper.backgroundColor ?? ''\"\n allow-transparent\n @update:model-value=\"updateWrapper({ backgroundColor: $event })\"\n />\n </div>\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"\n block.wrapper.padding ?? { top: 0, right: 0, bottom: 0, left: 0 }\n \"\n @update:model-value=\"handleWrapperPadding\"\n />\n <RadiusControl\n :model-value=\"block.wrapper.borderRadius\"\n :label=\"t.section.borderRadius\"\n :max=\"50\"\n testid-prefix=\"section-wrapper\"\n @update:model-value=\"handleWrapperRadius\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport { socialPlatformOptions } from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialIconStyle,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n data-testid=\"social-platform-select\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ t.social.platforms[platform] }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.iconStyle\"\n @change=\"\n updateField(\n 'iconStyle',\n ($event.target as HTMLSelectElement).value as SocialIconStyle,\n )\n \"\n >\n <option value=\"solid\">{{ t.social.styleSolid }}</option>\n <option value=\"outlined\">{{ t.social.styleOutlined }}</option>\n <option value=\"rounded\">{{ t.social.styleRounded }}</option>\n <option value=\"square\">{{ t.social.styleSquare }}</option>\n <option value=\"circle\">{{ t.social.styleCircle }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport { socialPlatformOptions } from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialIconStyle,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n data-testid=\"social-platform-select\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ t.social.platforms[platform] }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.iconStyle\"\n @change=\"\n updateField(\n 'iconStyle',\n ($event.target as HTMLSelectElement).value as SocialIconStyle,\n )\n \"\n >\n <option value=\"solid\">{{ t.social.styleSolid }}</option>\n <option value=\"outlined\">{{ t.social.styleOutlined }}</option>\n <option value=\"rounded\">{{ t.social.styleRounded }}</option>\n <option value=\"square\">{{ t.social.styleSquare }}</option>\n <option value=\"circle\">{{ t.social.styleCircle }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from({ length: columnCount }, (): TableCellData => ({\n id: generateId(),\n content: \"\",\n })),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.hasHeaderRow\"\n :label=\"t.table.hasHeaderRow\"\n @update:model-value=\"updateField('hasHeaderRow', $event)\"\n />\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor ?? ''\"\n :seed-color=\"DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from({ length: columnCount }, (): TableCellData => ({\n id: generateId(),\n content: \"\",\n })),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <ToggleSwitch\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.hasHeaderRow\"\n :label=\"t.table.hasHeaderRow\"\n @update:model-value=\"updateField('hasHeaderRow', $event)\"\n />\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor ?? ''\"\n :seed-color=\"DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color ?? ''\"\n @update:model-value=\"updateField('color', $event || undefined)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // An empty number field yields Number(\"\") === 0, which would collapse the\n // thumbnail. Absent means \"keep the aspect ratio\", so an invalid value must\n // not overwrite the last valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.video.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"video-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.video.heightAuto }}</option>\n <option value=\"custom\">{{ t.video.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"video-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useAliveFlag } from \"../../composables/useAliveFlag\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\nconst aliveFlag = useAliveFlag();\n\nconst DEFAULT_CUSTOM_HEIGHT = 200;\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst heightMode = computed(() =>\n typeof props.block.height === \"number\" ? \"custom\" : \"auto\",\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nfunction updateHeightMode(value: string): void {\n updateField(\"height\", value === \"custom\" ? DEFAULT_CUSTOM_HEIGHT : undefined);\n}\n\nfunction updateCustomHeight(raw: string): void {\n // An empty number field yields Number(\"\") === 0, which would collapse the\n // thumbnail. Absent means \"keep the aspect ratio\", so an invalid value must\n // not overwrite the last valid height either.\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) return;\n updateField(\"height\", n);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (!aliveFlag.alive) return;\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <ToggleSwitch\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"block.openInNewTab ?? false\"\n :label=\"t.video.openInNewTab\"\n @update:model-value=\"updateField('openInNewTab', $event)\"\n />\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.height }}</label>\n <select\n :class=\"inputClass\"\n data-testid=\"video-height-mode\"\n :value=\"heightMode\"\n @change=\"updateHeightMode(($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"auto\">{{ t.video.heightAuto }}</option>\n <option value=\"custom\">{{ t.video.heightCustom }}</option>\n </select>\n <div\n v-if=\"heightMode === 'custom'\"\n class=\"tpl:mt-2 tpl:flex tpl:items-stretch\"\n >\n <input\n type=\"number\"\n data-testid=\"video-height-input\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"20\"\n @input=\"updateCustomHeight(($event.target as HTMLInputElement).value)\"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport ParagraphToolbar from \"./toolbar/ParagraphToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n ParagraphBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n data-testid=\"block-toolbar\"\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Inline formatting lives in the TipTap toolbar; this carries only\n what applies to the block as a whole. -->\n <ParagraphToolbar\n v-else-if=\"blockType === 'paragraph'\"\n :block=\"block as ParagraphBlock\"\n @update=\"handleUpdate\"\n />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport ParagraphToolbar from \"./toolbar/ParagraphToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n ParagraphBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n data-testid=\"block-toolbar\"\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Inline formatting lives in the TipTap toolbar; this carries only\n what applies to the block as a whole. -->\n <ParagraphToolbar\n v-else-if=\"blockType === 'paragraph'\"\n :block=\"block as ParagraphBlock\"\n @update=\"handleUpdate\"\n />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n"],"mappings":"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"}