@templatical/editor 0.0.5 → 0.1.0

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 (169) hide show
  1. package/LICENSE +56 -0
  2. package/README.md +69 -0
  3. package/dist/{AiChatSidebar-DwME3f-a.js → AiChatSidebar-DSbBLDkW.js} +9 -9
  4. package/dist/{AiFeatureMenu-DJvWL1GZ.js → AiFeatureMenu-d04gmgud.js} +5 -5
  5. package/dist/{CloudEditor-Fe0ssRgi.js → CloudEditor-CjO4tzbV.js} +218 -227
  6. package/dist/{CollaboratorBar-DTT0EkZn.js → CollaboratorBar-DYBSTuMW.js} +3 -3
  7. package/dist/{CommentsSidebar-DrJhQRXK.js → CommentsSidebar-CQvUh5xt.js} +9 -9
  8. package/dist/CountdownBlock-D3olfssJ.js +92 -0
  9. package/dist/CountdownToolbar-ClpBwFoX.js +210 -0
  10. package/dist/{DesignReferenceSidebar-DdOht5zn.js → DesignReferenceSidebar-BOfeETis.js} +5 -5
  11. package/dist/{LoadingTrack-vK8W2PJf.js → LoadingTrack-CdqDYVR-.js} +1 -1
  12. package/dist/{ModuleBrowserModal-CiV_jOEM.js → ModuleBrowserModal-DKSSQonv.js} +7 -7
  13. package/dist/{ModulePreviewCanvas-Bmy6Y1WE.js → ModulePreviewCanvas-CoLNuu1o.js} +32 -32
  14. package/dist/NumberWithSuffix-_0l3Q4jq.js +424 -0
  15. package/dist/{ParagraphEditor-CoQ3NlS7.js → ParagraphEditor-C9R2ou7x.js} +57 -56
  16. package/dist/{RichTextEditorContent-CHJlh7HJ.js → RichTextEditorContent-BcY3Rcp4.js} +2 -2
  17. package/dist/{SaveModuleDialog-CD2ZYq1o.js → SaveModuleDialog-DmFe4VUc.js} +3 -3
  18. package/dist/{SnapshotHistory-DltsKvhP.js → SnapshotHistory-CAhikAOr.js} +7 -7
  19. package/dist/{TemplateScoringPanel-DmnmUE3y.js → TemplateScoringPanel-b5DnyIll.js} +10 -10
  20. package/dist/{TestEmailModal-Dl633j9o.js → TestEmailModal-BmDIzGYz.js} +3 -3
  21. package/dist/{TitleEditor-C7fds2Nc.js → TitleEditor-DtRYx-LS.js} +11 -11
  22. package/dist/{TplModal-C5_CF-qn.js → TplModal-BLbMDMd6.js} +4 -4
  23. package/dist/{_plugin-vue_export-helper-B1-bu7yR.js → _plugin-vue_export-helper-BHUPLLh1.js} +2 -2
  24. package/dist/{blockTypeIcons-BrKZB10B.js → blockTypeIcons-CEU2dMe1.js} +2 -2
  25. package/dist/cdn/chunks/{AiFeatureMenu-C5UQmEgV.js → AiFeatureMenu-DWMSYD2J.js} +4 -4
  26. package/dist/cdn/chunks/{AiFeatureMenu-C5UQmEgV.js.map → AiFeatureMenu-DWMSYD2J.js.map} +1 -1
  27. package/dist/cdn/chunks/{CloudEditor-DeTolKnf.js → CloudEditor-HrfSfr3G.js} +179 -188
  28. package/dist/cdn/chunks/CloudEditor-HrfSfr3G.js.map +1 -0
  29. package/dist/cdn/chunks/{CollaboratorBar-DO1nxSrr.js → CollaboratorBar-Cfok52TZ.js} +4 -4
  30. package/dist/cdn/chunks/{CollaboratorBar-DO1nxSrr.js.map → CollaboratorBar-Cfok52TZ.js.map} +1 -1
  31. package/dist/cdn/chunks/CountdownBlock-CkV21NEM.js +93 -0
  32. package/dist/cdn/chunks/CountdownBlock-CkV21NEM.js.map +1 -0
  33. package/dist/cdn/chunks/CountdownToolbar-CEmbR7kt.js +212 -0
  34. package/dist/cdn/chunks/CountdownToolbar-CEmbR7kt.js.map +1 -0
  35. package/dist/cdn/chunks/{ModuleBrowserModal-ChBr3aXj.js → ModuleBrowserModal-sASaPRot.js} +7 -7
  36. package/dist/cdn/chunks/{ModuleBrowserModal-ChBr3aXj.js.map → ModuleBrowserModal-sASaPRot.js.map} +1 -1
  37. package/dist/cdn/chunks/{ModulePreviewCanvas-DkSvri9H.js → ModulePreviewCanvas-D8zsajMf.js} +19 -19
  38. package/dist/cdn/chunks/{ModulePreviewCanvas-DkSvri9H.js.map → ModulePreviewCanvas-D8zsajMf.js.map} +1 -1
  39. package/dist/cdn/chunks/NumberWithSuffix-k5Te7Tuw.js +425 -0
  40. package/dist/cdn/chunks/NumberWithSuffix-k5Te7Tuw.js.map +1 -0
  41. package/dist/cdn/chunks/{ParagraphEditor-DU3oUKA7.js → ParagraphEditor-C1vQWGzW.js} +37 -37
  42. package/dist/cdn/chunks/ParagraphEditor-C1vQWGzW.js.map +1 -0
  43. package/dist/cdn/chunks/{RichTextEditorContent-BrsW1p9s.js → RichTextEditorContent-CT8dBB0R.js} +5 -5
  44. package/dist/cdn/chunks/{RichTextEditorContent-BrsW1p9s.js.map → RichTextEditorContent-CT8dBB0R.js.map} +1 -1
  45. package/dist/cdn/chunks/{SaveModuleDialog-CjqKkTEc.js → SaveModuleDialog-BkwKrB0N.js} +5 -5
  46. package/dist/cdn/chunks/{SaveModuleDialog-CjqKkTEc.js.map → SaveModuleDialog-BkwKrB0N.js.map} +1 -1
  47. package/dist/cdn/chunks/{TitleEditor-C8FYbadT.js → TitleEditor-BXqt42Px.js} +11 -11
  48. package/dist/cdn/chunks/{TitleEditor-C8FYbadT.js.map → TitleEditor-BXqt42Px.js.map} +1 -1
  49. package/dist/cdn/chunks/{blockTypeIcons-5QwYklNq.js → blockTypeIcons-BS6pWyGO.js} +3 -3
  50. package/dist/cdn/chunks/{blockTypeIcons-5QwYklNq.js.map → blockTypeIcons-BS6pWyGO.js.map} +1 -1
  51. package/dist/cdn/chunks/{de-BvYD17KT.js → de-3exf7MYE.js} +1 -1
  52. package/dist/cdn/chunks/{de-BvYD17KT.js.map → de-3exf7MYE.js.map} +1 -1
  53. package/dist/cdn/chunks/{de-BB3dgVOc.js → de-C8LxVUsb.js} +1 -1
  54. package/dist/cdn/chunks/de-C8LxVUsb.js.map +1 -0
  55. package/dist/cdn/chunks/{dist-KYv9v_1z2.js → dist-BpOx-ugr.js} +10 -3
  56. package/dist/cdn/chunks/dist-BpOx-ugr.js.map +1 -0
  57. package/dist/cdn/chunks/{draggable-BQNU47zu.js → draggable-Bcb86AsV.js} +1591 -1563
  58. package/dist/cdn/chunks/draggable-Bcb86AsV.js.map +1 -0
  59. package/dist/cdn/chunks/{emojiData-BVEJHcNH.js → emojiData-DUHzsh4j.js} +1 -1
  60. package/dist/cdn/chunks/{emojiData-BVEJHcNH.js.map → emojiData-DUHzsh4j.js.map} +1 -1
  61. package/dist/cdn/chunks/{en-DeDcpnoS.js → en-BoEycuqw.js} +1 -1
  62. package/dist/cdn/chunks/en-BoEycuqw.js.map +1 -0
  63. package/dist/cdn/chunks/{en-CpotcOPr.js → en-KNPUKxYp.js} +1 -1
  64. package/dist/cdn/chunks/{en-CpotcOPr.js.map → en-KNPUKxYp.js.map} +1 -1
  65. package/dist/cdn/chunks/{extensions-Bj7USRLr.js → extensions-DBxH6pz0.js} +21 -21
  66. package/dist/cdn/chunks/{extensions-Bj7USRLr.js.map → extensions-DBxH6pz0.js.map} +1 -1
  67. package/dist/cdn/chunks/{features-Ds0XUfte.js → features-C3n7AeUc.js} +1338 -1422
  68. package/dist/cdn/chunks/features-C3n7AeUc.js.map +1 -0
  69. package/dist/cdn/chunks/{icons-fWsuSvgd.js → icons-Hmpb0_8s.js} +2 -2
  70. package/dist/cdn/chunks/icons-Hmpb0_8s.js.map +1 -0
  71. package/dist/cdn/chunks/{liquid.browser-C1VIYISn.js → liquid.browser-CEMn-ZqL.js} +24 -17
  72. package/dist/cdn/chunks/liquid.browser-CEMn-ZqL.js.map +1 -0
  73. package/dist/cdn/chunks/{media-library-BGQm_OyC.js → media-library-C1wCpRY1.js} +114 -110
  74. package/dist/cdn/chunks/media-library-C1wCpRY1.js.map +1 -0
  75. package/dist/cdn/chunks/{pusher-DJPhQnE8.js → pusher-DwSQn6BA.js} +11 -8
  76. package/dist/cdn/chunks/pusher-DwSQn6BA.js.map +1 -0
  77. package/dist/cdn/chunks/readableTextColor-Cd_cgWO_.js.map +1 -1
  78. package/dist/cdn/chunks/{rolldown-runtime-DPITmOBR.js → rolldown-runtime-BNuo_Jkg.js} +1 -1
  79. package/dist/cdn/chunks/{src-3i8rPuqd.js → src-BUszJqfd.js} +8 -8
  80. package/dist/cdn/chunks/{src-3i8rPuqd.js.map → src-BUszJqfd.js.map} +1 -1
  81. package/dist/cdn/chunks/{styleConstants-DFe3I4Op.js → styleConstants-DP1VOca8.js} +1 -1
  82. package/dist/cdn/chunks/{styleConstants-DFe3I4Op.js.map → styleConstants-DP1VOca8.js.map} +1 -1
  83. package/dist/cdn/chunks/{styles-Dgijy53u.js → styles-U2VFFgxC.js} +623 -1239
  84. package/dist/cdn/chunks/styles-U2VFFgxC.js.map +1 -0
  85. package/dist/cdn/chunks/{tiptap-BhxaWR8R.js → tiptap-w5IqC8oW.js} +1610 -1364
  86. package/dist/cdn/chunks/tiptap-w5IqC8oW.js.map +1 -0
  87. package/dist/cdn/editor.css +1 -1
  88. package/dist/cdn/editor.js +92 -97
  89. package/dist/cdn/editor.js.map +1 -1
  90. package/dist/{check-B7kDuZmP.js → check-BDE2uFIC.js} +1 -1
  91. package/dist/{chevron-down-DJLW2Q9Z.js → chevron-down-BL1DcOiT.js} +1 -1
  92. package/dist/{circle-alert-E2vYPs5r.js → circle-alert-1tetIFJU.js} +1 -1
  93. package/dist/{clock-lWIIQA3C.js → clock-CNc3isoG.js} +1 -1
  94. package/dist/{dist-DysAFIPy.js → dist-7SaUYnxo.js} +385 -357
  95. package/dist/{dist-DmOE-Ubp.js → dist-BKZ5gvJf.js} +5 -5
  96. package/dist/{dist-CG-vEqSU.js → dist-CLg2qE0p.js} +2 -2
  97. package/dist/dist-CdzaZZSx.js +5 -0
  98. package/dist/dist-Cl9giA3B.js +5 -0
  99. package/dist/{dist-C_ymrGFi.js → dist-CpHKN3mI.js} +541 -508
  100. package/dist/{dist-4LiM9FDd.js → dist-DMuaebdo.js} +2 -2
  101. package/dist/{dist-ChAGLpWo.js → dist-DW_4_uv3.js} +2 -2
  102. package/dist/{dist-C1BIRHCQ.js → dist-DYG6FL7b.js} +2 -2
  103. package/dist/{dist-DrvKRSU6.js → dist-DbFMBx8E.js} +2 -2
  104. package/dist/{dist-D_HQYSY-.js → dist-Dn9cOicG.js} +2 -2
  105. package/dist/{dist-Ci5lFuUy.js → dist-ENKQZrNg.js} +138 -132
  106. package/dist/{dist-Bu7veieH.js → dist-fEtcLxee.js} +15 -15
  107. package/dist/{extensions-DWx_jj8v.js → extensions-Y880c0sP.js} +3 -3
  108. package/dist/{image-up-X4xIq4ea.js → image-up-4ZmaHFga.js} +1 -1
  109. package/dist/index.d.ts +234 -0
  110. package/dist/{liquid.browser-BemTg3sZ.js → liquid.browser-DZ62L7up.js} +20 -15
  111. package/dist/{loader-circle-BTQQxC3l.js → loader-circle-C7YEH4do.js} +1 -1
  112. package/dist/{message-circle-Blgm6V_h.js → message-circle-B_lxRh4z.js} +1 -1
  113. package/dist/{refresh-cw-Bb4PEeW1.js → refresh-cw-DmZNoimV.js} +1 -1
  114. package/dist/rolldown-runtime-gEudmnaM.js +23 -0
  115. package/dist/{scan-line-7lZPfOdm.js → scan-line-Cc9KlXo5.js} +1 -1
  116. package/dist/{send-C0ltAQrv.js → send-CMwNWTPX.js} +1 -1
  117. package/dist/{shield-check-f-qv4RKs.js → shield-check-DZFf4LTh.js} +1 -1
  118. package/dist/{sparkles-KhBCGlqB.js → sparkles-D80W88H8.js} +1 -1
  119. package/dist/{styleConstants-Cxw88naD.js → styleConstants-Codo7aUT.js} +6 -45
  120. package/dist/styles-unyYKihg.js +2942 -0
  121. package/dist/templatical-editor.css +1 -1
  122. package/dist/templatical-editor.js +85 -90
  123. package/dist/text-align-start-Cje6pwo4.js +43 -0
  124. package/dist/{trash-2-OwjZ-guZ.js → trash-2-BWvxOB4Q.js} +1 -1
  125. package/dist/{triangle-alert-DOSRIUYZ.js → triangle-alert-C6y94qVX.js} +1 -1
  126. package/dist/{useEditorCore-DUGD6pq_.js → useEditorCore-CQwWl2js.js} +1143 -1247
  127. package/dist/{x-CGlq2XQe.js → x-IyhJ6V90.js} +1 -1
  128. package/package.json +70 -52
  129. package/dist/cdn/chunks/AiChatSidebar-X_Bv3Qys.js +0 -2
  130. package/dist/cdn/chunks/CloudEditor-DeTolKnf.js.map +0 -1
  131. package/dist/cdn/chunks/CommentsSidebar-4MTw_hue.js +0 -2
  132. package/dist/cdn/chunks/DesignReferenceSidebar-Bswh4Yx4.js +0 -2
  133. package/dist/cdn/chunks/ParagraphEditor-DU3oUKA7.js.map +0 -1
  134. package/dist/cdn/chunks/SnapshotHistory-KME4xmn_.js +0 -2
  135. package/dist/cdn/chunks/TemplateScoringPanel-DgB3xDN6.js +0 -2
  136. package/dist/cdn/chunks/TestEmailModal-DdpvRbYf.js +0 -2
  137. package/dist/cdn/chunks/de-BB3dgVOc.js.map +0 -1
  138. package/dist/cdn/chunks/dist-BF5c3Dr-.js +0 -2
  139. package/dist/cdn/chunks/dist-BGzvIxcJ.js +0 -2
  140. package/dist/cdn/chunks/dist-CFemF8rI.js +0 -2
  141. package/dist/cdn/chunks/dist-Co6uFhFK.js +0 -2
  142. package/dist/cdn/chunks/dist-DCikBY9K.js +0 -2
  143. package/dist/cdn/chunks/dist-DUILafAC.js +0 -2
  144. package/dist/cdn/chunks/dist-DghiKH0A.js +0 -2
  145. package/dist/cdn/chunks/dist-Dw8ckvfK.js +0 -2
  146. package/dist/cdn/chunks/dist-H07p0KAw.js +0 -2
  147. package/dist/cdn/chunks/dist-KYv9v_1z2.js.map +0 -1
  148. package/dist/cdn/chunks/dist-MjnKIc0W.js +0 -2
  149. package/dist/cdn/chunks/dist-odp0vGRv.js +0 -2
  150. package/dist/cdn/chunks/draggable-BQNU47zu.js.map +0 -1
  151. package/dist/cdn/chunks/en-DeDcpnoS.js.map +0 -1
  152. package/dist/cdn/chunks/features-Ds0XUfte.js.map +0 -1
  153. package/dist/cdn/chunks/icons-fWsuSvgd.js.map +0 -1
  154. package/dist/cdn/chunks/liquid.browser-C1VIYISn.js.map +0 -1
  155. package/dist/cdn/chunks/media-library-BGQm_OyC.js.map +0 -1
  156. package/dist/cdn/chunks/pusher-DJPhQnE8.js.map +0 -1
  157. package/dist/cdn/chunks/styles-Dgijy53u.js.map +0 -1
  158. package/dist/cdn/chunks/tiptap-BhxaWR8R.js.map +0 -1
  159. package/dist/dist-DkypH7qG.js +0 -5
  160. package/dist/dist-Dxnd0GRf.js +0 -5
  161. package/dist/styles-fdXNRqI3.js +0 -3556
  162. package/dist/templatical-editor.umd.cjs +0 -179
  163. /package/dist/cdn/chunks/{_rolldown_dynamic_import_helper-DMEI4TQ3.js → _rolldown_dynamic_import_helper-BRcA6nWq.js} +0 -0
  164. /package/dist/{de-D7TLGIPA.js → de-DCRASLfd.js} +0 -0
  165. /package/dist/{emojiData-BfWQS72m.js → emojiData-CyACq8qV.js} +0 -0
  166. /package/dist/{en-DvtiEMwP.js → en-BC0igPeg.js} +0 -0
  167. /package/dist/{formatRelativeTime-DX3FgqN9.js → formatRelativeTime-mRxw8HK8.js} +0 -0
  168. /package/dist/{readableTextColor-LDlmVEUv.js → readableTextColor-B809bF5J.js} +0 -0
  169. /package/dist/{useMergeTag-BZ3X0bNr.js → useMergeTag-CSXcnFBc.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"styles-Dgijy53u.js","names":["css","hueCss","saturationCss","$emit","$emit"],"sources":["../../../src/components/Canvas.vue","../../../src/components/Canvas.vue","../../../src/components/Sidebar.vue","../../../src/components/Sidebar.vue","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/math.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/convert.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/compare.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/utils/dom.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/slider.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/hue.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/saturation.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/color-picker.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/hue.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/styles/saturation.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/components/color-picker.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/lib/entrypoints/hex.js","../../../../../node_modules/.bun/vanilla-colorful@0.7.2/node_modules/vanilla-colorful/hex-color-picker.js","../../../src/components/ColorPicker.vue","../../../src/components/ColorPicker.vue","../../../src/composables/useMergeTagField.ts","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagTextarea.vue","../../../src/components/MergeTagTextarea.vue","../../../src/components/TemplateSettings.vue","../../../src/components/TemplateSettings.vue","../../../src/components/MergeTagInput.vue","../../../src/components/MergeTagInput.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/SlidingPillSelect.vue","../../../src/components/SlidingPillSelect.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/CheckboxItem.vue","../../../src/components/toolbar/CheckboxItem.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/CountdownToolbar.vue","../../../src/components/toolbar/CountdownToolbar.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/MenuToolbar.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../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.vue","../../../src/components/Toolbar.vue","../../../src/components/RightSidebar.vue","../../../src/components/RightSidebar.vue","../../../src/components/ViewportToggle.vue","../../../src/components/ViewportToggle.vue","../../../src/components/PreviewToggle.vue","../../../src/components/PreviewToggle.vue","../../../src/components/DarkModeToggle.vue","../../../src/components/DarkModeToggle.vue","../../../src/components/EditorFooter.vue","../../../src/components/EditorFooter.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@templatical/types\";\nimport { ImageUp, Sparkles, SquarePlus } from \"@lucide/vue\";\nimport { computed, inject, type Component } from \"vue\";\nimport {\n EDITOR_KEY,\n CONDITION_PREVIEW_KEY,\n BLOCK_REGISTRY_KEY,\n CAPABILITIES_KEY,\n requireInject,\n} from \"../keys\";\nimport draggable from \"vuedraggable\";\nimport { resolveBlockComponent } from \"../utils/blockComponentResolver\";\nimport { readableTextColor } from \"../utils/readableTextColor\";\n\nimport BlockWrapper from \"./blocks/BlockWrapper.vue\";\nimport SectionBlock from \"./blocks/SectionBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport CountdownBlockComponent from \"./blocks/CountdownBlock.vue\";\n\nconst blockComponentMap: Record<string, Component> = {\n section: SectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n video: VideoBlock,\n countdown: CountdownBlockComponent,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n viewport: ViewportSize;\n content: TemplateContent;\n selectedBlockId: string | null;\n darkMode: boolean;\n previewMode: boolean;\n lockedBlocks?: Map<string, Collaborator>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select-block\", blockId: string | null): void;\n (e: \"open-ai-chat\"): void;\n (e: \"open-design-reference\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst editor = requireInject(EDITOR_KEY, \"Canvas\");\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst canUseAiChat = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"chat\") ?? false),\n);\nconst canUseDesignToTemplate = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"designToTemplate\") ?? false),\n);\n\nconst blocks = computed({\n get: () => props.content.blocks,\n set: (value: Block[]) => {\n editor.setContent({\n ...props.content,\n blocks: value,\n });\n },\n});\n\nconst viewportWidth = computed(() => {\n switch (props.viewport) {\n case \"mobile\":\n return 375;\n case \"tablet\":\n return 768;\n default:\n return props.content.settings.width;\n }\n});\n\n// Canvas dark mode preview: simulates how the email will appear in recipients'\n// dark-themed email clients. Uses CSS filter inversion — independent of the\n// editor UI theme (light/dark/auto) which is controlled via uiTheme config.\nconst canvasStyle = computed(() => ({\n backgroundColor: props.content.settings.backgroundColor,\n fontFamily: props.content.settings.fontFamily,\n}));\n\nfunction handleCanvasClick(event: MouseEvent): void {\n if (props.previewMode) {\n return;\n }\n if (event.target === event.currentTarget) {\n emit(\"select-block\", null);\n }\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, blockComponentMap);\n}\n\nfunction getBlockLock(blockId: string): Collaborator | null {\n return props.lockedBlocks?.get(blockId) ?? null;\n}\n\nfunction handleFetchData(\n block: Block,\n payload: {\n fieldValues: Record<string, unknown>;\n dataSourceFetched: boolean;\n },\n): void {\n if (block.type !== \"custom\") {\n return;\n }\n\n editor.updateBlock(block.id, {\n fieldValues: payload.fieldValues,\n dataSourceFetched: payload.dataSourceFetched,\n } as Partial<CustomBlockType>);\n}\n</script>\n\n<template>\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:rounded-lg\"\n :style=\"{\n width: `${viewportWidth}px`,\n backgroundColor: content.settings.backgroundColor,\n boxShadow: darkMode ? 'none' : 'var(--tpl-shadow-xl)',\n filter: darkMode ? 'invert(1) hue-rotate(180deg)' : 'none',\n transition:\n 'width 300ms cubic-bezier(0.34, 1.56, 0.64, 1), filter 300ms ease',\n }\"\n >\n <div\n class=\"tpl-canvas tpl:rounded-lg\"\n :class=\"{\n 'tpl-canvas--dark-mode': darkMode,\n 'tpl-preview-mode': previewMode,\n }\"\n :style=\"canvasStyle\"\n @click=\"handleCanvasClick\"\n >\n <draggable\n v-model=\"blocks\"\n group=\"blocks\"\n item-key=\"id\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n drag-class=\"tpl-dragging\"\n handle=\".tpl-block-btn\"\n :invert-swap=\"true\"\n :inverted-swap-threshold=\"0.65\"\n :disabled=\"previewMode\"\n class=\"tpl-canvas-blocks\"\n >\n <template #item=\"{ element: block }\">\n <div v-show=\"!conditionPreview?.isHidden(block.id)\">\n <div class=\"tpl:relative\">\n <!-- Collaboration lock overlay -->\n <div\n v-if=\"getBlockLock(block.id)\"\n class=\"tpl-collab-lock tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-[4] tpl:rounded-sm\"\n :style=\"{\n outline: `2px solid ${getBlockLock(block.id)!.color}`,\n outlineOffset: '-1px',\n }\"\n >\n <span\n class=\"tpl:absolute tpl:-top-0.5 tpl:left-1/2 tpl:z-[5] tpl:flex tpl:-translate-x-1/2 tpl:-translate-y-full tpl:items-center tpl:gap-1 tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:whitespace-nowrap\"\n :style=\"{\n backgroundColor: getBlockLock(block.id)!.color,\n color: readableTextColor(getBlockLock(block.id)!.color),\n }\"\n >\n <span\n class=\"tpl:inline-flex tpl:size-3 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[8px] tpl:font-bold\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg) 30%,\n transparent\n );\n \"\n >\n {{ getBlockLock(block.id)!.name.charAt(0) }}\n </span>\n {{ getBlockLock(block.id)!.name }}\n </span>\n </div>\n <BlockWrapper\n :block=\"block\"\n :is-selected=\"\n !previewMode &&\n selectedBlockId === block.id &&\n !getBlockLock(block.id)\n \"\n :viewport=\"viewport\"\n :preview-mode=\"previewMode\"\n @select=\"\n previewMode || getBlockLock(block.id)\n ? undefined\n : emit('select-block', block.id)\n \"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n @fetch-data=\"handleFetchData(block, $event)\"\n @update=\"\n (updates: Partial<Block>) =>\n editor.updateBlock(block.id, updates)\n \"\n />\n </BlockWrapper>\n </div>\n </div>\n </template>\n <template #footer>\n <div\n v-if=\"blocks.length === 0 && !previewMode\"\n class=\"tpl-canvas-empty tpl:m-6 tpl:flex tpl:min-h-[400px] tpl:flex-col tpl:items-center tpl:justify-center tpl:rounded-xl tpl:border-2 tpl:border-dashed tpl:px-10 tpl:py-12 tpl:text-center tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg-elevated)] tpl:font-[var(--tpl-font-family)]\"\n >\n <div\n class=\"tpl-canvas-empty-icon tpl:mb-4 tpl:text-[var(--tpl-primary)]\"\n >\n <SquarePlus :size=\"48\" :stroke-width=\"1\" />\n </div>\n <p\n class=\"tpl-canvas-empty-title tpl:m-0 tpl:mb-2 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-primary)]\"\n >\n {{ t.canvas.noBlocks }}\n </p>\n <p\n class=\"tpl-canvas-empty-text tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.dragHint }}\n </p>\n <p\n v-if=\"canUseAiChat\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintChat }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-ai-chat')\"\n >\n <Sparkles :size=\"14\" :stroke-width=\"2\" />\n {{ t.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate\"\n class=\"tpl:m-0 tpl:mt-4 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintDesign }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-design-reference')\"\n >\n <ImageUp :size=\"14\" :stroke-width=\"2\" />\n {{ t.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* Counter-invert images so they look normal in dark mode */\n.tpl-canvas--dark-mode :deep(img) {\n filter: invert(1) hue-rotate(180deg);\n}\n\n/* Counter-invert editor UI chrome so controls stay readable */\n.tpl-canvas--dark-mode :deep(.tpl-block-actions),\n.tpl-canvas--dark-mode :deep(.tpl-condition-toggle),\n.tpl-canvas--dark-mode :deep(.tpl-block-hidden-overlay) {\n filter: invert(1) hue-rotate(180deg);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@templatical/types\";\nimport { ImageUp, Sparkles, SquarePlus } from \"@lucide/vue\";\nimport { computed, inject, type Component } from \"vue\";\nimport {\n EDITOR_KEY,\n CONDITION_PREVIEW_KEY,\n BLOCK_REGISTRY_KEY,\n CAPABILITIES_KEY,\n requireInject,\n} from \"../keys\";\nimport draggable from \"vuedraggable\";\nimport { resolveBlockComponent } from \"../utils/blockComponentResolver\";\nimport { readableTextColor } from \"../utils/readableTextColor\";\n\nimport BlockWrapper from \"./blocks/BlockWrapper.vue\";\nimport SectionBlock from \"./blocks/SectionBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport CountdownBlockComponent from \"./blocks/CountdownBlock.vue\";\n\nconst blockComponentMap: Record<string, Component> = {\n section: SectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n video: VideoBlock,\n countdown: CountdownBlockComponent,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n viewport: ViewportSize;\n content: TemplateContent;\n selectedBlockId: string | null;\n darkMode: boolean;\n previewMode: boolean;\n lockedBlocks?: Map<string, Collaborator>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select-block\", blockId: string | null): void;\n (e: \"open-ai-chat\"): void;\n (e: \"open-design-reference\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst editor = requireInject(EDITOR_KEY, \"Canvas\");\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst canUseAiChat = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"chat\") ?? false),\n);\nconst canUseDesignToTemplate = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"designToTemplate\") ?? false),\n);\n\nconst blocks = computed({\n get: () => props.content.blocks,\n set: (value: Block[]) => {\n editor.setContent({\n ...props.content,\n blocks: value,\n });\n },\n});\n\nconst viewportWidth = computed(() => {\n switch (props.viewport) {\n case \"mobile\":\n return 375;\n case \"tablet\":\n return 768;\n default:\n return props.content.settings.width;\n }\n});\n\n// Canvas dark mode preview: simulates how the email will appear in recipients'\n// dark-themed email clients. Uses CSS filter inversion — independent of the\n// editor UI theme (light/dark/auto) which is controlled via uiTheme config.\nconst canvasStyle = computed(() => ({\n backgroundColor: props.content.settings.backgroundColor,\n fontFamily: props.content.settings.fontFamily,\n}));\n\nfunction handleCanvasClick(event: MouseEvent): void {\n if (props.previewMode) {\n return;\n }\n if (event.target === event.currentTarget) {\n emit(\"select-block\", null);\n }\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, blockComponentMap);\n}\n\nfunction getBlockLock(blockId: string): Collaborator | null {\n return props.lockedBlocks?.get(blockId) ?? null;\n}\n\nfunction handleFetchData(\n block: Block,\n payload: {\n fieldValues: Record<string, unknown>;\n dataSourceFetched: boolean;\n },\n): void {\n if (block.type !== \"custom\") {\n return;\n }\n\n editor.updateBlock(block.id, {\n fieldValues: payload.fieldValues,\n dataSourceFetched: payload.dataSourceFetched,\n } as Partial<CustomBlockType>);\n}\n</script>\n\n<template>\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:rounded-lg\"\n :style=\"{\n width: `${viewportWidth}px`,\n backgroundColor: content.settings.backgroundColor,\n boxShadow: darkMode ? 'none' : 'var(--tpl-shadow-xl)',\n filter: darkMode ? 'invert(1) hue-rotate(180deg)' : 'none',\n transition:\n 'width 300ms cubic-bezier(0.34, 1.56, 0.64, 1), filter 300ms ease',\n }\"\n >\n <div\n class=\"tpl-canvas tpl:rounded-lg\"\n :class=\"{\n 'tpl-canvas--dark-mode': darkMode,\n 'tpl-preview-mode': previewMode,\n }\"\n :style=\"canvasStyle\"\n @click=\"handleCanvasClick\"\n >\n <draggable\n v-model=\"blocks\"\n group=\"blocks\"\n item-key=\"id\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n drag-class=\"tpl-dragging\"\n handle=\".tpl-block-btn\"\n :invert-swap=\"true\"\n :inverted-swap-threshold=\"0.65\"\n :disabled=\"previewMode\"\n class=\"tpl-canvas-blocks\"\n >\n <template #item=\"{ element: block }\">\n <div v-show=\"!conditionPreview?.isHidden(block.id)\">\n <div class=\"tpl:relative\">\n <!-- Collaboration lock overlay -->\n <div\n v-if=\"getBlockLock(block.id)\"\n class=\"tpl-collab-lock tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-[4] tpl:rounded-sm\"\n :style=\"{\n outline: `2px solid ${getBlockLock(block.id)!.color}`,\n outlineOffset: '-1px',\n }\"\n >\n <span\n class=\"tpl:absolute tpl:-top-0.5 tpl:left-1/2 tpl:z-[5] tpl:flex tpl:-translate-x-1/2 tpl:-translate-y-full tpl:items-center tpl:gap-1 tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:whitespace-nowrap\"\n :style=\"{\n backgroundColor: getBlockLock(block.id)!.color,\n color: readableTextColor(getBlockLock(block.id)!.color),\n }\"\n >\n <span\n class=\"tpl:inline-flex tpl:size-3 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[8px] tpl:font-bold\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg) 30%,\n transparent\n );\n \"\n >\n {{ getBlockLock(block.id)!.name.charAt(0) }}\n </span>\n {{ getBlockLock(block.id)!.name }}\n </span>\n </div>\n <BlockWrapper\n :block=\"block\"\n :is-selected=\"\n !previewMode &&\n selectedBlockId === block.id &&\n !getBlockLock(block.id)\n \"\n :viewport=\"viewport\"\n :preview-mode=\"previewMode\"\n @select=\"\n previewMode || getBlockLock(block.id)\n ? undefined\n : emit('select-block', block.id)\n \"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n @fetch-data=\"handleFetchData(block, $event)\"\n @update=\"\n (updates: Partial<Block>) =>\n editor.updateBlock(block.id, updates)\n \"\n />\n </BlockWrapper>\n </div>\n </div>\n </template>\n <template #footer>\n <div\n v-if=\"blocks.length === 0 && !previewMode\"\n class=\"tpl-canvas-empty tpl:m-6 tpl:flex tpl:min-h-[400px] tpl:flex-col tpl:items-center tpl:justify-center tpl:rounded-xl tpl:border-2 tpl:border-dashed tpl:px-10 tpl:py-12 tpl:text-center tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg-elevated)] tpl:font-[var(--tpl-font-family)]\"\n >\n <div\n class=\"tpl-canvas-empty-icon tpl:mb-4 tpl:text-[var(--tpl-primary)]\"\n >\n <SquarePlus :size=\"48\" :stroke-width=\"1\" />\n </div>\n <p\n class=\"tpl-canvas-empty-title tpl:m-0 tpl:mb-2 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-primary)]\"\n >\n {{ t.canvas.noBlocks }}\n </p>\n <p\n class=\"tpl-canvas-empty-text tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.dragHint }}\n </p>\n <p\n v-if=\"canUseAiChat\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintChat }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-ai-chat')\"\n >\n <Sparkles :size=\"14\" :stroke-width=\"2\" />\n {{ t.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate\"\n class=\"tpl:m-0 tpl:mt-4 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintDesign }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-design-reference')\"\n >\n <ImageUp :size=\"14\" :stroke-width=\"2\" />\n {{ t.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* Counter-invert images so they look normal in dark mode */\n.tpl-canvas--dark-mode :deep(img) {\n filter: invert(1) hue-rotate(180deg);\n}\n\n/* Counter-invert editor UI chrome so controls stay readable */\n.tpl-canvas--dark-mode :deep(.tpl-block-actions),\n.tpl-canvas--dark-mode :deep(.tpl-condition-toggle),\n.tpl-canvas--dark-mode :deep(.tpl-block-hidden-overlay) {\n filter: invert(1) hue-rotate(180deg);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { Block, BlockType } from \"@templatical/types\";\nimport { createBlock, createCustomBlock } from \"@templatical/types\";\nimport { Package } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n BLOCK_DEFAULTS_KEY,\n CAPABILITIES_KEY,\n EDITOR_KEY,\n} from \"../keys\";\n\ninterface BlockTypeItem {\n type: BlockType | string;\n label: string;\n isCustom?: boolean;\n icon?: string;\n}\n\nconst { t, format } = useI18n();\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\nconst blockDefaults = inject(BLOCK_DEFAULTS_KEY, undefined);\nconst editor = inject(EDITOR_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst showModulesSection = computed(\n () => (caps.savedModules?.moduleCount.value ?? 0) > 0,\n);\n\nconst isExpanded = ref(false);\n\nconst builtInBlockTypeOrder: string[] = [\n \"section\",\n \"image\",\n \"title\",\n \"paragraph\",\n \"button\",\n \"divider\",\n \"video\",\n \"social\",\n \"menu\",\n \"table\",\n \"spacer\",\n \"html\",\n];\n\nconst builtInBlockTypes = computed<BlockTypeItem[]>(() => {\n const types: BlockTypeItem[] = builtInBlockTypeOrder.map((type) => ({\n type,\n label: getBlockTypeLabel(type, t),\n }));\n\n // Countdown requires Templatical Cloud for server-side GIF rendering\n if (caps.plan) {\n types.splice(-1, 0, {\n type: \"countdown\",\n label: getBlockTypeLabel(\"countdown\", t),\n });\n }\n\n return types;\n});\n\nconst customBlockItems = computed<BlockTypeItem[]>(() => {\n return customBlockDefinitions.map((def) => ({\n type: `custom:${def.type}`,\n label: def.name,\n isCustom: true,\n icon: def.icon,\n }));\n});\n\nconst blockTypes = computed<BlockTypeItem[]>(() => [\n ...builtInBlockTypes.value,\n ...customBlockItems.value,\n]);\n\nfunction createBlockFromItem(item: BlockTypeItem): Block {\n if (item.isCustom) {\n const customType = item.type.replace(\"custom:\", \"\");\n const definition = customBlockDefinitions.find(\n (d) => d.type === customType,\n );\n if (definition) {\n return createCustomBlock(definition);\n }\n }\n\n return createBlock(item.type as BlockType, blockDefaults);\n}\n\nfunction insertBlockFromItem(item: BlockTypeItem): void {\n if (!editor) return;\n const block = createBlockFromItem(item);\n editor.addBlock(block);\n editor.selectBlock(block.id);\n}\n\nfunction handlePaletteKeydown(event: KeyboardEvent, item: BlockTypeItem): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n insertBlockFromItem(item);\n }\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n class=\"tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden\"\n :style=\"{\n width: isExpanded ? '200px' : '48px',\n backgroundColor: 'var(--tpl-bg-elevated)',\n borderRight: '1px solid var(--tpl-border)',\n boxShadow: isExpanded ? 'var(--tpl-shadow-lg)' : 'none',\n transition:\n 'width 200ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n @mouseenter=\"isExpanded = true\"\n @mouseleave=\"isExpanded = false\"\n @focusin=\"isExpanded = true\"\n @focusout=\"isExpanded = false\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection\"\n class=\"tpl:border-b tpl:px-1 tpl:pb-1 tpl:border-[var(--tpl-border)]\"\n >\n <button\n type=\"button\"\n :aria-label=\"t.sidebarNav.browseModules\"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)]\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"caps.savedModules?.openBrowser()\"\n >\n <Package :size=\"20\" :stroke-width=\"1.5\" class=\"tpl:shrink-0\" />\n <span\n v-if=\"isExpanded\"\n class=\"tpl:flex-1 tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ t.modules.title }}\n </span>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ caps.savedModules?.moduleCount.value ?? 0 }}\n </span>\n </button>\n </div>\n <draggable\n :list=\"blockTypes\"\n :group=\"{ name: 'blocks', pull: 'clone', put: false }\"\n :clone=\"createBlockFromItem\"\n :sort=\"false\"\n item-key=\"type\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:p-1\"\n >\n <template #item=\"{ element: blockType }\">\n <button\n type=\"button\"\n :data-palette-type=\"blockType.type\"\n :aria-label=\"\n format(t.sidebarNav.insertBlock, { block: blockType.label })\n \"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-grab tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] active:tpl:cursor-grabbing\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"insertBlockFromItem(blockType)\"\n @keydown=\"handlePaletteKeydown($event, blockType)\"\n >\n <div\n class=\"tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:scale-105\"\n >\n <component\n :is=\"blockTypeIcons[blockType.type]\"\n v-if=\"blockTypeIcons[blockType.type]\"\n :size=\"20\"\n :stroke-width=\"1.5\"\n />\n <CustomBlockIcon\n v-else-if=\"blockType.isCustom\"\n :icon=\"blockType.icon\"\n :size=\"20\"\n />\n </div>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ blockType.label }}\n </span>\n </button>\n </template>\n </draggable>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { Block, BlockType } from \"@templatical/types\";\nimport { createBlock, createCustomBlock } from \"@templatical/types\";\nimport { Package } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n BLOCK_DEFAULTS_KEY,\n CAPABILITIES_KEY,\n EDITOR_KEY,\n} from \"../keys\";\n\ninterface BlockTypeItem {\n type: BlockType | string;\n label: string;\n isCustom?: boolean;\n icon?: string;\n}\n\nconst { t, format } = useI18n();\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\nconst blockDefaults = inject(BLOCK_DEFAULTS_KEY, undefined);\nconst editor = inject(EDITOR_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst showModulesSection = computed(\n () => (caps.savedModules?.moduleCount.value ?? 0) > 0,\n);\n\nconst isExpanded = ref(false);\n\nconst builtInBlockTypeOrder: string[] = [\n \"section\",\n \"image\",\n \"title\",\n \"paragraph\",\n \"button\",\n \"divider\",\n \"video\",\n \"social\",\n \"menu\",\n \"table\",\n \"spacer\",\n \"html\",\n];\n\nconst builtInBlockTypes = computed<BlockTypeItem[]>(() => {\n const types: BlockTypeItem[] = builtInBlockTypeOrder.map((type) => ({\n type,\n label: getBlockTypeLabel(type, t),\n }));\n\n // Countdown requires Templatical Cloud for server-side GIF rendering\n if (caps.plan) {\n types.splice(-1, 0, {\n type: \"countdown\",\n label: getBlockTypeLabel(\"countdown\", t),\n });\n }\n\n return types;\n});\n\nconst customBlockItems = computed<BlockTypeItem[]>(() => {\n return customBlockDefinitions.map((def) => ({\n type: `custom:${def.type}`,\n label: def.name,\n isCustom: true,\n icon: def.icon,\n }));\n});\n\nconst blockTypes = computed<BlockTypeItem[]>(() => [\n ...builtInBlockTypes.value,\n ...customBlockItems.value,\n]);\n\nfunction createBlockFromItem(item: BlockTypeItem): Block {\n if (item.isCustom) {\n const customType = item.type.replace(\"custom:\", \"\");\n const definition = customBlockDefinitions.find(\n (d) => d.type === customType,\n );\n if (definition) {\n return createCustomBlock(definition);\n }\n }\n\n return createBlock(item.type as BlockType, blockDefaults);\n}\n\nfunction insertBlockFromItem(item: BlockTypeItem): void {\n if (!editor) return;\n const block = createBlockFromItem(item);\n editor.addBlock(block);\n editor.selectBlock(block.id);\n}\n\nfunction handlePaletteKeydown(event: KeyboardEvent, item: BlockTypeItem): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n insertBlockFromItem(item);\n }\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n class=\"tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden\"\n :style=\"{\n width: isExpanded ? '200px' : '48px',\n backgroundColor: 'var(--tpl-bg-elevated)',\n borderRight: '1px solid var(--tpl-border)',\n boxShadow: isExpanded ? 'var(--tpl-shadow-lg)' : 'none',\n transition:\n 'width 200ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n @mouseenter=\"isExpanded = true\"\n @mouseleave=\"isExpanded = false\"\n @focusin=\"isExpanded = true\"\n @focusout=\"isExpanded = false\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection\"\n class=\"tpl:border-b tpl:px-1 tpl:pb-1 tpl:border-[var(--tpl-border)]\"\n >\n <button\n type=\"button\"\n :aria-label=\"t.sidebarNav.browseModules\"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)]\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"caps.savedModules?.openBrowser()\"\n >\n <Package :size=\"20\" :stroke-width=\"1.5\" class=\"tpl:shrink-0\" />\n <span\n v-if=\"isExpanded\"\n class=\"tpl:flex-1 tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ t.modules.title }}\n </span>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ caps.savedModules?.moduleCount.value ?? 0 }}\n </span>\n </button>\n </div>\n <draggable\n :list=\"blockTypes\"\n :group=\"{ name: 'blocks', pull: 'clone', put: false }\"\n :clone=\"createBlockFromItem\"\n :sort=\"false\"\n item-key=\"type\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:p-1\"\n >\n <template #item=\"{ element: blockType }\">\n <button\n type=\"button\"\n :data-palette-type=\"blockType.type\"\n :aria-label=\"\n format(t.sidebarNav.insertBlock, { block: blockType.label })\n \"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-grab tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] active:tpl:cursor-grabbing\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"insertBlockFromItem(blockType)\"\n @keydown=\"handlePaletteKeydown($event, blockType)\"\n >\n <div\n class=\"tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:scale-105\"\n >\n <component\n :is=\"blockTypeIcons[blockType.type]\"\n v-if=\"blockTypeIcons[blockType.type]\"\n :size=\"20\"\n :stroke-width=\"1.5\"\n />\n <CustomBlockIcon\n v-else-if=\"blockType.isCustom\"\n :icon=\"blockType.icon\"\n :size=\"20\"\n />\n </div>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ blockType.label }}\n </span>\n </button>\n </template>\n </draggable>\n </aside>\n</template>\n","// Clamps a value between an upper and lower bound.\n// We use ternary operators because it makes the minified code\n// 2 times shorter then `Math.min(Math.max(a,b),c)`\nexport const clamp = (number, min = 0, max = 1) => {\n return number > max ? max : number < min ? min : number;\n};\nexport const round = (number, digits = 0, base = Math.pow(10, digits)) => {\n return Math.round(base * number) / base;\n};\n//# sourceMappingURL=math.js.map","import { round } from './math.js';\n/**\n * Valid CSS <angle> units.\n * https://developer.mozilla.org/en-US/docs/Web/CSS/angle\n */\nconst angleUnits = {\n grad: 360 / 400,\n turn: 360,\n rad: 360 / (Math.PI * 2)\n};\nexport const hexToHsva = (hex) => rgbaToHsva(hexToRgba(hex));\nexport const hexToRgba = (hex) => {\n if (hex[0] === '#')\n hex = hex.substring(1);\n if (hex.length < 6) {\n return {\n r: parseInt(hex[0] + hex[0], 16),\n g: parseInt(hex[1] + hex[1], 16),\n b: parseInt(hex[2] + hex[2], 16),\n a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1\n };\n }\n return {\n r: parseInt(hex.substring(0, 2), 16),\n g: parseInt(hex.substring(2, 4), 16),\n b: parseInt(hex.substring(4, 6), 16),\n a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1\n };\n};\nexport const parseHue = (value, unit = 'deg') => {\n return Number(value) * (angleUnits[unit] || 1);\n};\nexport const hslaStringToHsva = (hslString) => {\n const matcher = /hsla?\\(?\\s*(-?\\d*\\.?\\d+)(deg|rad|grad|turn)?[,\\s]+(-?\\d*\\.?\\d+)%?[,\\s]+(-?\\d*\\.?\\d+)%?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(hslString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return hslaToHsva({\n h: parseHue(match[1], match[2]),\n s: Number(match[3]),\n l: Number(match[4]),\n a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1)\n });\n};\nexport const hslStringToHsva = hslaStringToHsva;\nexport const hslaToHsva = ({ h, s, l, a }) => {\n s *= (l < 50 ? l : 100 - l) / 100;\n return {\n h: h,\n s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0,\n v: l + s,\n a\n };\n};\nexport const hsvaToHex = (hsva) => rgbaToHex(hsvaToRgba(hsva));\nexport const hsvaToHsla = ({ h, s, v, a }) => {\n const hh = ((200 - s) * v) / 100;\n return {\n h: round(h),\n s: round(hh > 0 && hh < 200 ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 : 0),\n l: round(hh / 2),\n a: round(a, 2)\n };\n};\nexport const hsvaToHsvString = (hsva) => {\n const { h, s, v } = roundHsva(hsva);\n return `hsv(${h}, ${s}%, ${v}%)`;\n};\nexport const hsvaToHsvaString = (hsva) => {\n const { h, s, v, a } = roundHsva(hsva);\n return `hsva(${h}, ${s}%, ${v}%, ${a})`;\n};\nexport const hsvaToHslString = (hsva) => {\n const { h, s, l } = hsvaToHsla(hsva);\n return `hsl(${h}, ${s}%, ${l}%)`;\n};\nexport const hsvaToHslaString = (hsva) => {\n const { h, s, l, a } = hsvaToHsla(hsva);\n return `hsla(${h}, ${s}%, ${l}%, ${a})`;\n};\nexport const hsvaToRgba = ({ h, s, v, a }) => {\n h = (h / 360) * 6;\n s = s / 100;\n v = v / 100;\n const hh = Math.floor(h), b = v * (1 - s), c = v * (1 - (h - hh) * s), d = v * (1 - (1 - h + hh) * s), module = hh % 6;\n return {\n r: round([v, c, b, b, d, v][module] * 255),\n g: round([d, v, v, c, b, b][module] * 255),\n b: round([b, b, d, v, v, c][module] * 255),\n a: round(a, 2)\n };\n};\nexport const hsvaToRgbString = (hsva) => {\n const { r, g, b } = hsvaToRgba(hsva);\n return `rgb(${r}, ${g}, ${b})`;\n};\nexport const hsvaToRgbaString = (hsva) => {\n const { r, g, b, a } = hsvaToRgba(hsva);\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n};\nexport const hsvaStringToHsva = (hsvString) => {\n const matcher = /hsva?\\(?\\s*(-?\\d*\\.?\\d+)(deg|rad|grad|turn)?[,\\s]+(-?\\d*\\.?\\d+)%?[,\\s]+(-?\\d*\\.?\\d+)%?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(hsvString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return roundHsva({\n h: parseHue(match[1], match[2]),\n s: Number(match[3]),\n v: Number(match[4]),\n a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1)\n });\n};\nexport const hsvStringToHsva = hsvaStringToHsva;\nexport const rgbaStringToHsva = (rgbaString) => {\n const matcher = /rgba?\\(?\\s*(-?\\d*\\.?\\d+)(%)?[,\\s]+(-?\\d*\\.?\\d+)(%)?[,\\s]+(-?\\d*\\.?\\d+)(%)?,?\\s*[/\\s]*(-?\\d*\\.?\\d+)?(%)?\\s*\\)?/i;\n const match = matcher.exec(rgbaString);\n if (!match)\n return { h: 0, s: 0, v: 0, a: 1 };\n return rgbaToHsva({\n r: Number(match[1]) / (match[2] ? 100 / 255 : 1),\n g: Number(match[3]) / (match[4] ? 100 / 255 : 1),\n b: Number(match[5]) / (match[6] ? 100 / 255 : 1),\n a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1)\n });\n};\nexport const rgbStringToHsva = rgbaStringToHsva;\nconst format = (number) => {\n const hex = number.toString(16);\n return hex.length < 2 ? '0' + hex : hex;\n};\nexport const rgbaToHex = ({ r, g, b, a }) => {\n const alphaHex = a < 1 ? format(round(a * 255)) : '';\n return '#' + format(r) + format(g) + format(b) + alphaHex;\n};\nexport const rgbaToHsva = ({ r, g, b, a }) => {\n const max = Math.max(r, g, b);\n const delta = max - Math.min(r, g, b);\n // prettier-ignore\n const hh = delta\n ? max === r\n ? (g - b) / delta\n : max === g\n ? 2 + (b - r) / delta\n : 4 + (r - g) / delta\n : 0;\n return {\n h: round(60 * (hh < 0 ? hh + 6 : hh)),\n s: round(max ? (delta / max) * 100 : 0),\n v: round((max / 255) * 100),\n a\n };\n};\nexport const roundHsva = (hsva) => ({\n h: round(hsva.h),\n s: round(hsva.s),\n v: round(hsva.v),\n a: round(hsva.a, 2)\n});\nexport const rgbaToRgb = ({ r, g, b }) => ({ r, g, b });\nexport const hslaToHsl = ({ h, s, l }) => ({ h, s, l });\nexport const hsvaToHsv = (hsva) => {\n const { h, s, v } = roundHsva(hsva);\n return { h, s, v };\n};\n//# sourceMappingURL=convert.js.map","import { hexToRgba } from './convert.js';\nexport const equalColorObjects = (first, second) => {\n if (first === second)\n return true;\n for (const prop in first) {\n // The following allows for a type-safe calling of this function (first & second have to be HSL, HSV, or RGB)\n // with type-unsafe iterating over object keys. TS does not allow this without an index (`[key: string]: number`)\n // on an object to define how iteration is normally done. To ensure extra keys are not allowed on our types,\n // we must cast our object to unknown (as RGB demands `r` be a key, while `Record<string, x>` does not care if\n // there is or not), and then as a type TS can iterate over.\n if (first[prop] !==\n second[prop])\n return false;\n }\n return true;\n};\nexport const equalColorString = (first, second) => {\n return first.replace(/\\s/g, '') === second.replace(/\\s/g, '');\n};\nexport const equalHex = (first, second) => {\n if (first.toLowerCase() === second.toLowerCase())\n return true;\n // To compare colors like `#FFF` and `ffffff` we convert them into RGB objects\n return equalColorObjects(hexToRgba(first), hexToRgba(second));\n};\n//# sourceMappingURL=compare.js.map","const cache = {};\nexport const tpl = (html) => {\n let template = cache[html];\n if (!template) {\n template = document.createElement('template');\n template.innerHTML = html;\n cache[html] = template;\n }\n return template;\n};\nexport const fire = (target, type, detail) => {\n target.dispatchEvent(new CustomEvent(type, {\n bubbles: true,\n detail\n }));\n};\n//# sourceMappingURL=dom.js.map","import { fire, tpl } from '../utils/dom.js';\nimport { clamp } from '../utils/math.js';\nlet hasTouched = false;\n// Check if an event was triggered by touch\nconst isTouch = (e) => 'touches' in e;\n// Prevent mobile browsers from handling mouse events (conflicting with touch ones).\n// If we detected a touch interaction before, we prefer reacting to touch events only.\nconst isValid = (event) => {\n if (hasTouched && !isTouch(event))\n return false;\n if (!hasTouched)\n hasTouched = isTouch(event);\n return true;\n};\nconst pointerMove = (target, event) => {\n const pointer = isTouch(event) ? event.touches[0] : event;\n const rect = target.el.getBoundingClientRect();\n fire(target.el, 'move', target.getMove({\n x: clamp((pointer.pageX - (rect.left + window.pageXOffset)) / rect.width),\n y: clamp((pointer.pageY - (rect.top + window.pageYOffset)) / rect.height)\n }));\n};\nconst keyMove = (target, event) => {\n // We use `keyCode` instead of `key` to reduce the size of the library.\n const keyCode = event.keyCode;\n // Ignore all keys except arrow ones, Page Up, Page Down, Home and End.\n if (keyCode > 40 || (target.xy && keyCode < 37) || keyCode < 33)\n return;\n // Do not scroll page by keys when color picker element has focus.\n event.preventDefault();\n // Send relative offset to the parent component.\n fire(target.el, 'move', target.getMove({\n x: keyCode === 39 // Arrow Right\n ? 0.01\n : keyCode === 37 // Arrow Left\n ? -0.01\n : keyCode === 34 // Page Down\n ? 0.05\n : keyCode === 33 // Page Up\n ? -0.05\n : keyCode === 35 // End\n ? 1\n : keyCode === 36 // Home\n ? -1\n : 0,\n y: keyCode === 40 // Arrow down\n ? 0.01\n : keyCode === 38 // Arrow Up\n ? -0.01\n : 0\n }, true));\n};\nexport class Slider {\n constructor(root, part, aria, xy) {\n const template = tpl(`<div role=\"slider\" tabindex=\"0\" part=\"${part}\" ${aria}><div part=\"${part}-pointer\"></div></div>`);\n root.appendChild(template.content.cloneNode(true));\n const el = root.querySelector(`[part=${part}]`);\n el.addEventListener('mousedown', this);\n el.addEventListener('touchstart', this);\n el.addEventListener('keydown', this);\n this.el = el;\n this.xy = xy;\n this.nodes = [el.firstChild, el];\n }\n set dragging(state) {\n const toggleEvent = state ? document.addEventListener : document.removeEventListener;\n toggleEvent(hasTouched ? 'touchmove' : 'mousemove', this);\n toggleEvent(hasTouched ? 'touchend' : 'mouseup', this);\n }\n handleEvent(event) {\n switch (event.type) {\n case 'mousedown':\n case 'touchstart':\n event.preventDefault();\n // event.button is 0 in mousedown for left button activation\n if (!isValid(event) || (!hasTouched && event.button != 0))\n return;\n this.el.focus();\n pointerMove(this, event);\n this.dragging = true;\n break;\n case 'mousemove':\n case 'touchmove':\n event.preventDefault();\n pointerMove(this, event);\n break;\n case 'mouseup':\n case 'touchend':\n this.dragging = false;\n break;\n case 'keydown':\n keyMove(this, event);\n break;\n }\n }\n style(styles) {\n styles.forEach((style, i) => {\n for (const p in style) {\n this.nodes[i].style.setProperty(p, style[p]);\n }\n });\n }\n}\n//# sourceMappingURL=slider.js.map","import { Slider } from './slider.js';\nimport { hsvaToHslString } from '../utils/convert.js';\nimport { clamp, round } from '../utils/math.js';\nexport class Hue extends Slider {\n constructor(root) {\n super(root, 'hue', 'aria-label=\"Hue\" aria-valuemin=\"0\" aria-valuemax=\"360\"', false);\n }\n update({ h }) {\n this.h = h;\n this.style([\n {\n left: `${(h / 360) * 100}%`,\n color: hsvaToHslString({ h, s: 100, v: 100, a: 1 })\n }\n ]);\n this.el.setAttribute('aria-valuenow', `${round(h)}`);\n }\n getMove(offset, key) {\n // Hue measured in degrees of the color circle ranging from 0 to 360\n return { h: key ? clamp(this.h + offset.x * 360, 0, 360) : 360 * offset.x };\n }\n}\n//# sourceMappingURL=hue.js.map","import { Slider } from './slider.js';\nimport { hsvaToHslString } from '../utils/convert.js';\nimport { clamp, round } from '../utils/math.js';\nexport class Saturation extends Slider {\n constructor(root) {\n super(root, 'saturation', 'aria-label=\"Color\"', true);\n }\n update(hsva) {\n this.hsva = hsva;\n this.style([\n {\n top: `${100 - hsva.v}%`,\n left: `${hsva.s}%`,\n color: hsvaToHslString(hsva)\n },\n {\n 'background-color': hsvaToHslString({ h: hsva.h, s: 100, v: 100, a: 1 })\n }\n ]);\n this.el.setAttribute('aria-valuetext', `Saturation ${round(hsva.s)}%, Brightness ${round(hsva.v)}%`);\n }\n getMove(offset, key) {\n // Saturation and brightness always fit into [0, 100] range\n return {\n s: key ? clamp(this.hsva.s + offset.x * 100, 0, 100) : offset.x * 100,\n v: key ? clamp(this.hsva.v - offset.y * 100, 0, 100) : Math.round(100 - offset.y * 100)\n };\n }\n}\n//# sourceMappingURL=saturation.js.map","export default `:host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:\"\";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}`;\n//# sourceMappingURL=color-picker.js.map","export default `[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}`;\n//# sourceMappingURL=hue.js.map","export default `[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}`;\n//# sourceMappingURL=saturation.js.map","import { equalColorObjects } from '../utils/compare.js';\nimport { fire, tpl } from '../utils/dom.js';\nimport { Hue } from './hue.js';\nimport { Saturation } from './saturation.js';\nimport css from '../styles/color-picker.js';\nimport hueCss from '../styles/hue.js';\nimport saturationCss from '../styles/saturation.js';\nconst $isSame = Symbol('same');\nconst $color = Symbol('color');\nconst $hsva = Symbol('hsva');\nconst $update = Symbol('update');\nconst $parts = Symbol('parts');\nexport const $css = Symbol('css');\nexport const $sliders = Symbol('sliders');\nexport class ColorPicker extends HTMLElement {\n static get observedAttributes() {\n return ['color'];\n }\n get [$css]() {\n return [css, hueCss, saturationCss];\n }\n get [$sliders]() {\n return [Saturation, Hue];\n }\n get color() {\n return this[$color];\n }\n set color(newColor) {\n if (!this[$isSame](newColor)) {\n const newHsva = this.colorModel.toHsva(newColor);\n this[$update](newHsva);\n this[$color] = newColor;\n }\n }\n constructor() {\n super();\n const template = tpl(`<style>${this[$css].join('')}</style>`);\n const root = this.attachShadow({ mode: 'open' });\n root.appendChild(template.content.cloneNode(true));\n root.addEventListener('move', this);\n this[$parts] = this[$sliders].map((slider) => new slider(root));\n }\n connectedCallback() {\n // A user may set a property on an _instance_ of an element,\n // before its prototype has been connected to this class.\n // If so, we need to run it through the proper class setter.\n if (this.hasOwnProperty('color')) {\n const value = this.color;\n delete this['color'];\n this.color = value;\n }\n else if (!this.color) {\n this.color = this.colorModel.defaultColor;\n }\n }\n attributeChangedCallback(_attr, _oldVal, newVal) {\n const color = this.colorModel.fromAttr(newVal);\n if (!this[$isSame](color)) {\n this.color = color;\n }\n }\n handleEvent(event) {\n // Merge the current HSV color object with updated params.\n const oldHsva = this[$hsva];\n const newHsva = { ...oldHsva, ...event.detail };\n this[$update](newHsva);\n let newColor;\n if (!equalColorObjects(newHsva, oldHsva) &&\n !this[$isSame]((newColor = this.colorModel.fromHsva(newHsva)))) {\n this[$color] = newColor;\n fire(this, 'color-changed', { value: newColor });\n }\n }\n [$isSame](color) {\n return this.color && this.colorModel.equal(color, this.color);\n }\n [$update](hsva) {\n this[$hsva] = hsva;\n this[$parts].forEach((part) => part.update(hsva));\n }\n}\n//# sourceMappingURL=color-picker.js.map","import { ColorPicker } from '../components/color-picker.js';\nimport { hexToHsva, hsvaToHex } from '../utils/convert.js';\nimport { equalHex } from '../utils/compare.js';\nconst colorModel = {\n defaultColor: '#000',\n toHsva: hexToHsva,\n fromHsva: ({ h, s, v }) => hsvaToHex({ h, s, v, a: 1 }),\n equal: equalHex,\n fromAttr: (color) => color\n};\nexport class HexBase extends ColorPicker {\n get colorModel() {\n return colorModel;\n }\n}\n//# sourceMappingURL=hex.js.map","import { HexBase } from './lib/entrypoints/hex.js';\n/**\n * A color picker custom element that uses HEX format.\n *\n * @element hex-color-picker\n *\n * @prop {string} color - Selected color in HEX format.\n * @attr {string} color - Selected color in HEX format.\n *\n * @fires color-changed - Event fired when color property changes.\n *\n * @csspart hue - A hue selector container.\n * @csspart saturation - A saturation selector container\n * @csspart hue-pointer - A hue pointer element.\n * @csspart saturation-pointer - A saturation pointer element.\n */\nexport class HexColorPicker extends HexBase {\n}\ncustomElements.define('hex-color-picker', HexColorPicker);\n//# sourceMappingURL=hex-color-picker.js.map","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n colorTextClass,\n DEFAULT_TEXT_COLOR,\n} from \"../constants/styleConstants\";\nimport \"vanilla-colorful\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n size?: \"default\" | \"large\";\n swatchOnly?: boolean;\n disabled?: boolean;\n }>(),\n {\n placeholder: DEFAULT_TEXT_COLOR,\n size: \"default\",\n swatchOnly: false,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst popoverRef = ref<HTMLElement>();\nconst swatchRef = ref<HTMLElement>();\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [swatchRef] },\n);\n\nconst internalColor = computed({\n get: () => props.modelValue || DEFAULT_TEXT_COLOR,\n set: (val) => emit(\"update:modelValue\", val),\n});\n\nfunction onPickerChange(e: Event) {\n internalColor.value = (e as CustomEvent).detail.value;\n}\n\nfunction onTextInput(e: Event) {\n internalColor.value = (e.target as HTMLInputElement).value;\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'tpl:flex tpl:gap-2 tpl:relative',\n disabled && 'tpl:opacity-60 tpl:cursor-not-allowed',\n ]\"\n >\n <button\n ref=\"swatchRef\"\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.pickColor\"\n :aria-expanded=\"open\"\n :class=\"[\n 'tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-0.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n open\n ? 'tpl:border-[var(--tpl-primary)] tpl:shadow-[var(--tpl-ring)]'\n : !disabled && 'hover:tpl:border-[var(--tpl-text-dim)]',\n size === 'large' ? 'tpl:size-12' : 'tpl:size-10',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"tpl:block tpl:size-full tpl:rounded-[calc(var(--tpl-radius-sm)-2px)]\"\n :style=\"{ backgroundColor: internalColor }\"\n />\n </button>\n <input\n v-if=\"!swatchOnly\"\n type=\"text\"\n :class=\"colorTextClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @input=\"onTextInput\"\n />\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n enter-from-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n enter-to-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-active-class=\"tpl:transition-all tpl:duration-[80ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n leave-from-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-to-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n >\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n class=\"tpl:absolute tpl:left-0 tpl:top-full tpl:z-50 tpl:mt-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-3 tpl:shadow-lg\"\n >\n <hex-color-picker\n :color=\"internalColor\"\n :aria-label=\"t.colorPicker.pickColor\"\n @color-changed=\"onPickerChange\"\n @keydown.escape=\"open = false\"\n />\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\nhex-color-picker {\n --hcp-width: 200px;\n}\n\nhex-color-picker::part(hue) {\n border-radius: var(--tpl-radius-sm, 7px);\n}\n\nhex-color-picker::part(saturation) {\n border-radius: var(--tpl-radius-sm, 7px) var(--tpl-radius-sm, 7px) 0 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n colorTextClass,\n DEFAULT_TEXT_COLOR,\n} from \"../constants/styleConstants\";\nimport \"vanilla-colorful\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n size?: \"default\" | \"large\";\n swatchOnly?: boolean;\n disabled?: boolean;\n }>(),\n {\n placeholder: DEFAULT_TEXT_COLOR,\n size: \"default\",\n swatchOnly: false,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst popoverRef = ref<HTMLElement>();\nconst swatchRef = ref<HTMLElement>();\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [swatchRef] },\n);\n\nconst internalColor = computed({\n get: () => props.modelValue || DEFAULT_TEXT_COLOR,\n set: (val) => emit(\"update:modelValue\", val),\n});\n\nfunction onPickerChange(e: Event) {\n internalColor.value = (e as CustomEvent).detail.value;\n}\n\nfunction onTextInput(e: Event) {\n internalColor.value = (e.target as HTMLInputElement).value;\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'tpl:flex tpl:gap-2 tpl:relative',\n disabled && 'tpl:opacity-60 tpl:cursor-not-allowed',\n ]\"\n >\n <button\n ref=\"swatchRef\"\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.pickColor\"\n :aria-expanded=\"open\"\n :class=\"[\n 'tpl:shrink-0 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-0.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n open\n ? 'tpl:border-[var(--tpl-primary)] tpl:shadow-[var(--tpl-ring)]'\n : !disabled && 'hover:tpl:border-[var(--tpl-text-dim)]',\n size === 'large' ? 'tpl:size-12' : 'tpl:size-10',\n ]\"\n @click=\"!disabled && (open = !open)\"\n >\n <span\n class=\"tpl:block tpl:size-full tpl:rounded-[calc(var(--tpl-radius-sm)-2px)]\"\n :style=\"{ backgroundColor: internalColor }\"\n />\n </button>\n <input\n v-if=\"!swatchOnly\"\n type=\"text\"\n :class=\"colorTextClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"t.colorPicker.hexValue\"\n @input=\"onTextInput\"\n />\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n enter-from-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n enter-to-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-active-class=\"tpl:transition-all tpl:duration-[80ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n leave-from-class=\"tpl:opacity-100 tpl:scale-100 tpl:translate-y-0\"\n leave-to-class=\"tpl:opacity-0 tpl:scale-95 tpl:translate-y-1\"\n >\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n class=\"tpl:absolute tpl:left-0 tpl:top-full tpl:z-50 tpl:mt-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-3 tpl:shadow-lg\"\n >\n <hex-color-picker\n :color=\"internalColor\"\n :aria-label=\"t.colorPicker.pickColor\"\n @color-changed=\"onPickerChange\"\n @keydown.escape=\"open = false\"\n />\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\nhex-color-picker {\n --hcp-width: 200px;\n}\n\nhex-color-picker::part(hue) {\n border-radius: var(--tpl-radius-sm, 7px);\n}\n\nhex-color-picker::part(saturation) {\n border-radius: var(--tpl-radius-sm, 7px) var(--tpl-radius-sm, 7px) 0 0;\n}\n</style>\n","import {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport { computed, nextTick, ref, type ComputedRef, type Ref } from \"vue\";\nimport { useMergeTag } from \"./useMergeTag\";\n\nexport type MergeTagSegment =\n | { type: \"text\"; value: string }\n | { type: \"mergeTag\"; value: string; label: string }\n | { type: \"logicMergeTag\"; value: string; keyword: string };\n\nexport interface UseMergeTagFieldOptions {\n modelValue: () => string;\n emit: (value: string) => void;\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n}\n\nexport interface UseMergeTagFieldReturn {\n segments: ComputedRef<MergeTagSegment[]>;\n hasMergeTags: ComputedRef<boolean>;\n mergeTagEnabled: boolean;\n isRequestingMergeTag: Ref<boolean>;\n isEditing: Ref<boolean>;\n startEditing: () => void;\n stopEditing: () => void;\n handleInput: (event: Event) => void;\n clearValue: () => void;\n insertMergeTag: () => Promise<void>;\n}\n\nexport function useMergeTagField(\n options: UseMergeTagFieldOptions,\n): UseMergeTagFieldReturn {\n const { modelValue, emit, elementRef } = options;\n\n const {\n isEnabled: mergeTagEnabled,\n isRequesting: isRequestingMergeTag,\n isMergeTagValue,\n getMergeTagLabel,\n requestMergeTag,\n syntax,\n } = useMergeTag();\n\n const isEditing = ref(false);\n let insertingMergeTag = false;\n\n const segments = computed((): MergeTagSegment[] => {\n const val = modelValue();\n if (!val) return [];\n\n const result: MergeTagSegment[] = [];\n const combinedSource = `(${syntax.value.source}|${syntax.logic.source})`;\n const regex = new RegExp(combinedSource, \"g\");\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(val)) !== null) {\n if (match.index > lastIndex) {\n result.push({\n type: \"text\",\n value: val.slice(lastIndex, match.index),\n });\n }\n\n const matched = match[0];\n if (isMergeTagValue(matched)) {\n result.push({\n type: \"mergeTag\",\n value: matched,\n label: getMergeTagLabel(matched),\n });\n } else if (isLogicMergeTagValue(matched, syntax)) {\n result.push({\n type: \"logicMergeTag\",\n value: matched,\n keyword: getLogicMergeTagKeyword(matched, syntax),\n });\n } else {\n result.push({ type: \"text\", value: matched });\n }\n\n lastIndex = match.index + matched.length;\n }\n\n if (lastIndex < val.length) {\n result.push({ type: \"text\", value: val.slice(lastIndex) });\n }\n\n return result;\n });\n\n const hasMergeTags = computed(() =>\n segments.value.some(\n (s) => s.type === \"mergeTag\" || s.type === \"logicMergeTag\",\n ),\n );\n\n function startEditing(): void {\n isEditing.value = true;\n nextTick(() => {\n elementRef.value?.focus();\n const len = modelValue()?.length || 0;\n elementRef.value?.setSelectionRange(len, len);\n });\n }\n\n function stopEditing(): void {\n if (insertingMergeTag) return;\n isEditing.value = false;\n }\n\n function handleInput(event: Event): void {\n emit((event.target as HTMLInputElement | HTMLTextAreaElement).value);\n }\n\n function clearValue(): void {\n emit(\"\");\n }\n\n async function insertMergeTag(): Promise<void> {\n const cursorPos =\n isEditing.value && elementRef.value\n ? (elementRef.value.selectionStart ?? modelValue().length)\n : modelValue().length;\n\n insertingMergeTag = true;\n const mergeTag = await requestMergeTag();\n insertingMergeTag = false;\n\n if (mergeTag) {\n const before = modelValue().slice(0, cursorPos);\n const after = modelValue().slice(cursorPos);\n const newValue = before + mergeTag.value + after;\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n const newPos = cursorPos + mergeTag.value.length;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n }\n\n return {\n segments,\n hasMergeTags,\n mergeTagEnabled,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all hover:tpl:text-[var(--tpl-danger)] hover:tpl:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all hover:tpl:text-[var(--tpl-danger)] hover:tpl:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] hover:tpl:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insert }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] hover:tpl:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insert }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n rows?: number;\n }>(),\n {\n placeholder: \"\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n mergeTagEnabled,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: textareaRef,\n});\n\nconst textareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs 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:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:placeholder:text-[var(--tpl-text-dim)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-[5rem] tpl:cursor-pointer tpl:items-start tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3 tpl:py-2 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <textarea\n ref=\"textareaRef\"\n :class=\"textareaClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n rows?: number;\n }>(),\n {\n placeholder: \"\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n mergeTagEnabled,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: textareaRef,\n});\n\nconst textareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs 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:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:placeholder:text-[var(--tpl-text-dim)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-[5rem] tpl:cursor-pointer tpl:items-start tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3 tpl:py-2 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <textarea\n ref=\"textareaRef\"\n :class=\"textareaClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { TemplateSettings } from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Info, Square } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { FONTS_MANAGER_KEY, requireInject } from \"../keys\";\n\nconst props = defineProps<{\n settings: TemplateSettings;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst PREHEADER_MAX_LENGTH = 150;\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"TemplateSettings\");\nconst fontFamilies = computed(() => fontsManager.fonts.value);\n\n// If current font is not in available list (e.g., custom font when disabled), use default\nconst displayedFontFamily = computed(() => {\n const isAvailable = fontFamilies.value.some(\n (font) => font.value === props.settings.fontFamily,\n );\n return isAvailable\n ? props.settings.fontFamily\n : fontsManager.defaultFont.value;\n});\n\nconst widthPresets = [\n { value: 480, label: \"480px\" },\n { value: 600, label: \"600px\" },\n { value: 700, label: \"700px\" },\n { value: 800, label: \"800px\" },\n];\n</script>\n\n<template>\n <aside\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:flex-1 tpl:flex-col tpl:gap-3 tpl:overflow-y-auto tpl:p-4\"\n >\n <!-- Layout card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Square\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.layout }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.widthPreset\n }}</label>\n <div\n class=\"tpl:grid tpl:grid-cols-4 tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n v-for=\"preset in widthPresets\"\n :key=\"preset.value\"\n class=\"tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :style=\"{\n backgroundColor:\n settings.width === preset.value\n ? 'var(--tpl-bg)'\n : 'transparent',\n color:\n settings.width === preset.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow:\n settings.width === preset.value\n ? 'var(--tpl-shadow)'\n : 'none',\n }\"\n @click=\"emit('update', { width: preset.value })\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Circle\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.appearance }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.backgroundColor\n }}</label>\n <ColorPicker\n :model-value=\"settings.backgroundColor\"\n :placeholder=\"DEFAULT_BG_COLOR\"\n @update:model-value=\"emit('update', { backgroundColor: $event })\"\n />\n </div>\n\n <div>\n <label :class=\"labelClass\">{{ t.templateSettings.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"displayedFontFamily\"\n @change=\"\n emit('update', {\n fontFamily: ($event.target as HTMLSelectElement).value,\n })\n \"\n >\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>\n\n <!-- Preheader card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Eye\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.preheaderText }}</span>\n </div>\n\n <div>\n <MergeTagTextarea\n :model-value=\"settings.preheaderText ?? ''\"\n :placeholder=\"t.templateSettings.preheaderTextPlaceholder\"\n :rows=\"2\"\n @update:model-value=\"\n emit('update', {\n preheaderText: $event.replace(/[\\r\\n]/g, ' ') || undefined,\n })\n \"\n />\n <div\n class=\"tpl:mt-1 tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.preheaderTextHint }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:text-xs tpl:tabular-nums tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ (settings.preheaderText ?? \"\").length }}/{{\n PREHEADER_MAX_LENGTH\n }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Tips card -->\n <div\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <div\n class=\"tpl:mb-2.5 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text-muted)]\"\n >\n <Info :size=\"14\" :stroke-width=\"2\" />\n <span>{{ t.templateSettings.tips }}</span>\n </div>\n <ul\n class=\"tpl:m-0 tpl:pl-[18px] tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip1 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip2 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip3 }}\n </li>\n </ul>\n </div>\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { TemplateSettings } from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Info, Square } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { FONTS_MANAGER_KEY, requireInject } from \"../keys\";\n\nconst props = defineProps<{\n settings: TemplateSettings;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst PREHEADER_MAX_LENGTH = 150;\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"TemplateSettings\");\nconst fontFamilies = computed(() => fontsManager.fonts.value);\n\n// If current font is not in available list (e.g., custom font when disabled), use default\nconst displayedFontFamily = computed(() => {\n const isAvailable = fontFamilies.value.some(\n (font) => font.value === props.settings.fontFamily,\n );\n return isAvailable\n ? props.settings.fontFamily\n : fontsManager.defaultFont.value;\n});\n\nconst widthPresets = [\n { value: 480, label: \"480px\" },\n { value: 600, label: \"600px\" },\n { value: 700, label: \"700px\" },\n { value: 800, label: \"800px\" },\n];\n</script>\n\n<template>\n <aside\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:flex-1 tpl:flex-col tpl:gap-3 tpl:overflow-y-auto tpl:p-4\"\n >\n <!-- Layout card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Square\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.layout }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.widthPreset\n }}</label>\n <div\n class=\"tpl:grid tpl:grid-cols-4 tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n v-for=\"preset in widthPresets\"\n :key=\"preset.value\"\n class=\"tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :style=\"{\n backgroundColor:\n settings.width === preset.value\n ? 'var(--tpl-bg)'\n : 'transparent',\n color:\n settings.width === preset.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow:\n settings.width === preset.value\n ? 'var(--tpl-shadow)'\n : 'none',\n }\"\n @click=\"emit('update', { width: preset.value })\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Circle\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.appearance }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.backgroundColor\n }}</label>\n <ColorPicker\n :model-value=\"settings.backgroundColor\"\n :placeholder=\"DEFAULT_BG_COLOR\"\n @update:model-value=\"emit('update', { backgroundColor: $event })\"\n />\n </div>\n\n <div>\n <label :class=\"labelClass\">{{ t.templateSettings.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"displayedFontFamily\"\n @change=\"\n emit('update', {\n fontFamily: ($event.target as HTMLSelectElement).value,\n })\n \"\n >\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>\n\n <!-- Preheader card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Eye\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.preheaderText }}</span>\n </div>\n\n <div>\n <MergeTagTextarea\n :model-value=\"settings.preheaderText ?? ''\"\n :placeholder=\"t.templateSettings.preheaderTextPlaceholder\"\n :rows=\"2\"\n @update:model-value=\"\n emit('update', {\n preheaderText: $event.replace(/[\\r\\n]/g, ' ') || undefined,\n })\n \"\n />\n <div\n class=\"tpl:mt-1 tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.preheaderTextHint }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:text-xs tpl:tabular-nums tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ (settings.preheaderText ?? \"\").length }}/{{\n PREHEADER_MAX_LENGTH\n }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Tips card -->\n <div\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <div\n class=\"tpl:mb-2.5 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text-muted)]\"\n >\n <Info :size=\"14\" :stroke-width=\"2\" />\n <span>{{ t.templateSettings.tips }}</span>\n </div>\n <ul\n class=\"tpl:m-0 tpl:pl-[18px] tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip1 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip2 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip3 }}\n </li>\n </ul>\n </div>\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n mergeTagEnabled,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n mergeTagEnabled,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"mergeTagEnabled\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.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\ndefineProps<{\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\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\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 <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.button.openInNewTab }}\n </label>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\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 </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderRadius\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'borderRadius',\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.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>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.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\ndefineProps<{\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\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\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 <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.button.openInNewTab }}\n </label>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\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 </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderRadius\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'borderRadius',\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.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>\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)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] active:tpl: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] focus:tpl:border-[var(--tpl-primary)] focus:tpl: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)] hover:tpl: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)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] active:tpl: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] focus:tpl:border-[var(--tpl-primary)] focus:tpl: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)] hover:tpl: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\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 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\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 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 CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n DEFAULT_BG_COLOR,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } 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\" | \"css\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"tablet\" | \"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: { key: VisibilityKey; icon: Component; labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\" }[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"tablet\", icon: Tablet, labelKey: \"showOnTablet\" },\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 tablet: isVisible(\"tablet\"),\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 <div class=\"tpl:mt-4\">\n <SpacingControl\n :label=\"t.blockSettings.margin\"\n :model-value=\"block.styles.margin\"\n @update:model-value=\"updateStyle('margin', $event)\"\n />\n </div>\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 size=\"large\"\n :model-value=\"block.styles.backgroundColor || DEFAULT_BG_COLOR\"\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 <label\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n :checked=\"isVisible(item.key)\"\n @change=\"toggleVisibility(item.key)\"\n />\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </label>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.customCss\"\n :open=\"openSections.has('css')\"\n @toggle=\"toggleSection('css')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.css }}</label>\n <textarea\n :value=\"block.customCss || ''\"\n :placeholder=\"t.blockSettings.cssPlaceholder\"\n rows=\"3\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n customCss: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\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 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:duration-150 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-[11px] 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-[11px] 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=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:opacity-50\"\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-[11px] 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 >\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 >\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 CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n DEFAULT_BG_COLOR,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } 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\" | \"css\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"tablet\" | \"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: { key: VisibilityKey; icon: Component; labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\" }[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"tablet\", icon: Tablet, labelKey: \"showOnTablet\" },\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 tablet: isVisible(\"tablet\"),\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 <div class=\"tpl:mt-4\">\n <SpacingControl\n :label=\"t.blockSettings.margin\"\n :model-value=\"block.styles.margin\"\n @update:model-value=\"updateStyle('margin', $event)\"\n />\n </div>\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 size=\"large\"\n :model-value=\"block.styles.backgroundColor || DEFAULT_BG_COLOR\"\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 <label\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n :checked=\"isVisible(item.key)\"\n @change=\"toggleVisibility(item.key)\"\n />\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </label>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.customCss\"\n :open=\"openSections.has('css')\"\n @toggle=\"toggleSection('css')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.css }}</label>\n <textarea\n :value=\"block.customCss || ''\"\n :placeholder=\"t.blockSettings.cssPlaceholder\"\n rows=\"3\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n customCss: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\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 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:duration-150 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-[11px] 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-[11px] 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=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:opacity-50\"\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-[11px] 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 >\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 >\n </div>\n </template>\n </div>\n </CollapsibleSection>\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 pillOffset = computed(() => {\n const index = props.options.findIndex((o) => o.value === props.modelValue);\n return `translateX(${Math.max(index, 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 -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${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 pillOffset = computed(() => {\n const index = props.options.findIndex((o) => o.value === props.modelValue);\n return `translateX(${Math.max(index, 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 -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${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 { 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\">\ndefineProps<{\n modelValue: boolean;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n</script>\n\n<template>\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"modelValue\"\n @change=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).checked)\n \"\n />\n {{ label }}\n </label>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n modelValue: boolean;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n</script>\n\n<template>\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"modelValue\"\n @change=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).checked)\n \"\n />\n {{ label }}\n </label>\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\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\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\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport CheckboxItem from \"./CheckboxItem.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <CheckboxItem\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.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.countdown.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 <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <CheckboxItem\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n class=\"tpl:mb-3.5\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport CheckboxItem from \"./CheckboxItem.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <CheckboxItem\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.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.countdown.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 <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <CheckboxItem\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n class=\"tpl:mb-3.5\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/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 <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n readOnly ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\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 <span v-if=\"field.required\" class=\"tpl:text-[var(--tpl-danger)]\">\n *\n </span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"field.label\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n readOnly\n ? 'tpl:opacity-60 tpl:cursor-not-allowed'\n : 'tpl:cursor-pointer',\n ]\"\n :disabled=\"readOnly\"\n @click=\"!readOnly && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\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\";\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 <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n readOnly ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\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 <span v-if=\"field.required\" class=\"tpl:text-[var(--tpl-danger)]\">\n *\n </span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"field.label\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n readOnly\n ? 'tpl:opacity-60 tpl:cursor-not-allowed'\n : 'tpl:cursor-pointer',\n ]\"\n :disabled=\"readOnly\"\n @click=\"!readOnly && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\">*</span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\">*</span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport { DEFAULT_TEXT_COLOR } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\n\ndefineProps<{\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</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 || DEFAULT_TEXT_COLOR\"\n :placeholder=\"field.placeholder || DEFAULT_TEXT_COLOR\"\n :disabled=\"readOnly\"\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 { CustomBlockColorField } from \"@templatical/types\";\nimport { DEFAULT_TEXT_COLOR } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\n\ndefineProps<{\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</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 || DEFAULT_TEXT_COLOR\"\n :placeholder=\"field.placeholder || DEFAULT_TEXT_COLOR\"\n :disabled=\"readOnly\"\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 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\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);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\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=\"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:duration-150 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 </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 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\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);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\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=\"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:duration-150 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 </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 } = 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:duration-150 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 </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 } = 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:duration-150 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 </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-bg)] tpl:transition-all tpl:duration-150 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:duration-150 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-bg)] tpl:transition-all tpl:duration-150 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:duration-150 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-[11px] 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-[11px] 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 SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ImageBlock } 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 { useTimeoutFn } from \"@vueuse/core\";\n\ndefineProps<{\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 mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\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\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\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</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\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 tpl:duration-150\"\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 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 \"(optional)\"\n }}</span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\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 @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.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.image.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.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 <label\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.linkOpenInNewTab ?? false\"\n @change=\"\n updateField(\n 'linkOpenInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.image.openInNewTab }}\n </label>\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 { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ImageBlock } 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 { useTimeoutFn } from \"@vueuse/core\";\n\ndefineProps<{\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 mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\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\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\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</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\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 tpl:duration-150\"\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 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 \"(optional)\"\n }}</span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\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 @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.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.image.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.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 <label\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.linkOpenInNewTab ?? false\"\n @change=\"\n updateField(\n 'linkOpenInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.image.openInNewTab }}\n </label>\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 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:items-center tpl:gap-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <label\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-1\"\n >\n <input\n type=\"checkbox\"\n :checked=\"item[toggle.key]\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateMenuItem(\n item.id,\n toggle.key,\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ toggle.label }}\n </label>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{ t.menu.color }}</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)\"\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 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:items-center tpl:gap-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <label\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-1\"\n >\n <input\n type=\"checkbox\"\n :checked=\"item[toggle.key]\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateMenuItem(\n item.id,\n toggle.key,\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ toggle.label }}\n </label>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{ t.menu.color }}</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)\"\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 { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ColumnLayout, SectionBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\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</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=\"\n emit('update', {\n columns: ($event.target as HTMLSelectElement).value as ColumnLayout,\n })\n \"\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</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ColumnLayout, SectionBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\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</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=\"\n emit('update', {\n columns: ($event.target as HTMLSelectElement).value as ColumnLayout,\n })\n \"\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</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 {\n socialIcons,\n socialPlatformOptions,\n} from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\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 :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 {{ socialIcons[platform].name }}\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 <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.social.styleSolid },\n { value: 'outlined', label: t.social.styleOutlined },\n { value: 'rounded', label: t.social.styleRounded },\n { value: 'square', label: t.social.styleSquare },\n { value: 'circle', label: t.social.styleCircle },\n ]\"\n :model-value=\"block.iconStyle\"\n @update:model-value=\"updateField('iconStyle', $event)\"\n />\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 {\n socialIcons,\n socialPlatformOptions,\n} from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\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 :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 {{ socialIcons[platform].name }}\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 <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.social.styleSolid },\n { value: 'outlined', label: t.social.styleOutlined },\n { value: 'rounded', label: t.social.styleRounded },\n { value: 'square', label: t.social.styleSquare },\n { value: 'circle', label: t.social.styleCircle },\n ]\"\n :model-value=\"block.iconStyle\"\n @update:model-value=\"updateField('iconStyle', $event)\"\n />\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 { 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(\n { length: columnCount },\n (): TableCellData => ({\n id: generateId(),\n content: \"\",\n }),\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:duration-150 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:duration-150 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:duration-150 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:duration-150 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 <label\n class=\"tpl:mb-1.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n :checked=\"block.hasHeaderRow\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateField(\n 'hasHeaderRow',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.table.hasHeaderRow }}\n </label>\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 || 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)\"\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 {\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(\n { length: columnCount },\n (): TableCellData => ({\n id: generateId(),\n content: \"\",\n }),\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:duration-150 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:duration-150 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:duration-150 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:duration-150 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 <label\n class=\"tpl:mb-1.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n :checked=\"block.hasHeaderRow\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateField(\n 'hasHeaderRow',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.table.hasHeaderRow }}\n </label>\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 || 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)\"\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)\"\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)\"\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 ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nimport CountdownToolbar from \"./toolbar/CountdownToolbar.vue\";\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 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 { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\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 / CloudEditor.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 :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:duration-150 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:duration-150 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 <!-- Paragraph block: no text-specific sidebar controls — all formatting is in the TipTap toolbar -->\n <template v-else-if=\"blockType === 'paragraph'\" />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\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 ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nimport CountdownToolbar from \"./toolbar/CountdownToolbar.vue\";\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 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 { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\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 / CloudEditor.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 :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:duration-150 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:duration-150 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 <!-- Paragraph block: no text-specific sidebar controls — all formatting is in the TipTap toolbar -->\n <template v-else-if=\"blockType === 'paragraph'\" />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\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 TemplateSettingsPanel from \"./TemplateSettings.vue\";\nimport Toolbar from \"./Toolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { Block, TemplateSettings } from \"@templatical/types\";\nimport { LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n selectedBlock: Block | null;\n settings: TemplateSettings;\n shiftedLeft?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update-block\", updates: Partial<Block>): void;\n (e: \"delete-block\"): void;\n (e: \"duplicate-block\"): void;\n (e: \"update-settings\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst { t } = useI18n();\n\ntype Tab = \"content\" | \"settings\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst pillOffset = computed(() =>\n activeTab.value === \"content\" ? \"tpl:translate-x-0\" : \"tpl:translate-x-full\",\n);\n\nwatch(\n () => props.selectedBlock,\n (newBlock) => {\n if (newBlock) {\n activeTab.value = \"content\";\n }\n },\n);\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.rightSidebar\"\n class=\"tpl-right-sidebar tpl:absolute tpl:top-14 tpl:bottom-0 tpl:z-40 tpl:flex tpl:w-[320px] tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)] tpl:transition-all tpl:duration-200 tpl:border-l tpl:border-[var(--tpl-border)]\"\n :class=\"shiftedLeft ? 'tpl:right-[360px]' : 'tpl:right-0'\"\n >\n <div\n role=\"tablist\"\n class=\"tpl:relative tpl:flex tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <div\n class=\"tpl:absolute tpl:bottom-0 tpl:left-0 tpl:h-full tpl:w-1/2 tpl:p-1.5 tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"pillOffset\"\n >\n <div\n class=\"tpl:h-full tpl:w-full tpl:rounded-[var(--tpl-radius-sm)] tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow)]\"\n ></div>\n </div>\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:px-4 tpl:py-3 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-[120ms]\"\n :class=\"\n activeTab === 'content'\n ? 'tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n {{ t.sidebar.content }}\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:px-4 tpl:py-3 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-[120ms]\"\n :class=\"\n activeTab === 'settings'\n ? 'tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.sidebar.settings }}\n </button>\n </div>\n\n <div\n v-if=\"activeTab === 'content'\"\n id=\"tpl-tabpanel-content\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-content\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <Toolbar\n v-if=\"selectedBlock\"\n :block=\"selectedBlock\"\n @update=\"emit('update-block', $event)\"\n @delete=\"emit('delete-block')\"\n @duplicate=\"emit('duplicate-block')\"\n />\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:px-6 tpl:py-10 tpl:text-center tpl:text-[var(--tpl-text-muted)]\"\n >\n <div class=\"tpl:mb-4 tpl:text-[var(--tpl-text-dim)]\">\n <LayoutTemplate :size=\"40\" :stroke-width=\"1.5\" />\n </div>\n <h3\n class=\"tpl:m-0 tpl:mb-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.sidebar.noSelection }}\n </h3>\n <p class=\"tpl:m-0 tpl:text-sm tpl:leading-normal\">\n {{ t.sidebar.noSelectionHint }}\n </p>\n </div>\n </div>\n\n <div\n v-if=\"activeTab === 'settings'\"\n id=\"tpl-tabpanel-settings\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-settings\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <TemplateSettingsPanel\n :settings=\"settings\"\n @update=\"emit('update-settings', $event)\"\n />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport TemplateSettingsPanel from \"./TemplateSettings.vue\";\nimport Toolbar from \"./Toolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { Block, TemplateSettings } from \"@templatical/types\";\nimport { LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n selectedBlock: Block | null;\n settings: TemplateSettings;\n shiftedLeft?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update-block\", updates: Partial<Block>): void;\n (e: \"delete-block\"): void;\n (e: \"duplicate-block\"): void;\n (e: \"update-settings\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst { t } = useI18n();\n\ntype Tab = \"content\" | \"settings\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst pillOffset = computed(() =>\n activeTab.value === \"content\" ? \"tpl:translate-x-0\" : \"tpl:translate-x-full\",\n);\n\nwatch(\n () => props.selectedBlock,\n (newBlock) => {\n if (newBlock) {\n activeTab.value = \"content\";\n }\n },\n);\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.rightSidebar\"\n class=\"tpl-right-sidebar tpl:absolute tpl:top-14 tpl:bottom-0 tpl:z-40 tpl:flex tpl:w-[320px] tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)] tpl:transition-all tpl:duration-200 tpl:border-l tpl:border-[var(--tpl-border)]\"\n :class=\"shiftedLeft ? 'tpl:right-[360px]' : 'tpl:right-0'\"\n >\n <div\n role=\"tablist\"\n class=\"tpl:relative tpl:flex tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <div\n class=\"tpl:absolute tpl:bottom-0 tpl:left-0 tpl:h-full tpl:w-1/2 tpl:p-1.5 tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"pillOffset\"\n >\n <div\n class=\"tpl:h-full tpl:w-full tpl:rounded-[var(--tpl-radius-sm)] tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow)]\"\n ></div>\n </div>\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:px-4 tpl:py-3 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-[120ms]\"\n :class=\"\n activeTab === 'content'\n ? 'tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n {{ t.sidebar.content }}\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:px-4 tpl:py-3 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-[120ms]\"\n :class=\"\n activeTab === 'settings'\n ? 'tpl:text-[var(--tpl-primary)]'\n : 'tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.sidebar.settings }}\n </button>\n </div>\n\n <div\n v-if=\"activeTab === 'content'\"\n id=\"tpl-tabpanel-content\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-content\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <Toolbar\n v-if=\"selectedBlock\"\n :block=\"selectedBlock\"\n @update=\"emit('update-block', $event)\"\n @delete=\"emit('delete-block')\"\n @duplicate=\"emit('duplicate-block')\"\n />\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:px-6 tpl:py-10 tpl:text-center tpl:text-[var(--tpl-text-muted)]\"\n >\n <div class=\"tpl:mb-4 tpl:text-[var(--tpl-text-dim)]\">\n <LayoutTemplate :size=\"40\" :stroke-width=\"1.5\" />\n </div>\n <h3\n class=\"tpl:m-0 tpl:mb-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.sidebar.noSelection }}\n </h3>\n <p class=\"tpl:m-0 tpl:text-sm tpl:leading-normal\">\n {{ t.sidebar.noSelectionHint }}\n </p>\n </div>\n </div>\n\n <div\n v-if=\"activeTab === 'settings'\"\n id=\"tpl-tabpanel-settings\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-settings\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <TemplateSettingsPanel\n :settings=\"settings\"\n @update=\"emit('update-settings', $event)\"\n />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"tablet\" as ViewportSize, label: t.viewport.tablet },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Tablet\n v-else-if=\"vp.value === 'tablet'\"\n :size=\"18\"\n :stroke-width=\"1.5\"\n />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"tablet\" as ViewportSize, label: t.viewport.tablet },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Tablet\n v-else-if=\"vp.value === 'tablet'\"\n :size=\"18\"\n :stroke-width=\"1.5\"\n />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Eye, EyeOff } from \"@lucide/vue\";\n\ndefineProps<{\n previewMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", previewMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-preview-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: previewMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: previewMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :title=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :aria-pressed=\"previewMode\"\n @click=\"emit('change', !previewMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Eye v-if=\"previewMode\" key=\"eye\" :size=\"18\" :stroke-width=\"1.5\" />\n <EyeOff v-else key=\"eye-off\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-preview-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-preview-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Eye, EyeOff } from \"@lucide/vue\";\n\ndefineProps<{\n previewMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", previewMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-preview-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: previewMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: previewMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :title=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :aria-pressed=\"previewMode\"\n @click=\"emit('change', !previewMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Eye v-if=\"previewMode\" key=\"eye\" :size=\"18\" :stroke-width=\"1.5\" />\n <EyeOff v-else key=\"eye-off\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-preview-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-preview-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<!--\n Canvas Dark Mode Preview Toggle\n\n This toggle simulates how the email template will look when the recipient's\n email client uses dark mode (e.g. Gmail, Outlook, Apple Mail in dark theme).\n It applies a CSS filter (invert + hue-rotate) to the canvas area only.\n\n This is NOT the editor UI theme toggle. The editor UI theme (light/dark/auto)\n is controlled externally via the `uiTheme` config option or `editor.setTheme()`.\n-->\n<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Moon, Sun } from \"@lucide/vue\";\n\ndefineProps<{\n darkMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", darkMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-dark-mode-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: darkMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: darkMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :title=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :aria-pressed=\"darkMode\"\n @click=\"emit('change', !darkMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Moon v-if=\"darkMode\" key=\"moon\" :size=\"18\" :stroke-width=\"1.5\" />\n <Sun v-else key=\"sun\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-dark-mode-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-dark-mode-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<!--\n Canvas Dark Mode Preview Toggle\n\n This toggle simulates how the email template will look when the recipient's\n email client uses dark mode (e.g. Gmail, Outlook, Apple Mail in dark theme).\n It applies a CSS filter (invert + hue-rotate) to the canvas area only.\n\n This is NOT the editor UI theme toggle. The editor UI theme (light/dark/auto)\n is controlled externally via the `uiTheme` config option or `editor.setTheme()`.\n-->\n<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Moon, Sun } from \"@lucide/vue\";\n\ndefineProps<{\n darkMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", darkMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-dark-mode-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: darkMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: darkMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :title=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :aria-pressed=\"darkMode\"\n @click=\"emit('change', !darkMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Moon v-if=\"darkMode\" key=\"moon\" :size=\"18\" :stroke-width=\"1.5\" />\n <Sun v-else key=\"sun\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-dark-mode-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-dark-mode-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n /** Positioning classes for the footer (left/right offsets). */\n positionClass?: string | string[];\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <footer\n class=\"tpl:pointer-events-none tpl:absolute tpl:bottom-0 tpl:z-50 tpl:flex tpl:h-8 tpl:items-center tpl:justify-end tpl:pr-4 tpl:text-[9px] tpl:opacity-90 tpl:transition-all tpl:duration-300 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"positionClass\"\n >\n <div\n class=\"tpl:pointer-events-auto tpl:flex tpl:items-center tpl:gap-1.5 tpl:rounded-tl-lg tpl:p-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-canvas-bg) 85%,\n transparent\n );\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n >\n <span>{{ t.footer.poweredBy }}</span>\n <a\n href=\"https://templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:inline-flex tpl:items-center tpl:gap-1 tpl:font-medium tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-muted)]\"\n style=\"text-decoration: none\"\n >\n <img\n width=\"14\"\n height=\"14\"\n src=\"https://templatical.com/logo.svg\"\n alt=\"\"\n />\n Templatical\n </a>\n <span class=\"tpl:text-[var(--tpl-border)]\">·</span>\n <a\n href=\"https://github.com/templatical/sdk\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-dim)]\"\n style=\"text-decoration: none\"\n >\n {{ t.footer.openSource }}\n </a>\n </div>\n </footer>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n /** Positioning classes for the footer (left/right offsets). */\n positionClass?: string | string[];\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <footer\n class=\"tpl:pointer-events-none tpl:absolute tpl:bottom-0 tpl:z-50 tpl:flex tpl:h-8 tpl:items-center tpl:justify-end tpl:pr-4 tpl:text-[9px] tpl:opacity-90 tpl:transition-all tpl:duration-300 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"positionClass\"\n >\n <div\n class=\"tpl:pointer-events-auto tpl:flex tpl:items-center tpl:gap-1.5 tpl:rounded-tl-lg tpl:p-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-canvas-bg) 85%,\n transparent\n );\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n >\n <span>{{ t.footer.poweredBy }}</span>\n <a\n href=\"https://templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:inline-flex tpl:items-center tpl:gap-1 tpl:font-medium tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-muted)]\"\n style=\"text-decoration: none\"\n >\n <img\n width=\"14\"\n height=\"14\"\n src=\"https://templatical.com/logo.svg\"\n alt=\"\"\n />\n Templatical\n </a>\n <span class=\"tpl:text-[var(--tpl-border)]\">·</span>\n <a\n href=\"https://github.com/templatical/sdk\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-dim)]\"\n style=\"text-decoration: none\"\n >\n {{ t.footer.openSource }}\n </a>\n </div>\n </footer>\n</template>\n"],"x_google_ignoreList":[4,5,6,7,8,9,10,11,12,13,14,15,16],"mappings":"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"}