@aswin.dev/editor 0.7.0 → 0.7.2

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 (182) hide show
  1. package/LICENSE +56 -0
  2. package/README.md +3 -3
  3. package/dist/{AccessibilityPanel-CvQGLdu6.js → AccessibilityPanel-EmQ19wiS.js} +37 -37
  4. package/dist/{AiChatSidebar-B3SJIKG_.js → AiChatSidebar-DeB0w_32.js} +67 -81
  5. package/dist/{AiFeatureMenu-BLLKoOos.js → AiFeatureMenu-DoLD5Cde.js} +22 -22
  6. package/dist/{BlockA11yBadge-CXDLqkcJ.js → BlockA11yBadge-DkNbDvJA.js} +12 -12
  7. package/dist/CloudEditor-zo9PjjvY.js +1214 -0
  8. package/dist/{CollaboratorBar-DuPYW5iF.js → CollaboratorBar-BsXMY-8e.js} +21 -21
  9. package/dist/CommentsSidebar-DIWIa4rS.js +436 -0
  10. package/dist/CountdownBlock-BCi7-DAM.js +92 -0
  11. package/dist/CountdownToolbar-BuS8p5ju.js +210 -0
  12. package/dist/{DesignReferenceSidebar-B8V_F2yF.js → DesignReferenceSidebar-RDlnhL-6.js} +58 -58
  13. package/dist/{LoadingTrack-B0CWFHXQ.js → LoadingTrack-BJ59h9ok.js} +2 -2
  14. package/dist/ModuleBrowserModal-b7HbpByz.js +206 -0
  15. package/dist/ModulePreviewCanvas-D__YlOLS.js +108 -0
  16. package/dist/{NumberWithSuffix-DkXUez9t.js → NumberWithSuffix-Ca3vNY84.js} +87 -87
  17. package/dist/{ParagraphEditor-D75wl3BX.js → ParagraphEditor-B1aYPO-6.js} +182 -182
  18. package/dist/{RichTextEditorContent-DYkIauIk.js → RichTextEditorContent-BmEVMrkJ.js} +38 -38
  19. package/dist/{SaveModuleDialog-FZ9lxY7_.js → SaveModuleDialog-ClovqI6h.js} +28 -28
  20. package/dist/{SnapshotHistory-BR3eV120.js → SnapshotHistory-DoqtH0cw.js} +45 -50
  21. package/dist/{TemplateScoringPanel-4GTNHej5.js → TemplateScoringPanel-DQv2ZAiL.js} +75 -75
  22. package/dist/{TestEmailModal--ue5w9fT.js → TestEmailModal-iIeYJYsj.js} +28 -28
  23. package/dist/{TitleEditor-fStSADI-.js → TitleEditor-CTDIwRF_.js} +68 -68
  24. package/dist/{TplModal-BwSfxIHf.js → TplModal-CBq1J1pG.js} +14 -14
  25. package/dist/{accessibility-e8JYu_zd.js → accessibility-BU09xZrQ.js} +1 -1
  26. package/dist/{blockTypeIcons-BcTrDjmH.js → blockTypeIcons-QkC6f5UE.js} +19 -5
  27. package/dist/bundle-stats.json +7 -7
  28. package/dist/cdn/chunks/{AccessibilityPanel-B6DOjojm.js → AccessibilityPanel-BeU8nz7A.js} +28 -28
  29. package/dist/cdn/chunks/{AccessibilityPanel-B6DOjojm.js.map → AccessibilityPanel-BeU8nz7A.js.map} +1 -1
  30. package/dist/cdn/chunks/{AiFeatureMenu-qEdB2fZJ.js → AiFeatureMenu-B2mhscyP.js} +21 -21
  31. package/dist/cdn/chunks/AiFeatureMenu-B2mhscyP.js.map +1 -0
  32. package/dist/cdn/chunks/{BlockA11yBadge-DcEZftf6.js → BlockA11yBadge-C4g77gF0.js} +11 -11
  33. package/dist/cdn/chunks/{BlockA11yBadge-DcEZftf6.js.map → BlockA11yBadge-C4g77gF0.js.map} +1 -1
  34. package/dist/{CloudEditor-BCz1ZTYC.js → cdn/chunks/CloudEditor-Btyr0b0_.js} +486 -475
  35. package/dist/cdn/chunks/CloudEditor-Btyr0b0_.js.map +1 -0
  36. package/dist/cdn/chunks/{CollaboratorBar--nO7TX6b.js → CollaboratorBar-YBiIjiRh.js} +15 -15
  37. package/dist/cdn/chunks/CollaboratorBar-YBiIjiRh.js.map +1 -0
  38. package/dist/cdn/chunks/CountdownBlock-B06UZoWe.js +93 -0
  39. package/dist/cdn/chunks/CountdownBlock-B06UZoWe.js.map +1 -0
  40. package/dist/cdn/chunks/CountdownToolbar-C9XZr33O.js +212 -0
  41. package/dist/cdn/chunks/CountdownToolbar-C9XZr33O.js.map +1 -0
  42. package/dist/{ModuleBrowserModal-DrUFMTDx.js → cdn/chunks/ModuleBrowserModal-C2CDWKW6.js} +51 -62
  43. package/dist/cdn/chunks/ModuleBrowserModal-C2CDWKW6.js.map +1 -0
  44. package/dist/cdn/chunks/ModulePreviewCanvas-Drt8Evai.js +109 -0
  45. package/dist/cdn/chunks/ModulePreviewCanvas-Drt8Evai.js.map +1 -0
  46. package/dist/cdn/chunks/{NumberWithSuffix-CE3NrZhH.js → NumberWithSuffix-Ty1bp9vB.js} +64 -64
  47. package/dist/cdn/chunks/{NumberWithSuffix-CE3NrZhH.js.map → NumberWithSuffix-Ty1bp9vB.js.map} +1 -1
  48. package/dist/cdn/chunks/{ParagraphEditor-B6Ygu-Mq.js → ParagraphEditor-BA1WbHI7.js} +188 -188
  49. package/dist/cdn/chunks/{ParagraphEditor-B6Ygu-Mq.js.map → ParagraphEditor-BA1WbHI7.js.map} +1 -1
  50. package/dist/cdn/chunks/{RichTextEditorContent-DL_y2SrR.js → RichTextEditorContent-BtWCA_Oc.js} +30 -30
  51. package/dist/cdn/chunks/{RichTextEditorContent-DL_y2SrR.js.map → RichTextEditorContent-BtWCA_Oc.js.map} +1 -1
  52. package/dist/cdn/chunks/{SaveModuleDialog-B0TnO_o9.js → SaveModuleDialog-AwL0tkCV.js} +21 -21
  53. package/dist/cdn/chunks/SaveModuleDialog-AwL0tkCV.js.map +1 -0
  54. package/dist/cdn/chunks/{TitleEditor-BHpfxvwy.js → TitleEditor-DbSyeixS.js} +65 -65
  55. package/dist/cdn/chunks/TitleEditor-DbSyeixS.js.map +1 -0
  56. package/dist/cdn/chunks/blockTypeIcons-pQIkxJzc.js +23 -0
  57. package/dist/cdn/chunks/blockTypeIcons-pQIkxJzc.js.map +1 -0
  58. package/dist/cdn/chunks/{de-D8CnZxV9.js → de-_tooy3Q8.js} +250 -2
  59. package/dist/cdn/chunks/de-_tooy3Q8.js.map +1 -0
  60. package/dist/cdn/chunks/{draggable-Bcb86AsV.js → draggable-C-1_gch3.js} +2 -2
  61. package/dist/cdn/chunks/{draggable-Bcb86AsV.js.map → draggable-C-1_gch3.js.map} +1 -1
  62. package/dist/cdn/chunks/{en-8FHaQv4V.js → en-CNqLoIm9.js} +250 -2
  63. package/dist/cdn/chunks/en-CNqLoIm9.js.map +1 -0
  64. package/dist/cdn/chunks/{extensions-DIxF31tA.js → extensions-BVKQw_sp.js} +107 -107
  65. package/dist/cdn/chunks/extensions-BVKQw_sp.js.map +1 -0
  66. package/dist/cdn/chunks/{features-DEMb13KS.js → features-DIBEo4xl.js} +3388 -2289
  67. package/dist/cdn/chunks/features-DIBEo4xl.js.map +1 -0
  68. package/dist/cdn/chunks/{icons-CsLTcirh.js → icons-C1Gg-ov-.js} +346 -54
  69. package/dist/cdn/chunks/icons-C1Gg-ov-.js.map +1 -0
  70. package/dist/cdn/chunks/{media-library-CVaNvhpM.js → media-library-BTF_Ko70.js} +985 -985
  71. package/dist/cdn/chunks/media-library-BTF_Ko70.js.map +1 -0
  72. package/dist/cdn/chunks/{quality-BaBfc54_.js → quality-C5AmotmP.js} +312 -312
  73. package/dist/cdn/chunks/quality-C5AmotmP.js.map +1 -0
  74. package/dist/cdn/chunks/{renderer-CUxvx7ro.js → renderer-D0L44Vlp.js} +236 -142
  75. package/dist/cdn/chunks/renderer-D0L44Vlp.js.map +1 -0
  76. package/dist/cdn/chunks/{src-CRaqN-p8.js → src-RKexlYjA.js} +161 -161
  77. package/dist/cdn/chunks/src-RKexlYjA.js.map +1 -0
  78. package/dist/cdn/chunks/styleConstants-PgmvNBzQ.js +57 -0
  79. package/dist/cdn/chunks/styleConstants-PgmvNBzQ.js.map +1 -0
  80. package/dist/cdn/chunks/styles-CQR6ed13.js +4976 -0
  81. package/dist/cdn/chunks/styles-CQR6ed13.js.map +1 -0
  82. package/dist/cdn/chunks/{tiptap-ZhwKyFp7.js → tiptap-CDzAbF2j.js} +43 -43
  83. package/dist/cdn/chunks/{tiptap-ZhwKyFp7.js.map → tiptap-CDzAbF2j.js.map} +1 -1
  84. package/dist/cdn/editor.css +1 -1
  85. package/dist/cdn/editor.js +211 -123
  86. package/dist/cdn/editor.js.map +1 -1
  87. package/dist/{check-Da05j8yl.js → check-DJrpDKO_.js} +1 -1
  88. package/dist/{chevron-down-R2uY34iD.js → chevron-down-C5oBUhT8.js} +1 -1
  89. package/dist/chevron-right-BqCptpdp.js +10 -0
  90. package/dist/{circle-alert-DZuGWPX-.js → circle-alert-ZQQc98HC.js} +1 -1
  91. package/dist/{clock-CRp2sIub.js → clock-ik2pRJKG.js} +1 -1
  92. package/dist/{cloud-DEk_b4CR.js → cloud-DJG4tb4_.js} +485 -427
  93. package/dist/{createLucideIcon-C3pa2siy.js → createLucideIcon-ClREiSx3.js} +6 -6
  94. package/dist/{de-Brqvgr43.js → de-2LEvILeZ.js} +249 -1
  95. package/dist/{dist-Cp0zXPAD.js → dist-BHAeXaUY.js} +1 -1
  96. package/dist/{dist-BaQIYPsn.js → dist-BSJvAvH3.js} +1 -1
  97. package/dist/{dist-wzMIGj-D.js → dist-BeFnymxK.js} +1 -1
  98. package/dist/dist-Bmir0gYb.js +1167 -0
  99. package/dist/{dist-DDJIWTRY.js → dist-BvPgo-UK.js} +1 -1
  100. package/dist/{dist-BFawx6IS.js → dist-CPVBKMmd.js} +51 -51
  101. package/dist/dist-CivF9P8b.js +382 -0
  102. package/dist/{dist-D6uC2xhi.js → dist-Df4ie7vZ.js} +1 -1
  103. package/dist/{dist-us-RpCWN.js → dist-DxOoemkW.js} +1 -1
  104. package/dist/{dist-KoBJjK1G.js → dist-HEQ52gTJ.js} +1 -1
  105. package/dist/{dist-DjviJBCi.js → dist-b-XUqAoF.js} +1 -1
  106. package/dist/{dist-D90y8dvT.js → dist-kZfaVUpW.js} +3 -3
  107. package/dist/{dist-aRzjfSRN.js → dist-wfAedlzi.js} +1 -1
  108. package/dist/{en-WDVp87TE.js → en-D2RU2Poj.js} +249 -1
  109. package/dist/{extensions-CUcl9Ok4.js → extensions-BQ1xXx3d.js} +103 -103
  110. package/dist/{image-up-MBZKKg9p.js → image-up-DT7gcJLN.js} +1 -1
  111. package/dist/index.d.ts +102 -14
  112. package/dist/{info-CJEC7piy.js → info-BSPGcsSM.js} +1 -1
  113. package/dist/keys-CvX8D-8C.js +10 -0
  114. package/dist/{loader-circle-DsY5Yg33.js → loader-circle-Balo8p3d.js} +1 -1
  115. package/dist/{message-circle-yElBbR2C.js → message-circle-B39qAHxs.js} +1 -1
  116. package/dist/pencil-BZJPNYWR.js +10 -0
  117. package/dist/{refresh-cw-CE_AGtn8.js → refresh-cw-DwDqGUM0.js} +1 -1
  118. package/dist/{scan-line-D0vcUekt.js → scan-line-DlghmhNf.js} +1 -1
  119. package/dist/{send-DH4oDQqC.js → send-DDdhIRj8.js} +1 -1
  120. package/dist/{shield-check-CfJgs2Hd.js → shield-check-OSQ-JVTX.js} +1 -1
  121. package/dist/{sparkles-CvRXGqFs.js → sparkles-BN4a-CoF.js} +1 -1
  122. package/dist/style.css +1 -1
  123. package/dist/styleConstants-wWRbcuEK.js +55 -0
  124. package/dist/styles-CavWjvol.js +5487 -0
  125. package/dist/templatical-editor.js +211 -119
  126. package/dist/{text-align-start-BT9VUDxK.js → text-align-start-D1weisjS.js} +1 -1
  127. package/dist/{trash-2-DbP2Y6t2.js → trash-2-CMWvQ5KX.js} +1 -1
  128. package/dist/{triangle-alert-aOXceTSe.js → triangle-alert-DyidRNX_.js} +1 -1
  129. package/dist/undo-2-Cg8I7obC.js +16 -0
  130. package/dist/{useCloudI18n-BuIwR6OE.js → useCloudI18n-BTTNBk5i.js} +2 -2
  131. package/dist/{useEditorCore-C6ost42Q.js → useEditorCore-BGnzcT7p.js} +2424 -2249
  132. package/dist/{useI18n-lb2DHDiu.js → useI18n-C2xQZ6K9.js} +2 -2
  133. package/dist/{useMergeTag-CBwKnnNB.js → useMergeTag-CfuZq2fF.js} +4 -4
  134. package/dist/{vue.runtime.esm-bundler-DpvJL-nX.js → vue.runtime.esm-bundler-CjauPXjj.js} +1 -1
  135. package/dist/{x-u2oVmjN_.js → x-CgIhNcT9.js} +1 -1
  136. package/package.json +15 -15
  137. package/dist/CommentsSidebar-B1pvJdqF.js +0 -441
  138. package/dist/CountdownBlock-BNSj1jvJ.js +0 -92
  139. package/dist/CountdownToolbar-ClJr2GzL.js +0 -210
  140. package/dist/ModulePreviewCanvas-CHdOwV_4.js +0 -106
  141. package/dist/cdn/chunks/AiFeatureMenu-qEdB2fZJ.js.map +0 -1
  142. package/dist/cdn/chunks/CloudEditor-D2GsEC_n.js +0 -1143
  143. package/dist/cdn/chunks/CloudEditor-D2GsEC_n.js.map +0 -1
  144. package/dist/cdn/chunks/CollaboratorBar--nO7TX6b.js.map +0 -1
  145. package/dist/cdn/chunks/CountdownBlock-5YdT1uUu.js +0 -93
  146. package/dist/cdn/chunks/CountdownBlock-5YdT1uUu.js.map +0 -1
  147. package/dist/cdn/chunks/CountdownToolbar-DXPXrbAA.js +0 -212
  148. package/dist/cdn/chunks/CountdownToolbar-DXPXrbAA.js.map +0 -1
  149. package/dist/cdn/chunks/ModuleBrowserModal-DxoPp81s.js +0 -195
  150. package/dist/cdn/chunks/ModuleBrowserModal-DxoPp81s.js.map +0 -1
  151. package/dist/cdn/chunks/ModulePreviewCanvas-CoLdb4ar.js +0 -107
  152. package/dist/cdn/chunks/ModulePreviewCanvas-CoLdb4ar.js.map +0 -1
  153. package/dist/cdn/chunks/SaveModuleDialog-B0TnO_o9.js.map +0 -1
  154. package/dist/cdn/chunks/TitleEditor-BHpfxvwy.js.map +0 -1
  155. package/dist/cdn/chunks/blockTypeIcons-BzzY9_kA.js +0 -22
  156. package/dist/cdn/chunks/blockTypeIcons-BzzY9_kA.js.map +0 -1
  157. package/dist/cdn/chunks/de-D8CnZxV9.js.map +0 -1
  158. package/dist/cdn/chunks/en-8FHaQv4V.js.map +0 -1
  159. package/dist/cdn/chunks/extensions-DIxF31tA.js.map +0 -1
  160. package/dist/cdn/chunks/features-DEMb13KS.js.map +0 -1
  161. package/dist/cdn/chunks/icons-CsLTcirh.js.map +0 -1
  162. package/dist/cdn/chunks/media-library-CVaNvhpM.js.map +0 -1
  163. package/dist/cdn/chunks/quality-BaBfc54_.js.map +0 -1
  164. package/dist/cdn/chunks/renderer-CUxvx7ro.js.map +0 -1
  165. package/dist/cdn/chunks/src-CRaqN-p8.js.map +0 -1
  166. package/dist/cdn/chunks/styleConstants-DP1VOca8.js +0 -57
  167. package/dist/cdn/chunks/styleConstants-DP1VOca8.js.map +0 -1
  168. package/dist/cdn/chunks/styles-BHJULjNR.js +0 -2947
  169. package/dist/cdn/chunks/styles-BHJULjNR.js.map +0 -1
  170. package/dist/dist-B1IR0bpH.js +0 -326
  171. package/dist/dist-DJmnUmW9.js +0 -362
  172. package/dist/keys-ciNfSSGj.js +0 -10
  173. package/dist/styleConstants-fWzlIIwN.js +0 -55
  174. package/dist/styles-DEXEkBvg.js +0 -3176
  175. /package/dist/{_plugin-vue_export-helper-B0hnzhyu.js → _plugin-vue_export-helper-Bwh4ceeO.js} +0 -0
  176. /package/dist/{de-DCaaCE5s.js → de-D2npYjrZ.js} +0 -0
  177. /package/dist/{dist-iLBdeBDR.js → dist-DvHLtWVP.js} +0 -0
  178. /package/dist/{emojiData-PQyVa4bU.js → emojiData-DvQBBzmO.js} +0 -0
  179. /package/dist/{en-DXCyK4-X.js → en-ib-4h3_o.js} +0 -0
  180. /package/dist/{formatRelativeTime-BOEf47hq.js → formatRelativeTime-CFDZnEIs.js} +0 -0
  181. /package/dist/{liquid.browser-CdMv1BTn.js → liquid.browser-7Rv0QDiO.js} +0 -0
  182. /package/dist/{readableTextColor-CY3SiRnt.js → readableTextColor-C_9OpzBw.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles-CQR6ed13.js","names":["$emit","$emit"],"sources":["../../../src/utils/resolveAccessibilityOptions.ts","../../../src/utils/resolvePopupEmbed.ts","../../../src/components/Canvas.vue","../../../src/components/Canvas.vue","../../../src/components/PopupDesignSwitch.vue","../../../src/components/PopupDesignSwitch.vue","../../../src/components/PopupOverlayImagePanel.vue","../../../src/components/PopupOverlayImagePanel.vue","../../../src/components/PopupDesignView.vue","../../../src/components/PopupDesignView.vue","../../../src/composables/useBlockPalette.ts","../../../src/components/SidebarEmailRail.vue","../../../src/components/SidebarEmailRail.vue","../../../src/components/PopupLeftChrome.vue","../../../src/components/PopupLeftChrome.vue","../../../src/components/Sidebar.vue","../../../src/components/Sidebar.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/SpacingControl.vue","../../../src/components/SpacingControl.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/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/InputToolbar.vue","../../../src/components/toolbar/InputToolbar.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/VideoToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/RightSidebar.vue","../../../src/components/RightSidebar.vue","../../../src/components/HistoryUndoRedo.vue","../../../src/components/HistoryUndoRedo.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","../../../src/types/editor-tour.ts","../../../src/utils/tourStorage.ts","../../../src/components/EditorTour.vue","../../../src/components/EditorTour.vue","../../../src/components/PopupTriggerConditionDetails.vue","../../../src/components/PopupTriggerConditionDetails.vue","../../../src/components/PopupDisplayRulesView.vue","../../../src/components/PopupDisplayRulesView.vue","../../../src/utils/syncEditorThemeToPortal.ts","../../../src/components/PopupScheduleDatePicker.vue","../../../src/components/PopupScheduleDatePicker.vue","../../../src/components/PopupScheduleView.vue","../../../src/components/PopupScheduleView.vue"],"sourcesContent":["import type { A11yOptions } from \"@aswin.dev/quality\";\n\n/**\n * Build the `A11yOptions` object the editor passes to the lint composable.\n *\n * In editor / cloud-editor mode the linter locale is forced to match the\n * editor UI locale — `accessibility.locale` set by the consumer is\n * ignored. Headless callers (`lintAccessibility(...)` directly) keep\n * full control.\n */\nexport function resolveAccessibilityOptions(config: {\n locale?: string;\n accessibility?: A11yOptions;\n}): A11yOptions {\n return {\n ...config.accessibility,\n locale: config.locale,\n };\n}\n","import type { PopupEmbedSettings, TemplateSettings } from \"@aswin.dev/types\";\nimport {\n createDefaultPopupEmbedSettings,\n normalizePopupTriggersSettings,\n} from \"@aswin.dev/types\";\n\n/** Deep-merge saved `settings.popup` onto defaults so UI always has full shape. */\nexport function resolvePopupEmbed(\n settings: TemplateSettings,\n): PopupEmbedSettings {\n const base = createDefaultPopupEmbedSettings();\n const p = settings.popup;\n if (!p) return base;\n\n const triggers = normalizePopupTriggersSettings(base.triggers, p.triggers);\n\n return {\n triggers,\n design: {\n ...base.design,\n ...(p.design ?? {}),\n contentPadding: {\n ...base.design.contentPadding,\n ...(p.design?.contentPadding ?? {}),\n },\n },\n schedule: {\n ...base.schedule,\n ...(p.schedule ?? {}),\n slots:\n p.schedule?.slots !== undefined\n ? p.schedule.slots\n : base.schedule.slots,\n },\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@aswin.dev/types\";\nimport { normalizeTemplateContentPages } from \"@aswin.dev/types\";\nimport {\n ChevronRight,\n ImageUp,\n MoreVertical,\n Sparkles,\n SquarePlus,\n} from \"@lucide/vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { computed, inject, onMounted, ref, 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\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\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 InputBlock from \"./blocks/InputBlock.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 input: InputBlock,\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 /** Multi-step canvas (e.g. popup flows); enable via editor config `multiPageCanvas`. */\n multiPageCanvas?: boolean;\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();\nconst { t: cloudT } = useCloudI18n();\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 const c = props.content;\n const activeId = c.activeCanvasPageId;\n if (c.canvasPages?.length && activeId) {\n editor.setContent(\n normalizeTemplateContentPages({\n ...c,\n canvasPages: c.canvasPages.map((p) =>\n p.id === activeId ? { ...p, blocks: value } : p,\n ),\n blocks: value,\n activeCanvasPageId: activeId,\n }),\n );\n return;\n }\n editor.setContent({\n ...c,\n blocks: value,\n });\n },\n});\n\nconst canvasPagesStrip = computed(() => props.content.canvasPages ?? []);\n\nconst activeCanvasPageId = computed(\n () => props.content.activeCanvasPageId ?? null,\n);\n\nconst stepStripRef = ref<HTMLElement | null>(null);\nconst openStepMenuPageId = ref<string | null>(null);\n\nonClickOutside(stepStripRef, () => {\n openStepMenuPageId.value = null;\n});\n\nonMounted(() => {\n if (props.multiPageCanvas) {\n editor.ensureCanvasPages();\n }\n});\n\nfunction selectCanvasPage(pageId: string): void {\n if (pageId === activeCanvasPageId.value) {\n return;\n }\n openStepMenuPageId.value = null;\n editor.switchCanvasPage(pageId);\n}\n\nfunction toggleStepMenu(pageId: string): void {\n openStepMenuPageId.value =\n openStepMenuPageId.value === pageId ? null : pageId;\n}\n\nfunction handleAddCanvasPage(): void {\n openStepMenuPageId.value = null;\n editor.addCanvasPage();\n}\n\nfunction handleRenameCanvasPage(pageId: string, currentTitle: string): void {\n const next = window.prompt(t.canvas.pages.renamePrompt, currentTitle);\n if (next === null) {\n return;\n }\n editor.renameCanvasPage(pageId, next);\n openStepMenuPageId.value = null;\n}\n\nfunction handleRemoveCanvasPage(pageId: string): void {\n if (canvasPagesStrip.value.length <= 1) {\n return;\n }\n editor.removeCanvasPage(pageId);\n openStepMenuPageId.value = null;\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/** Step strip lives in a nav bar above the white canvas card (not inside it). */\nconst showStepNav = computed(\n () => props.multiPageCanvas === true && canvasPagesStrip.value.length > 0,\n);\n\nconst canvasCardStyle = computed(() => ({\n width: showStepNav.value ? \"100%\" : `${viewportWidth.value}px`,\n backgroundColor: props.content.settings.backgroundColor,\n boxShadow: props.darkMode ? \"none\" : \"var(--tpl-shadow-xl)\",\n filter: props.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\nconst canvasRootStyle = computed(() =>\n showStepNav.value ? { width: `${viewportWidth.value}px` } : undefined,\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\nconst popupCanvasPaddingStyle = computed(() => {\n if (!props.content.settings.popup) {\n return {};\n }\n const pad = resolvePopupEmbed(props.content.settings).design.contentPadding;\n return {\n paddingTop: `${pad.top}px`,\n paddingRight: `${pad.right}px`,\n paddingBottom: `${pad.bottom}px`,\n paddingLeft: `${pad.left}px`,\n };\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 class=\"tpl-canvas-root tpl:box-border tpl:max-w-full\"\n :class=\"showStepNav ? 'tpl:flex tpl:flex-col tpl:gap-3' : ''\"\n :style=\"canvasRootStyle\"\n >\n <nav\n v-if=\"showStepNav\"\n ref=\"stepStripRef\"\n class=\"tpl-canvas-steps-nav tpl:flex tpl:w-full tpl:flex-wrap tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:shadow-[var(--tpl-shadow-md)] tpl:font-[var(--tpl-font-family)]\"\n role=\"tablist\"\n :aria-label=\"t.canvas.pages.ariaStrip\"\n >\n <template v-for=\"(page, idx) in canvasPagesStrip\" :key=\"page.id\">\n <ChevronRight\n v-if=\"idx > 0\"\n :size=\"16\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:mx-0.5 tpl:text-[var(--tpl-text-muted)]\"\n aria-hidden=\"true\"\n />\n <div class=\"tpl:relative tpl:inline-flex tpl:items-stretch\">\n <div\n class=\"tpl:inline-flex tpl:items-center tpl:overflow-hidden tpl:rounded-full tpl:border tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150\"\n :class=\"\n activeCanvasPageId === page.id\n ? 'tpl-canvas-step tpl-canvas-step--active'\n : 'tpl-canvas-step'\n \"\n >\n <button\n type=\"button\"\n role=\"tab\"\n class=\"tpl:max-w-[200px] tpl:truncate tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-left tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-colors tpl:duration-150 focus-visible:tpl-ring-2 focus-visible:tpl-ring-[var(--tpl-primary)] focus-visible:tpl-ring-offset-2\"\n :aria-selected=\"activeCanvasPageId === page.id\"\n :tabindex=\"activeCanvasPageId === page.id ? 0 : -1\"\n @click=\"selectCanvasPage(page.id)\"\n >\n {{ page.title }}\n </button>\n <button\n v-if=\"!previewMode\"\n type=\"button\"\n class=\"tpl:flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:border-0 tpl:border-l tpl:border-solid tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:text-[var(--tpl-text-muted)] tpl:outline-none tpl:transition-colors tpl:duration-150 hover:tpl:text-[var(--tpl-text)] focus-visible:tpl-ring-2 focus-visible:tpl-ring-inset focus-visible:tpl-ring-[var(--tpl-primary)]\"\n :aria-expanded=\"openStepMenuPageId === page.id\"\n :aria-haspopup=\"true\"\n :aria-label=\"`${page.title}: ${t.canvas.pages.stepActions}`\"\n @click.stop=\"toggleStepMenu(page.id)\"\n >\n <MoreVertical :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n <div\n v-if=\"openStepMenuPageId === page.id && !previewMode\"\n class=\"tpl:absolute tpl:top-full tpl:right-0 tpl:z-[60] tpl:mt-1 tpl:min-w-[10rem] tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:py-1 tpl:shadow-lg\"\n role=\"menu\"\n >\n <button\n type=\"button\"\n role=\"menuitem\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none hover:tpl:bg-[var(--tpl-bg-elevated)]\"\n @click=\"handleRenameCanvasPage(page.id, page.title)\"\n >\n {{ t.canvas.pages.rename }}\n </button>\n <button\n v-if=\"canvasPagesStrip.length > 1\"\n type=\"button\"\n role=\"menuitem\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-danger)] tpl:outline-none hover:tpl:bg-[var(--tpl-bg-elevated)]\"\n @click=\"handleRemoveCanvasPage(page.id)\"\n >\n {{ t.canvas.pages.delete }}\n </button>\n </div>\n </div>\n </template>\n <button\n v-if=\"!previewMode\"\n type=\"button\"\n class=\"tpl:ml-1 tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:rounded-full tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-primary)] tpl:outline-none tpl:transition-colors tpl:duration-150 hover:tpl:border-[var(--tpl-primary)] hover:tpl:bg-[var(--tpl-primary-light)] focus-visible:tpl-ring-2 focus-visible:tpl-ring-[var(--tpl-primary)]\"\n @click=\"handleAddCanvasPage\"\n >\n <SquarePlus :size=\"14\" :stroke-width=\"2\" />\n {{ t.canvas.pages.addStep }}\n </button>\n </nav>\n\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:shrink-0 tpl:rounded-lg\"\n :style=\"canvasCardStyle\"\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, ...popupCanvasPaddingStyle }\"\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 && cloudT\"\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 {{ cloudT.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate && cloudT\"\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 {{ cloudT.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.tpl-canvas-step {\n border-color: var(--tpl-border);\n background-color: color-mix(in srgb, var(--tpl-bg) 92%, transparent);\n}\n\n.tpl-canvas-step--active {\n border-color: color-mix(in srgb, var(--tpl-text) 55%, var(--tpl-border));\n background-color: var(--tpl-bg);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--tpl-text) 12%, transparent);\n}\n\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 { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@aswin.dev/types\";\nimport { normalizeTemplateContentPages } from \"@aswin.dev/types\";\nimport {\n ChevronRight,\n ImageUp,\n MoreVertical,\n Sparkles,\n SquarePlus,\n} from \"@lucide/vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport { computed, inject, onMounted, ref, 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\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\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 InputBlock from \"./blocks/InputBlock.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 input: InputBlock,\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 /** Multi-step canvas (e.g. popup flows); enable via editor config `multiPageCanvas`. */\n multiPageCanvas?: boolean;\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();\nconst { t: cloudT } = useCloudI18n();\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 const c = props.content;\n const activeId = c.activeCanvasPageId;\n if (c.canvasPages?.length && activeId) {\n editor.setContent(\n normalizeTemplateContentPages({\n ...c,\n canvasPages: c.canvasPages.map((p) =>\n p.id === activeId ? { ...p, blocks: value } : p,\n ),\n blocks: value,\n activeCanvasPageId: activeId,\n }),\n );\n return;\n }\n editor.setContent({\n ...c,\n blocks: value,\n });\n },\n});\n\nconst canvasPagesStrip = computed(() => props.content.canvasPages ?? []);\n\nconst activeCanvasPageId = computed(\n () => props.content.activeCanvasPageId ?? null,\n);\n\nconst stepStripRef = ref<HTMLElement | null>(null);\nconst openStepMenuPageId = ref<string | null>(null);\n\nonClickOutside(stepStripRef, () => {\n openStepMenuPageId.value = null;\n});\n\nonMounted(() => {\n if (props.multiPageCanvas) {\n editor.ensureCanvasPages();\n }\n});\n\nfunction selectCanvasPage(pageId: string): void {\n if (pageId === activeCanvasPageId.value) {\n return;\n }\n openStepMenuPageId.value = null;\n editor.switchCanvasPage(pageId);\n}\n\nfunction toggleStepMenu(pageId: string): void {\n openStepMenuPageId.value =\n openStepMenuPageId.value === pageId ? null : pageId;\n}\n\nfunction handleAddCanvasPage(): void {\n openStepMenuPageId.value = null;\n editor.addCanvasPage();\n}\n\nfunction handleRenameCanvasPage(pageId: string, currentTitle: string): void {\n const next = window.prompt(t.canvas.pages.renamePrompt, currentTitle);\n if (next === null) {\n return;\n }\n editor.renameCanvasPage(pageId, next);\n openStepMenuPageId.value = null;\n}\n\nfunction handleRemoveCanvasPage(pageId: string): void {\n if (canvasPagesStrip.value.length <= 1) {\n return;\n }\n editor.removeCanvasPage(pageId);\n openStepMenuPageId.value = null;\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/** Step strip lives in a nav bar above the white canvas card (not inside it). */\nconst showStepNav = computed(\n () => props.multiPageCanvas === true && canvasPagesStrip.value.length > 0,\n);\n\nconst canvasCardStyle = computed(() => ({\n width: showStepNav.value ? \"100%\" : `${viewportWidth.value}px`,\n backgroundColor: props.content.settings.backgroundColor,\n boxShadow: props.darkMode ? \"none\" : \"var(--tpl-shadow-xl)\",\n filter: props.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\nconst canvasRootStyle = computed(() =>\n showStepNav.value ? { width: `${viewportWidth.value}px` } : undefined,\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\nconst popupCanvasPaddingStyle = computed(() => {\n if (!props.content.settings.popup) {\n return {};\n }\n const pad = resolvePopupEmbed(props.content.settings).design.contentPadding;\n return {\n paddingTop: `${pad.top}px`,\n paddingRight: `${pad.right}px`,\n paddingBottom: `${pad.bottom}px`,\n paddingLeft: `${pad.left}px`,\n };\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 class=\"tpl-canvas-root tpl:box-border tpl:max-w-full\"\n :class=\"showStepNav ? 'tpl:flex tpl:flex-col tpl:gap-3' : ''\"\n :style=\"canvasRootStyle\"\n >\n <nav\n v-if=\"showStepNav\"\n ref=\"stepStripRef\"\n class=\"tpl-canvas-steps-nav tpl:flex tpl:w-full tpl:flex-wrap tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:shadow-[var(--tpl-shadow-md)] tpl:font-[var(--tpl-font-family)]\"\n role=\"tablist\"\n :aria-label=\"t.canvas.pages.ariaStrip\"\n >\n <template v-for=\"(page, idx) in canvasPagesStrip\" :key=\"page.id\">\n <ChevronRight\n v-if=\"idx > 0\"\n :size=\"16\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:mx-0.5 tpl:text-[var(--tpl-text-muted)]\"\n aria-hidden=\"true\"\n />\n <div class=\"tpl:relative tpl:inline-flex tpl:items-stretch\">\n <div\n class=\"tpl:inline-flex tpl:items-center tpl:overflow-hidden tpl:rounded-full tpl:border tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150\"\n :class=\"\n activeCanvasPageId === page.id\n ? 'tpl-canvas-step tpl-canvas-step--active'\n : 'tpl-canvas-step'\n \"\n >\n <button\n type=\"button\"\n role=\"tab\"\n class=\"tpl:max-w-[200px] tpl:truncate tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-left tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-colors tpl:duration-150 focus-visible:tpl-ring-2 focus-visible:tpl-ring-[var(--tpl-primary)] focus-visible:tpl-ring-offset-2\"\n :aria-selected=\"activeCanvasPageId === page.id\"\n :tabindex=\"activeCanvasPageId === page.id ? 0 : -1\"\n @click=\"selectCanvasPage(page.id)\"\n >\n {{ page.title }}\n </button>\n <button\n v-if=\"!previewMode\"\n type=\"button\"\n class=\"tpl:flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:border-0 tpl:border-l tpl:border-solid tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:text-[var(--tpl-text-muted)] tpl:outline-none tpl:transition-colors tpl:duration-150 hover:tpl:text-[var(--tpl-text)] focus-visible:tpl-ring-2 focus-visible:tpl-ring-inset focus-visible:tpl-ring-[var(--tpl-primary)]\"\n :aria-expanded=\"openStepMenuPageId === page.id\"\n :aria-haspopup=\"true\"\n :aria-label=\"`${page.title}: ${t.canvas.pages.stepActions}`\"\n @click.stop=\"toggleStepMenu(page.id)\"\n >\n <MoreVertical :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n <div\n v-if=\"openStepMenuPageId === page.id && !previewMode\"\n class=\"tpl:absolute tpl:top-full tpl:right-0 tpl:z-[60] tpl:mt-1 tpl:min-w-[10rem] tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:py-1 tpl:shadow-lg\"\n role=\"menu\"\n >\n <button\n type=\"button\"\n role=\"menuitem\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none hover:tpl:bg-[var(--tpl-bg-elevated)]\"\n @click=\"handleRenameCanvasPage(page.id, page.title)\"\n >\n {{ t.canvas.pages.rename }}\n </button>\n <button\n v-if=\"canvasPagesStrip.length > 1\"\n type=\"button\"\n role=\"menuitem\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-danger)] tpl:outline-none hover:tpl:bg-[var(--tpl-bg-elevated)]\"\n @click=\"handleRemoveCanvasPage(page.id)\"\n >\n {{ t.canvas.pages.delete }}\n </button>\n </div>\n </div>\n </template>\n <button\n v-if=\"!previewMode\"\n type=\"button\"\n class=\"tpl:ml-1 tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:rounded-full tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-primary)] tpl:outline-none tpl:transition-colors tpl:duration-150 hover:tpl:border-[var(--tpl-primary)] hover:tpl:bg-[var(--tpl-primary-light)] focus-visible:tpl-ring-2 focus-visible:tpl-ring-[var(--tpl-primary)]\"\n @click=\"handleAddCanvasPage\"\n >\n <SquarePlus :size=\"14\" :stroke-width=\"2\" />\n {{ t.canvas.pages.addStep }}\n </button>\n </nav>\n\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:shrink-0 tpl:rounded-lg\"\n :style=\"canvasCardStyle\"\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, ...popupCanvasPaddingStyle }\"\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 && cloudT\"\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 {{ cloudT.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate && cloudT\"\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 {{ cloudT.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.tpl-canvas-step {\n border-color: var(--tpl-border);\n background-color: color-mix(in srgb, var(--tpl-bg) 92%, transparent);\n}\n\n.tpl-canvas-step--active {\n border-color: color-mix(in srgb, var(--tpl-text) 55%, var(--tpl-border));\n background-color: var(--tpl-bg);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--tpl-text) 12%, transparent);\n}\n\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 { useId } from \"vue\";\n\nwithDefaults(\n defineProps<{\n checked: boolean;\n /** `neutral`: charcoal track when on (schedule-style). Default orange (design). */\n tone?: \"brand\" | \"neutral\";\n }>(),\n { tone: \"brand\" },\n);\n\nconst emit = defineEmits<{\n toggle: [];\n}>();\n\nconst switchId = useId();\n\nfunction onNativeChange(): void {\n emit(\"toggle\");\n}\n</script>\n\n<template>\n <!-- Structure adapted from Uiverse.io by zanina-yassine (checkbox + track + thumb) -->\n <div\n class=\"popup-design-switch__shell\"\n :class=\"{ 'popup-design-switch__shell--neutral': tone === 'neutral' }\"\n >\n <input\n :id=\"switchId\"\n type=\"checkbox\"\n class=\"popup-design-switch__native\"\n :checked=\"checked\"\n @change=\"onNativeChange\"\n />\n <label class=\"popup-design-switch__track\" :for=\"switchId\">\n <span class=\"popup-design-switch__thumb\" aria-hidden=\"true\" />\n </label>\n </div>\n</template>\n\n<style scoped>\n/* From Uiverse.io by zanina-yassine — scoped + BEM class names for this component */\n\n.popup-design-switch__shell {\n width: 44px;\n height: 26px;\n position: relative;\n flex-shrink: 0;\n vertical-align: middle;\n}\n\n.popup-design-switch__native {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n\n.popup-design-switch__track {\n width: 100%;\n height: 100%;\n display: block;\n background-color: #e9e9eb;\n border-radius: 13px;\n cursor: pointer;\n transition: all 0.2s ease-out;\n}\n\n.popup-design-switch__thumb {\n width: 22px;\n height: 22px;\n position: absolute;\n left: calc(50% - 22px / 2 - 8px);\n top: calc(50% - 22px / 2);\n border-radius: 50%;\n background: #ffffff;\n box-shadow:\n 0px 2px 6px rgba(0, 0, 0, 0.14),\n 0px 2px 1px rgba(0, 0, 0, 0.06);\n transition: all 0.2s ease-out;\n cursor: pointer;\n pointer-events: none;\n}\n\n.popup-design-switch__native:checked + .popup-design-switch__track {\n background-color: #e87e26;\n}\n\n.popup-design-switch__native:checked\n + .popup-design-switch__track\n .popup-design-switch__thumb {\n left: calc(50% - 22px / 2 + 8px);\n top: calc(50% - 22px / 2);\n}\n\n.popup-design-switch__native:focus {\n outline: none;\n}\n\n.popup-design-switch__native:focus-visible + .popup-design-switch__track {\n outline: 2px solid #e87e26;\n outline-offset: 2px;\n}\n\n.popup-design-switch__shell--neutral\n .popup-design-switch__native:checked\n + .popup-design-switch__track {\n background-color: #3b3b3e;\n}\n\n.popup-design-switch__shell--neutral\n .popup-design-switch__native:focus-visible\n + .popup-design-switch__track {\n outline: 2px solid #6a6a6f;\n outline-offset: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useId } from \"vue\";\n\nwithDefaults(\n defineProps<{\n checked: boolean;\n /** `neutral`: charcoal track when on (schedule-style). Default orange (design). */\n tone?: \"brand\" | \"neutral\";\n }>(),\n { tone: \"brand\" },\n);\n\nconst emit = defineEmits<{\n toggle: [];\n}>();\n\nconst switchId = useId();\n\nfunction onNativeChange(): void {\n emit(\"toggle\");\n}\n</script>\n\n<template>\n <!-- Structure adapted from Uiverse.io by zanina-yassine (checkbox + track + thumb) -->\n <div\n class=\"popup-design-switch__shell\"\n :class=\"{ 'popup-design-switch__shell--neutral': tone === 'neutral' }\"\n >\n <input\n :id=\"switchId\"\n type=\"checkbox\"\n class=\"popup-design-switch__native\"\n :checked=\"checked\"\n @change=\"onNativeChange\"\n />\n <label class=\"popup-design-switch__track\" :for=\"switchId\">\n <span class=\"popup-design-switch__thumb\" aria-hidden=\"true\" />\n </label>\n </div>\n</template>\n\n<style scoped>\n/* From Uiverse.io by zanina-yassine — scoped + BEM class names for this component */\n\n.popup-design-switch__shell {\n width: 44px;\n height: 26px;\n position: relative;\n flex-shrink: 0;\n vertical-align: middle;\n}\n\n.popup-design-switch__native {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n\n.popup-design-switch__track {\n width: 100%;\n height: 100%;\n display: block;\n background-color: #e9e9eb;\n border-radius: 13px;\n cursor: pointer;\n transition: all 0.2s ease-out;\n}\n\n.popup-design-switch__thumb {\n width: 22px;\n height: 22px;\n position: absolute;\n left: calc(50% - 22px / 2 - 8px);\n top: calc(50% - 22px / 2);\n border-radius: 50%;\n background: #ffffff;\n box-shadow:\n 0px 2px 6px rgba(0, 0, 0, 0.14),\n 0px 2px 1px rgba(0, 0, 0, 0.06);\n transition: all 0.2s ease-out;\n cursor: pointer;\n pointer-events: none;\n}\n\n.popup-design-switch__native:checked + .popup-design-switch__track {\n background-color: #e87e26;\n}\n\n.popup-design-switch__native:checked\n + .popup-design-switch__track\n .popup-design-switch__thumb {\n left: calc(50% - 22px / 2 + 8px);\n top: calc(50% - 22px / 2);\n}\n\n.popup-design-switch__native:focus {\n outline: none;\n}\n\n.popup-design-switch__native:focus-visible + .popup-design-switch__track {\n outline: 2px solid #e87e26;\n outline-offset: 2px;\n}\n\n.popup-design-switch__shell--neutral\n .popup-design-switch__native:checked\n + .popup-design-switch__track {\n background-color: #3b3b3e;\n}\n\n.popup-design-switch__shell--neutral\n .popup-design-switch__native:focus-visible\n + .popup-design-switch__track {\n outline: 2px solid #6a6a6f;\n outline-offset: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { Crop, Image, Pencil, X } from \"@lucide/vue\";\nimport { ref } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n imageUrl: string;\n}>();\n\nconst emit = defineEmits<{\n \"set-url\": [url: string];\n}>();\n\nconst { t } = useI18n();\n\nconst fileRef = ref<HTMLInputElement | null>(null);\nconst isDragging = ref(false);\n\nconst MAX_DATA_URL_CHARS = 2_400_000;\n\nfunction pickFiles(files: FileList | File[] | null): void {\n const file = files?.[0];\n if (!file || !file.type.startsWith(\"image/\")) return;\n const reader = new FileReader();\n reader.onload = () => {\n const url = typeof reader.result === \"string\" ? reader.result : \"\";\n if (url.length > MAX_DATA_URL_CHARS) return;\n emit(\"set-url\", url);\n if (fileRef.value) fileRef.value.value = \"\";\n };\n reader.readAsDataURL(file);\n}\n\nfunction onDrop(e: DragEvent): void {\n isDragging.value = false;\n pickFiles(e.dataTransfer?.files ?? null);\n}\n\nfunction onBrowse(): void {\n fileRef.value?.click();\n}\n\nfunction onReplace(): void {\n fileRef.value?.click();\n}\n\nfunction onRemove(): void {\n emit(\"set-url\", \"\");\n}\n\nfunction onCrop(): void {\n /* Cropping UI — future */\n}\n</script>\n\n<template>\n <div\n class=\"tpl:mt-4 tpl:rounded-lg tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n role=\"region\"\n :aria-label=\"t.popupDesign.overlayImagePanelAriaLabel\"\n >\n <input\n ref=\"fileRef\"\n type=\"file\"\n class=\"tpl:sr-only\"\n tabindex=\"-1\"\n accept=\"image/jpeg,image/png,image/gif,image/webp,image/avif,image/svg+xml\"\n @change=\"pickFiles(($event.target as HTMLInputElement).files)\"\n />\n\n <template v-if=\"!props.imageUrl\">\n <button\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-8 tpl:text-center tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-colors hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"\n isDragging\n ? 'tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : ''\n \"\n @dragenter.prevent=\"isDragging = true\"\n @dragleave.prevent=\"isDragging = false\"\n @dragover.prevent=\"isDragging = true\"\n @drop.prevent=\"onDrop\"\n @click=\"onBrowse\"\n >\n <Image\n :size=\"28\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n />\n <span class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ t.popupDesign.overlayImageDropLineBefore }}\n <span class=\"tpl:underline tpl:decoration-[var(--tpl-text-muted)]\">{{\n t.popupDesign.overlayImageBrowse\n }}</span>\n {{ t.popupDesign.overlayImageDropLineAfter }}\n </span>\n <span class=\"tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\">{{\n t.popupDesign.overlayImageFormats\n }}</span>\n </button>\n </template>\n\n <template v-else>\n <div class=\"tpl:mb-3 tpl:flex tpl:gap-2\">\n <div\n class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n role=\"img\"\n :aria-label=\"t.popupDesign.overlayImageDesktopPreview\"\n >\n <div\n class=\"tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-2 tpl:py-1.5\"\n >\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:aspect-[16/9] tpl:max-h-[72px] tpl:items-center tpl:justify-center tpl:bg-[var(--tpl-bg)] tpl:p-1\"\n >\n <img\n :src=\"props.imageUrl\"\n alt=\"\"\n class=\"tpl:max-h-full tpl:max-w-full tpl:rounded tpl:object-contain\"\n />\n </div>\n </div>\n <div\n class=\"tpl:flex tpl:w-[52px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n role=\"img\"\n :aria-label=\"t.popupDesign.overlayImageMobilePreview\"\n >\n <div\n class=\"tpl:flex tpl:gap-0.5 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-1 tpl:py-1\"\n >\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:aspect-[9/14] tpl:max-h-[72px] tpl:items-center tpl:justify-center tpl:bg-[var(--tpl-bg)] tpl:p-0.5\"\n >\n <img\n :src=\"props.imageUrl\"\n alt=\"\"\n class=\"tpl:max-h-full tpl:max-w-full tpl:rounded-sm tpl:object-cover\"\n />\n </div>\n </div>\n </div>\n\n <div class=\"tpl:flex tpl:flex-wrap tpl:gap-2\">\n <!-- <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n @click=\"onCrop\"\n >\n <Crop :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageCrop }}\n </button> -->\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n @click=\"onReplace\"\n >\n <Pencil :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageReplace }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-danger)] hover:tpl:text-[var(--tpl-danger)]\"\n @click=\"onRemove\"\n >\n <X :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageRemove }}\n </button>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Crop, Image, Pencil, X } from \"@lucide/vue\";\nimport { ref } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n imageUrl: string;\n}>();\n\nconst emit = defineEmits<{\n \"set-url\": [url: string];\n}>();\n\nconst { t } = useI18n();\n\nconst fileRef = ref<HTMLInputElement | null>(null);\nconst isDragging = ref(false);\n\nconst MAX_DATA_URL_CHARS = 2_400_000;\n\nfunction pickFiles(files: FileList | File[] | null): void {\n const file = files?.[0];\n if (!file || !file.type.startsWith(\"image/\")) return;\n const reader = new FileReader();\n reader.onload = () => {\n const url = typeof reader.result === \"string\" ? reader.result : \"\";\n if (url.length > MAX_DATA_URL_CHARS) return;\n emit(\"set-url\", url);\n if (fileRef.value) fileRef.value.value = \"\";\n };\n reader.readAsDataURL(file);\n}\n\nfunction onDrop(e: DragEvent): void {\n isDragging.value = false;\n pickFiles(e.dataTransfer?.files ?? null);\n}\n\nfunction onBrowse(): void {\n fileRef.value?.click();\n}\n\nfunction onReplace(): void {\n fileRef.value?.click();\n}\n\nfunction onRemove(): void {\n emit(\"set-url\", \"\");\n}\n\nfunction onCrop(): void {\n /* Cropping UI — future */\n}\n</script>\n\n<template>\n <div\n class=\"tpl:mt-4 tpl:rounded-lg tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n role=\"region\"\n :aria-label=\"t.popupDesign.overlayImagePanelAriaLabel\"\n >\n <input\n ref=\"fileRef\"\n type=\"file\"\n class=\"tpl:sr-only\"\n tabindex=\"-1\"\n accept=\"image/jpeg,image/png,image/gif,image/webp,image/avif,image/svg+xml\"\n @change=\"pickFiles(($event.target as HTMLInputElement).files)\"\n />\n\n <template v-if=\"!props.imageUrl\">\n <button\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-8 tpl:text-center tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-colors hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"\n isDragging\n ? 'tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : ''\n \"\n @dragenter.prevent=\"isDragging = true\"\n @dragleave.prevent=\"isDragging = false\"\n @dragover.prevent=\"isDragging = true\"\n @drop.prevent=\"onDrop\"\n @click=\"onBrowse\"\n >\n <Image\n :size=\"28\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n />\n <span class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ t.popupDesign.overlayImageDropLineBefore }}\n <span class=\"tpl:underline tpl:decoration-[var(--tpl-text-muted)]\">{{\n t.popupDesign.overlayImageBrowse\n }}</span>\n {{ t.popupDesign.overlayImageDropLineAfter }}\n </span>\n <span class=\"tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\">{{\n t.popupDesign.overlayImageFormats\n }}</span>\n </button>\n </template>\n\n <template v-else>\n <div class=\"tpl:mb-3 tpl:flex tpl:gap-2\">\n <div\n class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n role=\"img\"\n :aria-label=\"t.popupDesign.overlayImageDesktopPreview\"\n >\n <div\n class=\"tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-2 tpl:py-1.5\"\n >\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1.5 tpl:w-1.5 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:aspect-[16/9] tpl:max-h-[72px] tpl:items-center tpl:justify-center tpl:bg-[var(--tpl-bg)] tpl:p-1\"\n >\n <img\n :src=\"props.imageUrl\"\n alt=\"\"\n class=\"tpl:max-h-full tpl:max-w-full tpl:rounded tpl:object-contain\"\n />\n </div>\n </div>\n <div\n class=\"tpl:flex tpl:w-[52px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n role=\"img\"\n :aria-label=\"t.popupDesign.overlayImageMobilePreview\"\n >\n <div\n class=\"tpl:flex tpl:gap-0.5 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-1 tpl:py-1\"\n >\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n <span\n class=\"tpl:h-1 tpl:w-1 tpl:rounded-full tpl:bg-[var(--tpl-border-light)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:aspect-[9/14] tpl:max-h-[72px] tpl:items-center tpl:justify-center tpl:bg-[var(--tpl-bg)] tpl:p-0.5\"\n >\n <img\n :src=\"props.imageUrl\"\n alt=\"\"\n class=\"tpl:max-h-full tpl:max-w-full tpl:rounded-sm tpl:object-cover\"\n />\n </div>\n </div>\n </div>\n\n <div class=\"tpl:flex tpl:flex-wrap tpl:gap-2\">\n <!-- <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n @click=\"onCrop\"\n >\n <Crop :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageCrop }}\n </button> -->\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n @click=\"onReplace\"\n >\n <Pencil :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageReplace }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-2 tpl:py-2 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:border-[var(--tpl-danger)] hover:tpl:text-[var(--tpl-danger)]\"\n @click=\"onRemove\"\n >\n <X :size=\"14\" :stroke-width=\"1.75\" />\n {{ t.popupDesign.overlayImageRemove }}\n </button>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from \"vue\";\nimport type {\n PopupDisplayAnimation,\n PopupEmbedSettings,\n PopupScreenPosition,\n PopupSizePreset,\n} from \"@aswin.dev/types\";\nimport { POPUP_SIZE_PRESET_WIDTH } from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupDesignSwitch from \"./PopupDesignSwitch.vue\";\nimport PopupOverlayImagePanel from \"./PopupOverlayImagePanel.vue\";\n\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupDesignView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rootClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl-popup-design tpl:w-full tpl:overflow-x-hidden tpl:px-4 tpl:pb-6 tpl:pt-4 tpl:bg-[var(--tpl-bg)]\"\n : \"tpl-popup-design tpl:mx-auto tpl:max-w-md tpl:px-5 tpl:py-8 tpl:bg-[var(--tpl-bg)]\",\n);\n\nconst presetGridClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-2\"\n : \"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-3 sm:tpl:grid-cols-6\",\n);\n\nconst designTab = ref<\"style\" | \"position\" | \"closing\" | \"css\">(\"style\");\n\nconst designTabs = computed(() => [\n { id: \"style\" as const, label: t.popupDesign.tabStyle },\n { id: \"position\" as const, label: t.popupDesign.tabPosition },\n { id: \"closing\" as const, label: t.popupDesign.tabClosing },\n { id: \"css\" as const, label: t.popupDesign.tabCss },\n]);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nfunction patchDesign(patch: Partial<PopupEmbedSettings[\"design\"]>): void {\n const cur = popup.value;\n const design = { ...cur.design, ...patch };\n const updates: Record<string, unknown> = {\n popup: { ...cur, design },\n };\n if (patch.sizePreset !== undefined) {\n updates.width = POPUP_SIZE_PRESET_WIDTH[patch.sizePreset];\n }\n editor!.updateSettings(\n updates as Parameters<BaseEditorReturn[\"updateSettings\"]>[0],\n );\n}\n\nfunction toggleDesign<K extends keyof PopupEmbedSettings[\"design\"]>(\n key: K,\n value: PopupEmbedSettings[\"design\"][K],\n): void {\n patchDesign({ [key]: value } as Partial<PopupEmbedSettings[\"design\"]>);\n}\n\nfunction toggleOverlayImage(): void {\n const next = !popup.value.design.overlayImage;\n patchDesign({\n overlayImage: next,\n ...(!next ? { overlayImageUrl: \"\" } : {}),\n });\n}\n\nfunction setUseOverlay(enabled: boolean): void {\n if (!enabled) {\n patchDesign({\n useOverlay: false,\n overlayImage: false,\n overlayImageUrl: \"\",\n });\n } else {\n patchDesign({ useOverlay: true });\n }\n}\n\nfunction setOverlayImageUrl(url: string): void {\n patchDesign({ overlayImageUrl: url });\n}\n\nconst POSITION_GRID: { id: PopupScreenPosition; cellAlign: string }[] = [\n {\n id: \"topLeft\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-start\",\n },\n {\n id: \"topCenter\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-start\",\n },\n {\n id: \"topRight\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-start\",\n },\n {\n id: \"middleLeft\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-center\",\n },\n {\n id: \"center\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-center\",\n },\n {\n id: \"middleRight\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-center\",\n },\n {\n id: \"bottomLeft\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-end\",\n },\n {\n id: \"bottomCenter\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-end\",\n },\n {\n id: \"bottomRight\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-end\",\n },\n];\n\nconst animationSelectOptions = computed(() => [\n { id: \"smooth\" as const, label: t.popupDesign.animationSmooth },\n { id: \"fadeIn\" as const, label: t.popupDesign.animationFadeIn },\n { id: \"slideIn\" as const, label: t.popupDesign.animationSlideIn },\n { id: \"none\" as const, label: t.popupDesign.animationNone },\n]);\n\nfunction onAnimationChange(e: Event): void {\n patchDesign({\n displayAnimation: (e.target as HTMLSelectElement)\n .value as PopupDisplayAnimation,\n });\n}\n\nfunction onCloseButtonPositionChange(e: Event): void {\n patchDesign({\n closeButtonPosition: (e.target as HTMLSelectElement).value as\n | \"left\"\n | \"right\",\n });\n}\n\nconst sizePresets = computed(() =>\n (\n [\n \"small\",\n \"medium\",\n \"large\",\n \"fullscreen\",\n \"fullHeight\",\n \"custom\",\n ] as PopupSizePreset[]\n ).map((id) => ({\n id,\n label: t.popupDesign.sizeLabels[id],\n })),\n);\n</script>\n\n<template>\n <div :class=\"rootClass\">\n <div\n class=\"tpl:mb-6 tpl:flex tpl:flex-wrap tpl:gap-1 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:p-1\"\n >\n <button\n v-for=\"tab in designTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n designTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:shadow-sm'\n : 'tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"designTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <template v-if=\"designTab === 'style'\">\n <p\n class=\"tpl:mb-4 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.standardSize }}\n </p>\n <div :class=\"presetGridClass\">\n <button\n v-for=\"preset in sizePresets\"\n :key=\"preset.id\"\n type=\"button\"\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-center tpl:transition-shadow hover:tpl:border-[var(--tpl-primary)]\"\n :class=\"\n popup.design.sizePreset === preset.id\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-text)] tpl:ring-offset-2 tpl:ring-offset-[var(--tpl-bg)]'\n : ''\n \"\n @click=\"patchDesign({ sizePreset: preset.id })\"\n >\n <span\n class=\"tpl:flex tpl:h-12 tpl:w-full tpl:items-center tpl:justify-center tpl:rounded-md tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <span\n class=\"tpl:block tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n :class=\"{\n 'tpl:h-6 tpl:w-10': preset.id === 'small',\n 'tpl:h-8 tpl:w-12': preset.id === 'medium',\n 'tpl:h-10 tpl:w-14': preset.id === 'large',\n 'tpl:h-12 tpl:w-full': preset.id === 'fullscreen',\n 'tpl:h-12 tpl:w-8': preset.id === 'fullHeight',\n 'tpl:h-8 tpl:w-10 tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-transparent':\n preset.id === 'custom',\n }\"\n />\n </span>\n <span\n class=\"tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ preset.label }}</span\n >\n </button>\n </div>\n\n <p\n class=\"tpl:mb-3 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.options }}\n </p>\n <div\n class=\"tpl:mb-8 tpl:space-y-5 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.useOverlay }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.useOverlay\"\n @toggle=\"setUseOverlay(!popup.design.useOverlay)\"\n />\n </div>\n <template v-if=\"popup.design.useOverlay\">\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.overlayImage }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.overlayImage\"\n @toggle=\"toggleOverlayImage()\"\n />\n </div>\n <PopupOverlayImagePanel\n v-if=\"popup.design.overlayImage\"\n :image-url=\"popup.design.overlayImageUrl ?? ''\"\n @set-url=\"setOverlayImageUrl\"\n />\n </template>\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.backgroundImage }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.backgroundImage\"\n @toggle=\"\n toggleDesign('backgroundImage', !popup.design.backgroundImage)\n \"\n />\n </div>\n </div>\n\n <div\n class=\"tpl:mb-8 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:mb-3 tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDesign.multiColumn }}\n </span>\n <PopupDesignSwitch\n :checked=\"popup.design.multiColumnEnabled\"\n @toggle=\"\n toggleDesign(\n 'multiColumnEnabled',\n !popup.design.multiColumnEnabled,\n )\n \"\n />\n </div>\n <div v-if=\"popup.design.multiColumnEnabled\" class=\"tpl:flex tpl:gap-3\">\n <button\n type=\"button\"\n class=\"tpl:flex-1 tpl:rounded-lg tpl:border tpl:px-3 tpl:py-4\"\n :class=\"\n popup.design.multiColumnPreset === 'two'\n ? 'tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : 'tpl:border-[var(--tpl-border)]'\n \"\n @click=\"patchDesign({ multiColumnPreset: 'two' })\"\n >\n <span class=\"tpl:flex tpl:justify-center tpl:gap-0.5\">\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n />\n </span>\n </button>\n <button\n type=\"button\"\n class=\"tpl:flex-1 tpl:rounded-lg tpl:border tpl:px-3 tpl:py-4\"\n :class=\"\n popup.design.multiColumnPreset === 'three'\n ? 'tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : 'tpl:border-[var(--tpl-border)]'\n \"\n @click=\"patchDesign({ multiColumnPreset: 'three' })\"\n >\n <span class=\"tpl:flex tpl:justify-center tpl:gap-0.5\">\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n </span>\n </button>\n </div>\n </div>\n\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\">{{\n t.popupDesign.rtl\n }}</span>\n <PopupDesignSwitch\n :checked=\"popup.design.rtl\"\n @toggle=\"toggleDesign('rtl', !popup.design.rtl)\"\n />\n </div>\n </template>\n\n <template v-else-if=\"designTab === 'position'\">\n <p\n class=\"tpl:mb-3 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.positionHeading }}\n </p>\n <div\n class=\"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-2\"\n role=\"group\"\n :aria-label=\"t.popupDesign.positionHeading\"\n >\n <button\n v-for=\"cell in POSITION_GRID\"\n :key=\"cell.id\"\n type=\"button\"\n class=\"tpl:flex tpl:min-h-[52px] tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:transition-all tpl:outline-none hover:tpl:border-[var(--tpl-text-muted)] focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"\n popup.design.popupPosition === cell.id\n ? 'tpl:border-2 tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : ''\n \"\n :aria-pressed=\"popup.design.popupPosition === cell.id\"\n :aria-label=\"t.popupDesign.positionLabels[cell.id]\"\n @click=\"patchDesign({ popupPosition: cell.id })\"\n >\n <div :class=\"cell.cellAlign\">\n <span\n class=\"tpl:block tpl:h-4 tpl:w-7 tpl:rounded-sm tpl:transition-colors\"\n :class=\"\n popup.design.popupPosition === cell.id\n ? 'tpl:bg-[var(--tpl-text)]'\n : 'tpl:bg-[var(--tpl-text-muted)] tpl:opacity-70'\n \"\n />\n </div>\n </button>\n </div>\n <p\n class=\"tpl:mb-2 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.displayAnimationHeading }}\n </p>\n <label class=\"tpl:sr-only\" for=\"popup-display-animation\">{{\n t.popupDesign.displayAnimationHeading\n }}</label>\n <select\n id=\"popup-display-animation\"\n class=\"tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :value=\"popup.design.displayAnimation\"\n @change=\"onAnimationChange\"\n >\n <option\n v-for=\"opt in animationSelectOptions\"\n :key=\"opt.id\"\n :value=\"opt.id\"\n >\n {{ opt.label }}\n </option>\n </select>\n </template>\n\n <template v-else-if=\"designTab === 'closing'\">\n <div\n class=\"tpl:space-y-5 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.closingDisplayCloseButton }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.displayCloseButton\"\n @toggle=\"\n toggleDesign(\n 'displayCloseButton',\n !popup.design.displayCloseButton,\n )\n \"\n />\n </div>\n <div v-if=\"popup.design.displayCloseButton\" class=\"tpl:space-y-2\">\n <label\n class=\"tpl:block tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n for=\"popup-close-button-position\"\n >\n {{ t.popupDesign.closingButtonPosition }}\n </label>\n <select\n id=\"popup-close-button-position\"\n class=\"tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :value=\"popup.design.closeButtonPosition\"\n @change=\"onCloseButtonPositionChange\"\n >\n <option value=\"left\">\n {{ t.popupDesign.closingButtonPositionLeft }}\n </option>\n <option value=\"right\">\n {{ t.popupDesign.closingButtonPositionRight }}\n </option>\n </select>\n </div>\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.closingOnOverlayClick }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.closeOnOverlayClick\"\n @toggle=\"\n toggleDesign(\n 'closeOnOverlayClick',\n !popup.design.closeOnOverlayClick,\n )\n \"\n />\n </div>\n </div>\n </template>\n\n <template v-else>\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDesign.placeholderSubtab }}\n </p>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref } from \"vue\";\nimport type {\n PopupDisplayAnimation,\n PopupEmbedSettings,\n PopupScreenPosition,\n PopupSizePreset,\n} from \"@aswin.dev/types\";\nimport { POPUP_SIZE_PRESET_WIDTH } from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupDesignSwitch from \"./PopupDesignSwitch.vue\";\nimport PopupOverlayImagePanel from \"./PopupOverlayImagePanel.vue\";\n\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupDesignView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rootClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl-popup-design tpl:w-full tpl:overflow-x-hidden tpl:px-4 tpl:pb-6 tpl:pt-4 tpl:bg-[var(--tpl-bg)]\"\n : \"tpl-popup-design tpl:mx-auto tpl:max-w-md tpl:px-5 tpl:py-8 tpl:bg-[var(--tpl-bg)]\",\n);\n\nconst presetGridClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-2\"\n : \"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-3 sm:tpl:grid-cols-6\",\n);\n\nconst designTab = ref<\"style\" | \"position\" | \"closing\" | \"css\">(\"style\");\n\nconst designTabs = computed(() => [\n { id: \"style\" as const, label: t.popupDesign.tabStyle },\n { id: \"position\" as const, label: t.popupDesign.tabPosition },\n { id: \"closing\" as const, label: t.popupDesign.tabClosing },\n { id: \"css\" as const, label: t.popupDesign.tabCss },\n]);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nfunction patchDesign(patch: Partial<PopupEmbedSettings[\"design\"]>): void {\n const cur = popup.value;\n const design = { ...cur.design, ...patch };\n const updates: Record<string, unknown> = {\n popup: { ...cur, design },\n };\n if (patch.sizePreset !== undefined) {\n updates.width = POPUP_SIZE_PRESET_WIDTH[patch.sizePreset];\n }\n editor!.updateSettings(\n updates as Parameters<BaseEditorReturn[\"updateSettings\"]>[0],\n );\n}\n\nfunction toggleDesign<K extends keyof PopupEmbedSettings[\"design\"]>(\n key: K,\n value: PopupEmbedSettings[\"design\"][K],\n): void {\n patchDesign({ [key]: value } as Partial<PopupEmbedSettings[\"design\"]>);\n}\n\nfunction toggleOverlayImage(): void {\n const next = !popup.value.design.overlayImage;\n patchDesign({\n overlayImage: next,\n ...(!next ? { overlayImageUrl: \"\" } : {}),\n });\n}\n\nfunction setUseOverlay(enabled: boolean): void {\n if (!enabled) {\n patchDesign({\n useOverlay: false,\n overlayImage: false,\n overlayImageUrl: \"\",\n });\n } else {\n patchDesign({ useOverlay: true });\n }\n}\n\nfunction setOverlayImageUrl(url: string): void {\n patchDesign({ overlayImageUrl: url });\n}\n\nconst POSITION_GRID: { id: PopupScreenPosition; cellAlign: string }[] = [\n {\n id: \"topLeft\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-start\",\n },\n {\n id: \"topCenter\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-start\",\n },\n {\n id: \"topRight\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-start\",\n },\n {\n id: \"middleLeft\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-center\",\n },\n {\n id: \"center\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-center\",\n },\n {\n id: \"middleRight\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-center\",\n },\n {\n id: \"bottomLeft\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-start tpl:items-end\",\n },\n {\n id: \"bottomCenter\",\n cellAlign:\n \"tpl:flex tpl:h-full tpl:w-full tpl:justify-center tpl:items-end\",\n },\n {\n id: \"bottomRight\",\n cellAlign: \"tpl:flex tpl:h-full tpl:w-full tpl:justify-end tpl:items-end\",\n },\n];\n\nconst animationSelectOptions = computed(() => [\n { id: \"smooth\" as const, label: t.popupDesign.animationSmooth },\n { id: \"fadeIn\" as const, label: t.popupDesign.animationFadeIn },\n { id: \"slideIn\" as const, label: t.popupDesign.animationSlideIn },\n { id: \"none\" as const, label: t.popupDesign.animationNone },\n]);\n\nfunction onAnimationChange(e: Event): void {\n patchDesign({\n displayAnimation: (e.target as HTMLSelectElement)\n .value as PopupDisplayAnimation,\n });\n}\n\nfunction onCloseButtonPositionChange(e: Event): void {\n patchDesign({\n closeButtonPosition: (e.target as HTMLSelectElement).value as\n | \"left\"\n | \"right\",\n });\n}\n\nconst sizePresets = computed(() =>\n (\n [\n \"small\",\n \"medium\",\n \"large\",\n \"fullscreen\",\n \"fullHeight\",\n \"custom\",\n ] as PopupSizePreset[]\n ).map((id) => ({\n id,\n label: t.popupDesign.sizeLabels[id],\n })),\n);\n</script>\n\n<template>\n <div :class=\"rootClass\">\n <div\n class=\"tpl:mb-6 tpl:flex tpl:flex-wrap tpl:gap-1 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:p-1\"\n >\n <button\n v-for=\"tab in designTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n designTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:shadow-sm'\n : 'tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"designTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <template v-if=\"designTab === 'style'\">\n <p\n class=\"tpl:mb-4 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.standardSize }}\n </p>\n <div :class=\"presetGridClass\">\n <button\n v-for=\"preset in sizePresets\"\n :key=\"preset.id\"\n type=\"button\"\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-center tpl:transition-shadow hover:tpl:border-[var(--tpl-primary)]\"\n :class=\"\n popup.design.sizePreset === preset.id\n ? 'tpl:ring-2 tpl:ring-[var(--tpl-text)] tpl:ring-offset-2 tpl:ring-offset-[var(--tpl-bg)]'\n : ''\n \"\n @click=\"patchDesign({ sizePreset: preset.id })\"\n >\n <span\n class=\"tpl:flex tpl:h-12 tpl:w-full tpl:items-center tpl:justify-center tpl:rounded-md tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <span\n class=\"tpl:block tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n :class=\"{\n 'tpl:h-6 tpl:w-10': preset.id === 'small',\n 'tpl:h-8 tpl:w-12': preset.id === 'medium',\n 'tpl:h-10 tpl:w-14': preset.id === 'large',\n 'tpl:h-12 tpl:w-full': preset.id === 'fullscreen',\n 'tpl:h-12 tpl:w-8': preset.id === 'fullHeight',\n 'tpl:h-8 tpl:w-10 tpl:border tpl:border-dashed tpl:border-[var(--tpl-border)] tpl:bg-transparent':\n preset.id === 'custom',\n }\"\n />\n </span>\n <span\n class=\"tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ preset.label }}</span\n >\n </button>\n </div>\n\n <p\n class=\"tpl:mb-3 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.options }}\n </p>\n <div\n class=\"tpl:mb-8 tpl:space-y-5 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.useOverlay }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.useOverlay\"\n @toggle=\"setUseOverlay(!popup.design.useOverlay)\"\n />\n </div>\n <template v-if=\"popup.design.useOverlay\">\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.overlayImage }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.overlayImage\"\n @toggle=\"toggleOverlayImage()\"\n />\n </div>\n <PopupOverlayImagePanel\n v-if=\"popup.design.overlayImage\"\n :image-url=\"popup.design.overlayImageUrl ?? ''\"\n @set-url=\"setOverlayImageUrl\"\n />\n </template>\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.backgroundImage }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.backgroundImage\"\n @toggle=\"\n toggleDesign('backgroundImage', !popup.design.backgroundImage)\n \"\n />\n </div>\n </div>\n\n <div\n class=\"tpl:mb-8 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:mb-3 tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDesign.multiColumn }}\n </span>\n <PopupDesignSwitch\n :checked=\"popup.design.multiColumnEnabled\"\n @toggle=\"\n toggleDesign(\n 'multiColumnEnabled',\n !popup.design.multiColumnEnabled,\n )\n \"\n />\n </div>\n <div v-if=\"popup.design.multiColumnEnabled\" class=\"tpl:flex tpl:gap-3\">\n <button\n type=\"button\"\n class=\"tpl:flex-1 tpl:rounded-lg tpl:border tpl:px-3 tpl:py-4\"\n :class=\"\n popup.design.multiColumnPreset === 'two'\n ? 'tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : 'tpl:border-[var(--tpl-border)]'\n \"\n @click=\"patchDesign({ multiColumnPreset: 'two' })\"\n >\n <span class=\"tpl:flex tpl:justify-center tpl:gap-0.5\">\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-text-muted)] tpl:opacity-80\"\n />\n </span>\n </button>\n <button\n type=\"button\"\n class=\"tpl:flex-1 tpl:rounded-lg tpl:border tpl:px-3 tpl:py-4\"\n :class=\"\n popup.design.multiColumnPreset === 'three'\n ? 'tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : 'tpl:border-[var(--tpl-border)]'\n \"\n @click=\"patchDesign({ multiColumnPreset: 'three' })\"\n >\n <span class=\"tpl:flex tpl:justify-center tpl:gap-0.5\">\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n <span\n class=\"tpl:h-10 tpl:flex-1 tpl:rounded tpl:bg-[var(--tpl-bg-hover)]\"\n />\n </span>\n </button>\n </div>\n </div>\n\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\">{{\n t.popupDesign.rtl\n }}</span>\n <PopupDesignSwitch\n :checked=\"popup.design.rtl\"\n @toggle=\"toggleDesign('rtl', !popup.design.rtl)\"\n />\n </div>\n </template>\n\n <template v-else-if=\"designTab === 'position'\">\n <p\n class=\"tpl:mb-3 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.positionHeading }}\n </p>\n <div\n class=\"tpl:mb-8 tpl:grid tpl:grid-cols-3 tpl:gap-2\"\n role=\"group\"\n :aria-label=\"t.popupDesign.positionHeading\"\n >\n <button\n v-for=\"cell in POSITION_GRID\"\n :key=\"cell.id\"\n type=\"button\"\n class=\"tpl:flex tpl:min-h-[52px] tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:transition-all tpl:outline-none hover:tpl:border-[var(--tpl-text-muted)] focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"\n popup.design.popupPosition === cell.id\n ? 'tpl:border-2 tpl:border-[var(--tpl-text)] tpl:bg-[var(--tpl-bg-elevated)]'\n : ''\n \"\n :aria-pressed=\"popup.design.popupPosition === cell.id\"\n :aria-label=\"t.popupDesign.positionLabels[cell.id]\"\n @click=\"patchDesign({ popupPosition: cell.id })\"\n >\n <div :class=\"cell.cellAlign\">\n <span\n class=\"tpl:block tpl:h-4 tpl:w-7 tpl:rounded-sm tpl:transition-colors\"\n :class=\"\n popup.design.popupPosition === cell.id\n ? 'tpl:bg-[var(--tpl-text)]'\n : 'tpl:bg-[var(--tpl-text-muted)] tpl:opacity-70'\n \"\n />\n </div>\n </button>\n </div>\n <p\n class=\"tpl:mb-2 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDesign.displayAnimationHeading }}\n </p>\n <label class=\"tpl:sr-only\" for=\"popup-display-animation\">{{\n t.popupDesign.displayAnimationHeading\n }}</label>\n <select\n id=\"popup-display-animation\"\n class=\"tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :value=\"popup.design.displayAnimation\"\n @change=\"onAnimationChange\"\n >\n <option\n v-for=\"opt in animationSelectOptions\"\n :key=\"opt.id\"\n :value=\"opt.id\"\n >\n {{ opt.label }}\n </option>\n </select>\n </template>\n\n <template v-else-if=\"designTab === 'closing'\">\n <div\n class=\"tpl:space-y-5 tpl:rounded-[10px] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-4 tpl:py-5\"\n >\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.closingDisplayCloseButton }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.displayCloseButton\"\n @toggle=\"\n toggleDesign(\n 'displayCloseButton',\n !popup.design.displayCloseButton,\n )\n \"\n />\n </div>\n <div v-if=\"popup.design.displayCloseButton\" class=\"tpl:space-y-2\">\n <label\n class=\"tpl:block tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n for=\"popup-close-button-position\"\n >\n {{ t.popupDesign.closingButtonPosition }}\n </label>\n <select\n id=\"popup-close-button-position\"\n class=\"tpl:w-full tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :value=\"popup.design.closeButtonPosition\"\n @change=\"onCloseButtonPositionChange\"\n >\n <option value=\"left\">\n {{ t.popupDesign.closingButtonPositionLeft }}\n </option>\n <option value=\"right\">\n {{ t.popupDesign.closingButtonPositionRight }}\n </option>\n </select>\n </div>\n <div\n class=\"tpl:flex tpl:min-h-[28px] tpl:items-center tpl:justify-between tpl:gap-4\"\n >\n <span\n class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >{{ t.popupDesign.closingOnOverlayClick }}</span\n >\n <PopupDesignSwitch\n :checked=\"popup.design.closeOnOverlayClick\"\n @toggle=\"\n toggleDesign(\n 'closeOnOverlayClick',\n !popup.design.closeOnOverlayClick,\n )\n \"\n />\n </div>\n </div>\n </template>\n\n <template v-else>\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDesign.placeholderSubtab }}\n </p>\n </template>\n </div>\n</template>\n","import type { Block, BlockType } from \"@aswin.dev/types\";\nimport { createBlock, createCustomBlock } from \"@aswin.dev/types\";\nimport { computed, inject } from \"vue\";\nimport {\n BLOCK_DEFAULTS_KEY,\n CAPABILITIES_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n EDITOR_KEY,\n} from \"../keys\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport { useI18n } from \"./useI18n\";\n\nexport interface BlockPaletteItem {\n type: BlockType | string;\n label: string;\n isCustom?: boolean;\n icon?: string;\n}\n\nexport function useBlockPalette() {\n const { t, format } = useI18n();\n const customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n const blockDefaults = inject(BLOCK_DEFAULTS_KEY, undefined);\n const editor = inject(EDITOR_KEY, null);\n const caps = inject(CAPABILITIES_KEY, {});\n\n const showModulesSection = computed(\n () => (caps.savedModules?.moduleCount.value ?? 0) > 0,\n );\n\n const builtInBlockTypeOrder: string[] = [\n \"section\",\n \"image\",\n \"title\",\n \"paragraph\",\n \"button\",\n \"input\",\n \"divider\",\n \"video\",\n \"social\",\n \"menu\",\n \"table\",\n \"spacer\",\n \"html\",\n ];\n\n const builtInBlockTypes = computed<BlockPaletteItem[]>(() => {\n const types: BlockPaletteItem[] = builtInBlockTypeOrder.map((type) => ({\n type,\n label: getBlockTypeLabel(type, t),\n }));\n\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\n const customBlockItems = computed<BlockPaletteItem[]>(() => {\n return customBlockDefinitions.map((def) => ({\n type: `custom:${def.type}`,\n label: def.name,\n isCustom: true,\n icon: def.icon,\n }));\n });\n\n const blockTypes = computed<BlockPaletteItem[]>(() => [\n ...builtInBlockTypes.value,\n ...customBlockItems.value,\n ]);\n\n function createBlockFromItem(item: BlockPaletteItem): 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\n function insertBlockFromItem(item: BlockPaletteItem): void {\n if (!editor) return;\n const block = createBlockFromItem(item);\n editor.addBlock(block);\n editor.selectBlock(block.id);\n }\n\n function handlePaletteKeydown(\n event: KeyboardEvent,\n item: BlockPaletteItem,\n ): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n insertBlockFromItem(item);\n }\n }\n\n return {\n t,\n format,\n blockTypes,\n createBlockFromItem,\n insertBlockFromItem,\n handlePaletteKeydown,\n caps,\n showModulesSection,\n };\n}\n","<script setup lang=\"ts\">\nimport { Package } from \"@lucide/vue\";\nimport { computed, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { useBlockPalette } from \"../composables/useBlockPalette\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\n\n/** `standalone`: docked to viewport left. `popupAdjacent`: beside popup icon rail. */\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst hoverExpanded = ref(false);\n\n/** Popup chrome uses a narrow strip; keep labels visible without hover. Standalone email sidebar stays hover/focus-expand. */\nconst isExpanded = computed(\n () => props.layout === \"popupAdjacent\" || hoverExpanded.value,\n);\n\nfunction setAsideExpanded(next: boolean): void {\n if (props.layout === \"popupAdjacent\") return;\n hoverExpanded.value = next;\n}\n\nconst {\n t,\n format,\n blockTypes,\n createBlockFromItem,\n insertBlockFromItem,\n handlePaletteKeydown,\n showModulesSection,\n caps,\n} = useBlockPalette();\n\nconst { t: cloudT } = useCloudI18n();\n\nfunction openModulesBrowser(): void {\n caps.savedModules?.openBrowser();\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n :class=\"\n props.layout === 'standalone'\n ? 'tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden'\n : 'tpl-sidebar-rail tpl:relative tpl:z-0 tpl:h-full tpl:min-h-0 tpl:shrink-0 tpl:overflow-hidden'\n \"\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=\"setAsideExpanded(true)\"\n @mouseleave=\"setAsideExpanded(false)\"\n @focusin=\"setAsideExpanded(true)\"\n @focusout=\"setAsideExpanded(false)\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection && cloudT\"\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=\"openModulesBrowser\"\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 {{ cloudT.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 { Package } from \"@lucide/vue\";\nimport { computed, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { useBlockPalette } from \"../composables/useBlockPalette\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\n\n/** `standalone`: docked to viewport left. `popupAdjacent`: beside popup icon rail. */\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst hoverExpanded = ref(false);\n\n/** Popup chrome uses a narrow strip; keep labels visible without hover. Standalone email sidebar stays hover/focus-expand. */\nconst isExpanded = computed(\n () => props.layout === \"popupAdjacent\" || hoverExpanded.value,\n);\n\nfunction setAsideExpanded(next: boolean): void {\n if (props.layout === \"popupAdjacent\") return;\n hoverExpanded.value = next;\n}\n\nconst {\n t,\n format,\n blockTypes,\n createBlockFromItem,\n insertBlockFromItem,\n handlePaletteKeydown,\n showModulesSection,\n caps,\n} = useBlockPalette();\n\nconst { t: cloudT } = useCloudI18n();\n\nfunction openModulesBrowser(): void {\n caps.savedModules?.openBrowser();\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n :class=\"\n props.layout === 'standalone'\n ? 'tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden'\n : 'tpl-sidebar-rail tpl:relative tpl:z-0 tpl:h-full tpl:min-h-0 tpl:shrink-0 tpl:overflow-hidden'\n \"\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=\"setAsideExpanded(true)\"\n @mouseleave=\"setAsideExpanded(false)\"\n @focusin=\"setAsideExpanded(true)\"\n @focusout=\"setAsideExpanded(false)\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection && cloudT\"\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=\"openModulesBrowser\"\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 {{ cloudT.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 { Calendar, LayoutGrid, Palette, ScrollText } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { POPUP_RAIL_TAB_KEY } from \"../keys\";\nimport type { PopupRailTabId } from \"../types/popup-rail-tab\";\nimport PopupDesignView from \"./PopupDesignView.vue\";\nimport SidebarEmailRail from \"./SidebarEmailRail.vue\";\n\nconst fallbackRailTab = ref<PopupRailTabId>(\"blocks\");\nconst popupRailTab = inject(POPUP_RAIL_TAB_KEY, fallbackRailTab);\n\nconst { t } = useI18n();\n\nconst railTabs = computed(() => [\n { id: \"design\" as const, icon: Palette, label: t.popupSidebar.railDesign },\n { id: \"blocks\" as const, icon: LayoutGrid, label: t.popupSidebar.railBlocks },\n {\n id: \"displayRules\" as const,\n icon: ScrollText,\n label: t.popupSidebar.railDisplayRules,\n },\n {\n id: \"schedule\" as const,\n icon: Calendar,\n label: t.popupSidebar.railSchedule,\n },\n]);\n</script>\n\n<template>\n <div\n class=\"tpl-popup-left-chrome tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:flex tpl:overflow-hidden\"\n style=\"\n border-right: 1px solid var(--tpl-border);\n box-shadow: var(--tpl-shadow-sm);\n \"\n :aria-label=\"t.popupSidebar.workspaceLabel\"\n >\n <nav\n class=\"tpl:flex tpl:w-[72px] tpl:shrink-0 tpl:flex-col tpl:items-stretch tpl:gap-0.5 tpl:overflow-y-auto tpl:py-2 tpl:px-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg-elevated) 92%,\n transparent\n );\n \"\n :aria-label=\"t.popupSidebar.railNavLabel\"\n >\n <button\n v-for=\"tab in railTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-0.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:py-2 tpl:px-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-offset-[-2px] focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :class=\"\n popupRailTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-primary)] tpl:shadow-sm'\n : 'tpl:bg-transparent hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)]'\n \"\n :aria-current=\"popupRailTab === tab.id ? 'page' : undefined\"\n @click=\"popupRailTab = tab.id\"\n >\n <component\n :is=\"tab.icon\"\n :size=\"18\"\n :stroke-width=\"1.75\"\n class=\"tpl:shrink-0\"\n />\n <span\n class=\"tpl:max-w-[4.25rem] tpl:text-center tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n >\n {{ tab.label }}\n </span>\n </button>\n </nav>\n\n <SidebarEmailRail v-if=\"popupRailTab === 'blocks'\" layout=\"popupAdjacent\" />\n\n <div\n v-else-if=\"popupRailTab === 'design'\"\n class=\"tpl:flex tpl:h-full tpl:w-[320px] tpl:min-h-0 tpl:shrink-0 tpl:flex-col tpl:overflow-y-auto tpl:overflow-x-hidden tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n role=\"region\"\n :aria-label=\"t.popupSidebar.railDesign\"\n >\n <PopupDesignView layout=\"popupAdjacent\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Calendar, LayoutGrid, Palette, ScrollText } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { POPUP_RAIL_TAB_KEY } from \"../keys\";\nimport type { PopupRailTabId } from \"../types/popup-rail-tab\";\nimport PopupDesignView from \"./PopupDesignView.vue\";\nimport SidebarEmailRail from \"./SidebarEmailRail.vue\";\n\nconst fallbackRailTab = ref<PopupRailTabId>(\"blocks\");\nconst popupRailTab = inject(POPUP_RAIL_TAB_KEY, fallbackRailTab);\n\nconst { t } = useI18n();\n\nconst railTabs = computed(() => [\n { id: \"design\" as const, icon: Palette, label: t.popupSidebar.railDesign },\n { id: \"blocks\" as const, icon: LayoutGrid, label: t.popupSidebar.railBlocks },\n {\n id: \"displayRules\" as const,\n icon: ScrollText,\n label: t.popupSidebar.railDisplayRules,\n },\n {\n id: \"schedule\" as const,\n icon: Calendar,\n label: t.popupSidebar.railSchedule,\n },\n]);\n</script>\n\n<template>\n <div\n class=\"tpl-popup-left-chrome tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:flex tpl:overflow-hidden\"\n style=\"\n border-right: 1px solid var(--tpl-border);\n box-shadow: var(--tpl-shadow-sm);\n \"\n :aria-label=\"t.popupSidebar.workspaceLabel\"\n >\n <nav\n class=\"tpl:flex tpl:w-[72px] tpl:shrink-0 tpl:flex-col tpl:items-stretch tpl:gap-0.5 tpl:overflow-y-auto tpl:py-2 tpl:px-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg-elevated) 92%,\n transparent\n );\n \"\n :aria-label=\"t.popupSidebar.railNavLabel\"\n >\n <button\n v-for=\"tab in railTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-0.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:py-2 tpl:px-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-offset-[-2px] focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :class=\"\n popupRailTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-primary)] tpl:shadow-sm'\n : 'tpl:bg-transparent hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)]'\n \"\n :aria-current=\"popupRailTab === tab.id ? 'page' : undefined\"\n @click=\"popupRailTab = tab.id\"\n >\n <component\n :is=\"tab.icon\"\n :size=\"18\"\n :stroke-width=\"1.75\"\n class=\"tpl:shrink-0\"\n />\n <span\n class=\"tpl:max-w-[4.25rem] tpl:text-center tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n >\n {{ tab.label }}\n </span>\n </button>\n </nav>\n\n <SidebarEmailRail v-if=\"popupRailTab === 'blocks'\" layout=\"popupAdjacent\" />\n\n <div\n v-else-if=\"popupRailTab === 'design'\"\n class=\"tpl:flex tpl:h-full tpl:w-[320px] tpl:min-h-0 tpl:shrink-0 tpl:flex-col tpl:overflow-y-auto tpl:overflow-x-hidden tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n role=\"region\"\n :aria-label=\"t.popupSidebar.railDesign\"\n >\n <PopupDesignView layout=\"popupAdjacent\" />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { inject } from \"vue\";\nimport { EDITOR_TYPE_KEY } from \"../keys\";\nimport PopupLeftChrome from \"./PopupLeftChrome.vue\";\nimport SidebarEmailRail from \"./SidebarEmailRail.vue\";\n\nconst editorType = inject(EDITOR_TYPE_KEY, \"email\");\n</script>\n\n<template>\n <PopupLeftChrome v-if=\"editorType === 'popup'\" />\n <SidebarEmailRail v-else />\n</template>\n","<script setup lang=\"ts\">\nimport { inject } from \"vue\";\nimport { EDITOR_TYPE_KEY } from \"../keys\";\nimport PopupLeftChrome from \"./PopupLeftChrome.vue\";\nimport SidebarEmailRail from \"./SidebarEmailRail.vue\";\n\nconst editorType = inject(EDITOR_TYPE_KEY, \"email\");\n</script>\n\n<template>\n <PopupLeftChrome v-if=\"editorType === 'popup'\" />\n <SidebarEmailRail v-else />\n</template>\n","import {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@aswin.dev/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 canRequestMergeTag: 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 canRequestMergeTag,\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 let mergeTag: Awaited<ReturnType<typeof requestMergeTag>>;\n try {\n mergeTag = await requestMergeTag();\n } finally {\n insertingMergeTag = false;\n }\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 canRequestMergeTag,\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 canRequestMergeTag,\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=\"canRequestMergeTag\"\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=\"canRequestMergeTag\"\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 canRequestMergeTag,\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=\"canRequestMergeTag\"\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=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] 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 ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport SpacingControl from \"./SpacingControl.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n PopupDesignSettings,\n SpacingValue,\n TemplateSettings,\n} from \"@aswin.dev/types\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, 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\nconst hasPopupSettings = computed(() => Boolean(props.settings.popup));\n\nconst popupContentPadding = computed(() =>\n hasPopupSettings.value\n ? resolvePopupEmbed(props.settings).design.contentPadding\n : null,\n);\n\nfunction patchPopupDesign(patch: Partial<PopupDesignSettings>): void {\n const resolved = resolvePopupEmbed(props.settings);\n emit(\"update\", {\n popup: {\n ...resolved,\n design: {\n ...resolved.design,\n ...patch,\n },\n },\n });\n}\n\nfunction onPopupContentPaddingUpdate(value: SpacingValue): void {\n patchPopupDesign({ contentPadding: value });\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\n <div v-if=\"hasPopupSettings && popupContentPadding\" class=\"tpl:mt-4\">\n <SpacingControl\n :model-value=\"popupContentPadding\"\n :label=\"t.templateSettings.contentPadding\"\n @update:model-value=\"onPopupContentPaddingUpdate\"\n />\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 <!-- Language 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 <Globe\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.language }}</span>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.contentLocale\n }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"settings.locale ?? ''\"\n placeholder=\"en\"\n spellcheck=\"false\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n @input=\"\n emit('update', {\n locale:\n ($event.target as HTMLInputElement).value.trim() || undefined,\n })\n \"\n />\n <p\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.contentLocaleHint }}\n </p>\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 SpacingControl from \"./SpacingControl.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n PopupDesignSettings,\n SpacingValue,\n TemplateSettings,\n} from \"@aswin.dev/types\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, 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\nconst hasPopupSettings = computed(() => Boolean(props.settings.popup));\n\nconst popupContentPadding = computed(() =>\n hasPopupSettings.value\n ? resolvePopupEmbed(props.settings).design.contentPadding\n : null,\n);\n\nfunction patchPopupDesign(patch: Partial<PopupDesignSettings>): void {\n const resolved = resolvePopupEmbed(props.settings);\n emit(\"update\", {\n popup: {\n ...resolved,\n design: {\n ...resolved.design,\n ...patch,\n },\n },\n });\n}\n\nfunction onPopupContentPaddingUpdate(value: SpacingValue): void {\n patchPopupDesign({ contentPadding: value });\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\n <div v-if=\"hasPopupSettings && popupContentPadding\" class=\"tpl:mt-4\">\n <SpacingControl\n :model-value=\"popupContentPadding\"\n :label=\"t.templateSettings.contentPadding\"\n @update:model-value=\"onPopupContentPaddingUpdate\"\n />\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 <!-- Language 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 <Globe\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.language }}</span>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.contentLocale\n }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"settings.locale ?? ''\"\n placeholder=\"en\"\n spellcheck=\"false\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n @input=\"\n emit('update', {\n locale:\n ($event.target as HTMLInputElement).value.trim() || undefined,\n })\n \"\n />\n <p\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.contentLocaleHint }}\n </p>\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 canRequestMergeTag,\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=\"canRequestMergeTag\"\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=\"canRequestMergeTag\"\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 canRequestMergeTag,\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=\"canRequestMergeTag\"\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=\"canRequestMergeTag\"\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 { computed } from \"vue\";\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 {\n ButtonBlock,\n ButtonBorderSides,\n ButtonClickAction,\n} from \"@aswin.dev/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst effectiveClickAction = computed(\n (): ButtonClickAction => props.block.clickAction ?? \"none\",\n);\n\nconst showLinkUrlFields = computed(() => {\n const a = effectiveClickAction.value;\n return a === \"none\" || a === \"load_page\";\n});\n\nconst urlFieldLabel = computed(() =>\n effectiveClickAction.value === \"load_page\" ? t.button.pageUrl : t.button.url,\n);\n\nconst selectActionModel = computed(() =>\n effectiveClickAction.value === \"none\" ? \"\" : effectiveClickAction.value,\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nconst BORDER_SIDE_IDS: ButtonBorderSides[] = [\n \"all\",\n \"top\",\n \"right\",\n \"bottom\",\n \"left\",\n];\n\nconst borderSideOptions = computed(() =>\n BORDER_SIDE_IDS.map((id) => ({\n id,\n label:\n t.button.borderSidesLabels[id as keyof typeof t.button.borderSidesLabels],\n })),\n);\n\nfunction setClickActionFromSelect(ev: Event): void {\n const value = (ev.target as HTMLSelectElement).value;\n const action = (value === \"\" ? \"none\" : value) as ButtonClickAction;\n const patch: Partial<ButtonBlock> = {};\n if (action === \"none\") {\n patch.clickAction = undefined;\n patch.clickActionAnchorId = undefined;\n } else {\n patch.clickAction = action;\n if (action !== \"scroll_anchor\") {\n patch.clickActionAnchorId = undefined;\n }\n }\n emit(\"update\", patch);\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\n <div class=\"tpl:mb-3.5\">\n <label\n :class=\"[\n labelClass,\n 'tpl:!mb-1 tpl:!text-xs tpl:!font-semibold tpl:!uppercase tpl:!tracking-wide',\n ]\"\n >\n {{ t.button.actionOnClick }}\n </label>\n <select\n :class=\"inputClass\"\n :value=\"selectActionModel\"\n @change=\"setClickActionFromSelect\"\n >\n <option value=\"\">{{ t.button.clickActionNone }}</option>\n <option value=\"next_step\">{{ t.button.clickActionNextStep }}</option>\n <option value=\"previous_step\">\n {{ t.button.clickActionPreviousStep }}\n </option>\n <option value=\"load_page\">{{ t.button.clickActionLoadPage }}</option>\n <option value=\"scroll_anchor\">\n {{ t.button.clickActionScrollAnchor }}\n </option>\n <option value=\"close_popup\">{{ t.button.clickActionClosePopup }}</option>\n </select>\n </div>\n\n <div v-if=\"effectiveClickAction === 'scroll_anchor'\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.anchorId }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.clickActionAnchorId ?? ''\"\n :placeholder=\"t.button.anchorIdPlaceholder\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=\"\n updateField(\n 'clickActionAnchorId',\n ($event.target as HTMLInputElement).value.trim() || undefined,\n )\n \"\n />\n </div>\n\n <div v-if=\"showLinkUrlFields\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ urlFieldLabel }}</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\n <div class=\"tpl:flex tpl:flex-col tpl:gap-3\">\n <div class=\"tpl:w-full tpl:min-w-0\">\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:w-full tpl:min-w-0\">\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\n <div class=\"tpl:mt-3 tpl:flex tpl:flex-col tpl:gap-3\">\n <p\n :class=\"[\n labelClass,\n 'tpl:!mb-0 tpl:!text-xs tpl:!font-semibold tpl:!uppercase tpl:!tracking-wide',\n ]\"\n >\n {{ t.button.border }}\n </p>\n <div class=\"tpl:w-full tpl:min-w-0\">\n <label :class=\"labelClass\">{{ t.button.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor ?? '#000000'\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth ?? 0\"\n min=\"0\"\n max=\"20\"\n @input=\"\n updateField(\n 'borderWidth',\n Math.max(\n 0,\n Number(($event.target as HTMLInputElement).value) || 0,\n ),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderSides }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.borderSides ?? 'all'\"\n @change=\"\n updateField(\n 'borderSides',\n ($event.target as HTMLSelectElement).value as ButtonBorderSides,\n )\n \"\n >\n <option v-for=\"opt in borderSideOptions\" :key=\"opt.id\" :value=\"opt.id\">\n {{ opt.label }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"tpl:mt-3 tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\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>\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 { computed } from \"vue\";\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 {\n ButtonBlock,\n ButtonBorderSides,\n ButtonClickAction,\n} from \"@aswin.dev/types\";\n\nconst props = defineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst effectiveClickAction = computed(\n (): ButtonClickAction => props.block.clickAction ?? \"none\",\n);\n\nconst showLinkUrlFields = computed(() => {\n const a = effectiveClickAction.value;\n return a === \"none\" || a === \"load_page\";\n});\n\nconst urlFieldLabel = computed(() =>\n effectiveClickAction.value === \"load_page\" ? t.button.pageUrl : t.button.url,\n);\n\nconst selectActionModel = computed(() =>\n effectiveClickAction.value === \"none\" ? \"\" : effectiveClickAction.value,\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n\nconst BORDER_SIDE_IDS: ButtonBorderSides[] = [\n \"all\",\n \"top\",\n \"right\",\n \"bottom\",\n \"left\",\n];\n\nconst borderSideOptions = computed(() =>\n BORDER_SIDE_IDS.map((id) => ({\n id,\n label:\n t.button.borderSidesLabels[id as keyof typeof t.button.borderSidesLabels],\n })),\n);\n\nfunction setClickActionFromSelect(ev: Event): void {\n const value = (ev.target as HTMLSelectElement).value;\n const action = (value === \"\" ? \"none\" : value) as ButtonClickAction;\n const patch: Partial<ButtonBlock> = {};\n if (action === \"none\") {\n patch.clickAction = undefined;\n patch.clickActionAnchorId = undefined;\n } else {\n patch.clickAction = action;\n if (action !== \"scroll_anchor\") {\n patch.clickActionAnchorId = undefined;\n }\n }\n emit(\"update\", patch);\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\n <div class=\"tpl:mb-3.5\">\n <label\n :class=\"[\n labelClass,\n 'tpl:!mb-1 tpl:!text-xs tpl:!font-semibold tpl:!uppercase tpl:!tracking-wide',\n ]\"\n >\n {{ t.button.actionOnClick }}\n </label>\n <select\n :class=\"inputClass\"\n :value=\"selectActionModel\"\n @change=\"setClickActionFromSelect\"\n >\n <option value=\"\">{{ t.button.clickActionNone }}</option>\n <option value=\"next_step\">{{ t.button.clickActionNextStep }}</option>\n <option value=\"previous_step\">\n {{ t.button.clickActionPreviousStep }}\n </option>\n <option value=\"load_page\">{{ t.button.clickActionLoadPage }}</option>\n <option value=\"scroll_anchor\">\n {{ t.button.clickActionScrollAnchor }}\n </option>\n <option value=\"close_popup\">{{ t.button.clickActionClosePopup }}</option>\n </select>\n </div>\n\n <div v-if=\"effectiveClickAction === 'scroll_anchor'\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.anchorId }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.clickActionAnchorId ?? ''\"\n :placeholder=\"t.button.anchorIdPlaceholder\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=\"\n updateField(\n 'clickActionAnchorId',\n ($event.target as HTMLInputElement).value.trim() || undefined,\n )\n \"\n />\n </div>\n\n <div v-if=\"showLinkUrlFields\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ urlFieldLabel }}</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\n <div class=\"tpl:flex tpl:flex-col tpl:gap-3\">\n <div class=\"tpl:w-full tpl:min-w-0\">\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:w-full tpl:min-w-0\">\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\n <div class=\"tpl:mt-3 tpl:flex tpl:flex-col tpl:gap-3\">\n <p\n :class=\"[\n labelClass,\n 'tpl:!mb-0 tpl:!text-xs tpl:!font-semibold tpl:!uppercase tpl:!tracking-wide',\n ]\"\n >\n {{ t.button.border }}\n </p>\n <div class=\"tpl:w-full tpl:min-w-0\">\n <label :class=\"labelClass\">{{ t.button.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor ?? '#000000'\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth ?? 0\"\n min=\"0\"\n max=\"20\"\n @input=\"\n updateField(\n 'borderWidth',\n Math.max(\n 0,\n Number(($event.target as HTMLInputElement).value) || 0,\n ),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderSides }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.borderSides ?? 'all'\"\n @change=\"\n updateField(\n 'borderSides',\n ($event.target as HTMLSelectElement).value as ButtonBorderSides,\n )\n \"\n >\n <option v-for=\"opt in borderSideOptions\" :key=\"opt.id\" :value=\"opt.id\">\n {{ opt.label }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"tpl:mt-3 tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\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>\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 { 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 \"@aswin.dev/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: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\";\n}[] = [\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 \"@aswin.dev/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: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\";\n}[] = [\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 { computed, ref } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type {\n ButtonBorderSides,\n InputBlock,\n InputFieldType,\n SpacingValue,\n} from \"@aswin.dev/types\";\n\nconst props = defineProps<{\n block: InputBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<InputBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst openLabelSection = ref(true);\nconst openInputSection = ref(true);\n\nfunction updateField<K extends keyof InputBlock>(\n field: K,\n value: InputBlock[K],\n): void {\n emit(\"update\", { [field]: value } as Partial<InputBlock>);\n}\n\nconst BORDER_SIDE_IDS: ButtonBorderSides[] = [\n \"all\",\n \"top\",\n \"right\",\n \"bottom\",\n \"left\",\n];\n\nconst borderSideOptions = BORDER_SIDE_IDS.map((id) => ({\n id,\n label:\n t.button.borderSidesLabels[id as keyof typeof t.button.borderSidesLabels],\n}));\n\nconst inputTypes: { value: InputFieldType; label: string }[] = [\n { value: \"text\", label: t.input.types.text },\n { value: \"email\", label: t.input.types.email },\n { value: \"date\", label: t.input.types.date },\n];\n\nfunction patchSpacing<\n K extends \"labelMargin\" | \"labelPadding\" | \"inputMargin\" | \"inputPadding\",\n>(key: K, value: SpacingValue): void {\n emit(\"update\", { [key]: value } as Partial<InputBlock>);\n}\n\nconst widthMode = computed(() =>\n props.block.inputWidth === \"full\" ? \"full\" : \"fixed\",\n);\n\nfunction setWidthMode(ev: Event): void {\n const v = (ev.target as HTMLSelectElement).value;\n if (v === \"full\") {\n updateField(\"inputWidth\", \"full\");\n } else {\n const n =\n typeof props.block.inputWidth === \"number\" ? props.block.inputWidth : 280;\n updateField(\"inputWidth\", n);\n }\n}\n\nfunction toggleLabelSection(): void {\n openLabelSection.value = !openLabelSection.value;\n}\n\nfunction toggleInputSection(): void {\n openInputSection.value = !openInputSection.value;\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fieldType }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputType\"\n @change=\"\n updateField(\n 'inputType',\n ($event.target as HTMLSelectElement).value as InputFieldType,\n )\n \"\n >\n <option v-for=\"opt in inputTypes\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </select>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.name }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.name\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=\"\n updateField('name', ($event.target as HTMLInputElement).value.trim())\n \"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.label }}</label>\n <MergeTagInput\n :model-value=\"block.label\"\n type=\"text\"\n @update:model-value=\"updateField('label', $event)\"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.placeholder }}</label>\n <MergeTagInput\n :model-value=\"block.placeholder\"\n type=\"text\"\n @update:model-value=\"updateField('placeholder', $event)\"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.defaultValue }}</label>\n <MergeTagInput\n :model-value=\"block.defaultValue ?? ''\"\n type=\"text\"\n @update:model-value=\"updateField('defaultValue', $event)\"\n />\n </div>\n\n <label\n class=\"tpl:mb-3.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[13px] 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=\"block.required ?? false\"\n @change=\"\n updateField('required', ($event.target as HTMLInputElement).checked)\n \"\n />\n {{ t.input.required }}\n </label>\n\n <CollapsibleSection\n :title=\"t.input.labelAppearance\"\n :open=\"openLabelSection\"\n no-border\n @toggle=\"toggleLabelSection\"\n >\n <SpacingControl\n :model-value=\"block.labelMargin\"\n :label=\"t.input.labelMargin\"\n @update:model-value=\"patchSpacing('labelMargin', $event)\"\n />\n <SpacingControl\n :model-value=\"block.labelPadding\"\n :label=\"t.input.labelPadding\"\n @update:model-value=\"patchSpacing('labelPadding', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelFontFamily || ''\"\n @change=\"\n updateField(\n 'labelFontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.input.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.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.labelFontSize\"\n min=\"8\"\n max=\"48\"\n @input=\"\n updateField(\n 'labelFontSize',\n Number(($event.target as HTMLInputElement).value) || 14,\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.textColor }}</label>\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fontWeight }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelFontWeight\"\n @change=\"\n updateField(\n 'labelFontWeight',\n ($event.target as HTMLSelectElement).value as 'normal' | 'bold',\n )\n \"\n >\n <option value=\"normal\">{{ t.input.weightNormal }}</option>\n <option value=\"bold\">{{ t.input.weightBold }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.menu.textAlign }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelTextAlign\"\n @change=\"\n updateField(\n 'labelTextAlign',\n ($event.target as HTMLSelectElement).value as\n | 'left'\n | 'center'\n | 'right',\n )\n \"\n >\n <option value=\"left\">{{ t.input.alignLeft }}</option>\n <option value=\"center\">{{ t.input.alignCenter }}</option>\n <option value=\"right\">{{ t.input.alignRight }}</option>\n </select>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.input.inputAppearance\"\n :open=\"openInputSection\"\n @toggle=\"toggleInputSection\"\n >\n <SpacingControl\n :model-value=\"block.inputMargin\"\n :label=\"t.input.inputMargin\"\n @update:model-value=\"patchSpacing('inputMargin', $event)\"\n />\n <SpacingControl\n :model-value=\"block.inputPadding\"\n :label=\"t.input.inputPadding\"\n @update:model-value=\"patchSpacing('inputPadding', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fieldWidth }}</label>\n <select :class=\"inputClass\" :value=\"widthMode\" @change=\"setWidthMode\">\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"fixed\">{{ t.input.fixedWidth }}</option>\n </select>\n </div>\n <div v-if=\"block.inputWidth !== 'full'\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"typeof block.inputWidth === 'number' ? block.inputWidth : 280\"\n min=\"80\"\n max=\"1200\"\n @input=\"\n updateField(\n 'inputWidth',\n Math.max(\n 80,\n Number(($event.target as HTMLInputElement).value) || 280,\n ),\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.input.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputFontFamily || ''\"\n @change=\"\n updateField(\n 'inputFontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.input.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 tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.inputBackgroundColor\"\n @update:model-value=\"updateField('inputBackgroundColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.inputTextColor\"\n @update:model-value=\"updateField('inputTextColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.input.placeholderColor }}</label>\n <ColorPicker\n :model-value=\"block.inputPlaceholderColor\"\n @update:model-value=\"updateField('inputPlaceholderColor', $event)\"\n />\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.inputFontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'inputFontSize',\n Number(($event.target as HTMLInputElement).value) || 15,\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.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.inputBorderRadius\"\n min=\"0\"\n max=\"40\"\n @input=\"\n updateField(\n 'inputBorderRadius',\n Number(($event.target as HTMLInputElement).value) || 0,\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.border }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\n <label :class=\"labelClass\">{{ t.button.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.inputBorderColor ?? '#d1d5db'\"\n @update:model-value=\"updateField('inputBorderColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.inputBorderWidth ?? 0\"\n min=\"0\"\n max=\"20\"\n @input=\"\n updateField(\n 'inputBorderWidth',\n Math.max(\n 0,\n Number(($event.target as HTMLInputElement).value) || 0,\n ),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderSides }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputBorderSides ?? 'all'\"\n @change=\"\n updateField(\n 'inputBorderSides',\n ($event.target as HTMLSelectElement).value as ButtonBorderSides,\n )\n \"\n >\n <option\n v-for=\"opt in borderSideOptions\"\n :key=\"opt.id\"\n :value=\"opt.id\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n </CollapsibleSection>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type {\n ButtonBorderSides,\n InputBlock,\n InputFieldType,\n SpacingValue,\n} from \"@aswin.dev/types\";\n\nconst props = defineProps<{\n block: InputBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<InputBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst openLabelSection = ref(true);\nconst openInputSection = ref(true);\n\nfunction updateField<K extends keyof InputBlock>(\n field: K,\n value: InputBlock[K],\n): void {\n emit(\"update\", { [field]: value } as Partial<InputBlock>);\n}\n\nconst BORDER_SIDE_IDS: ButtonBorderSides[] = [\n \"all\",\n \"top\",\n \"right\",\n \"bottom\",\n \"left\",\n];\n\nconst borderSideOptions = BORDER_SIDE_IDS.map((id) => ({\n id,\n label:\n t.button.borderSidesLabels[id as keyof typeof t.button.borderSidesLabels],\n}));\n\nconst inputTypes: { value: InputFieldType; label: string }[] = [\n { value: \"text\", label: t.input.types.text },\n { value: \"email\", label: t.input.types.email },\n { value: \"date\", label: t.input.types.date },\n];\n\nfunction patchSpacing<\n K extends \"labelMargin\" | \"labelPadding\" | \"inputMargin\" | \"inputPadding\",\n>(key: K, value: SpacingValue): void {\n emit(\"update\", { [key]: value } as Partial<InputBlock>);\n}\n\nconst widthMode = computed(() =>\n props.block.inputWidth === \"full\" ? \"full\" : \"fixed\",\n);\n\nfunction setWidthMode(ev: Event): void {\n const v = (ev.target as HTMLSelectElement).value;\n if (v === \"full\") {\n updateField(\"inputWidth\", \"full\");\n } else {\n const n =\n typeof props.block.inputWidth === \"number\" ? props.block.inputWidth : 280;\n updateField(\"inputWidth\", n);\n }\n}\n\nfunction toggleLabelSection(): void {\n openLabelSection.value = !openLabelSection.value;\n}\n\nfunction toggleInputSection(): void {\n openInputSection.value = !openInputSection.value;\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fieldType }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputType\"\n @change=\"\n updateField(\n 'inputType',\n ($event.target as HTMLSelectElement).value as InputFieldType,\n )\n \"\n >\n <option v-for=\"opt in inputTypes\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </option>\n </select>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.name }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.name\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=\"\n updateField('name', ($event.target as HTMLInputElement).value.trim())\n \"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.label }}</label>\n <MergeTagInput\n :model-value=\"block.label\"\n type=\"text\"\n @update:model-value=\"updateField('label', $event)\"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.placeholder }}</label>\n <MergeTagInput\n :model-value=\"block.placeholder\"\n type=\"text\"\n @update:model-value=\"updateField('placeholder', $event)\"\n />\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.defaultValue }}</label>\n <MergeTagInput\n :model-value=\"block.defaultValue ?? ''\"\n type=\"text\"\n @update:model-value=\"updateField('defaultValue', $event)\"\n />\n </div>\n\n <label\n class=\"tpl:mb-3.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[13px] 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=\"block.required ?? false\"\n @change=\"\n updateField('required', ($event.target as HTMLInputElement).checked)\n \"\n />\n {{ t.input.required }}\n </label>\n\n <CollapsibleSection\n :title=\"t.input.labelAppearance\"\n :open=\"openLabelSection\"\n no-border\n @toggle=\"toggleLabelSection\"\n >\n <SpacingControl\n :model-value=\"block.labelMargin\"\n :label=\"t.input.labelMargin\"\n @update:model-value=\"patchSpacing('labelMargin', $event)\"\n />\n <SpacingControl\n :model-value=\"block.labelPadding\"\n :label=\"t.input.labelPadding\"\n @update:model-value=\"patchSpacing('labelPadding', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelFontFamily || ''\"\n @change=\"\n updateField(\n 'labelFontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.input.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.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.labelFontSize\"\n min=\"8\"\n max=\"48\"\n @input=\"\n updateField(\n 'labelFontSize',\n Number(($event.target as HTMLInputElement).value) || 14,\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.textColor }}</label>\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fontWeight }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelFontWeight\"\n @change=\"\n updateField(\n 'labelFontWeight',\n ($event.target as HTMLSelectElement).value as 'normal' | 'bold',\n )\n \"\n >\n <option value=\"normal\">{{ t.input.weightNormal }}</option>\n <option value=\"bold\">{{ t.input.weightBold }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.menu.textAlign }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.labelTextAlign\"\n @change=\"\n updateField(\n 'labelTextAlign',\n ($event.target as HTMLSelectElement).value as\n | 'left'\n | 'center'\n | 'right',\n )\n \"\n >\n <option value=\"left\">{{ t.input.alignLeft }}</option>\n <option value=\"center\">{{ t.input.alignCenter }}</option>\n <option value=\"right\">{{ t.input.alignRight }}</option>\n </select>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.input.inputAppearance\"\n :open=\"openInputSection\"\n @toggle=\"toggleInputSection\"\n >\n <SpacingControl\n :model-value=\"block.inputMargin\"\n :label=\"t.input.inputMargin\"\n @update:model-value=\"patchSpacing('inputMargin', $event)\"\n />\n <SpacingControl\n :model-value=\"block.inputPadding\"\n :label=\"t.input.inputPadding\"\n @update:model-value=\"patchSpacing('inputPadding', $event)\"\n />\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.input.fieldWidth }}</label>\n <select :class=\"inputClass\" :value=\"widthMode\" @change=\"setWidthMode\">\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"fixed\">{{ t.input.fixedWidth }}</option>\n </select>\n </div>\n <div v-if=\"block.inputWidth !== 'full'\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"typeof block.inputWidth === 'number' ? block.inputWidth : 280\"\n min=\"80\"\n max=\"1200\"\n @input=\"\n updateField(\n 'inputWidth',\n Math.max(\n 80,\n Number(($event.target as HTMLInputElement).value) || 280,\n ),\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.input.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputFontFamily || ''\"\n @change=\"\n updateField(\n 'inputFontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.input.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 tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.inputBackgroundColor\"\n @update:model-value=\"updateField('inputBackgroundColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.inputTextColor\"\n @update:model-value=\"updateField('inputTextColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.input.placeholderColor }}</label>\n <ColorPicker\n :model-value=\"block.inputPlaceholderColor\"\n @update:model-value=\"updateField('inputPlaceholderColor', $event)\"\n />\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.inputFontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'inputFontSize',\n Number(($event.target as HTMLInputElement).value) || 15,\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.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.inputBorderRadius\"\n min=\"0\"\n max=\"40\"\n @input=\"\n updateField(\n 'inputBorderRadius',\n Number(($event.target as HTMLInputElement).value) || 0,\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.border }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-3\">\n <div>\n <label :class=\"labelClass\">{{ t.button.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.inputBorderColor ?? '#d1d5db'\"\n @update:model-value=\"updateField('inputBorderColor', $event)\"\n />\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.inputBorderWidth ?? 0\"\n min=\"0\"\n max=\"20\"\n @input=\"\n updateField(\n 'inputBorderWidth',\n Math.max(\n 0,\n Number(($event.target as HTMLInputElement).value) || 0,\n ),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div>\n <label :class=\"labelClass\">{{ t.button.borderSides }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.inputBorderSides ?? 'all'\"\n @change=\"\n updateField(\n 'inputBorderSides',\n ($event.target as HTMLSelectElement).value as ButtonBorderSides,\n )\n \"\n >\n <option\n v-for=\"opt in borderSideOptions\"\n :key=\"opt.id\"\n :value=\"opt.id\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n </CollapsibleSection>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/core\";\nimport type { CustomBlock, CustomBlockField } from \"@aswin.dev/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 \"@aswin.dev/core\";\nimport type { CustomBlock, CustomBlockField } from \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@aswin.dev/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 :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <label\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.decorative === true\"\n @change=\"\n updateField('decorative', ($event.target as HTMLInputElement).checked)\n \"\n />\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </label>\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 \"@aswin.dev/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@aswin.dev/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 :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <label\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.decorative === true\"\n @change=\"\n updateField('decorative', ($event.target as HTMLInputElement).checked)\n \"\n />\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </label>\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 \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\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 \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\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 \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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 \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 { TableBlock, TableCellData, TableRowData } from \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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 { TableBlock, TableCellData, TableRowData } from \"@aswin.dev/types\";\nimport { generateId } from \"@aswin.dev/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 \"@aswin.dev/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 \"@aswin.dev/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 MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@aswin.dev/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@aswin.dev/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\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <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.video.openInNewTab }}\n </label>\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all 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 class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@aswin.dev/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@aswin.dev/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\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <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.video.openInNewTab }}\n </label>\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all 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 class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nimport InputToolbar from \"./toolbar/InputToolbar.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n InputBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@aswin.dev/types\";\nimport { isCustomBlock } from \"@aswin.dev/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 <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <InputToolbar\n v-else-if=\"blockType === 'input'\"\n :block=\"block as InputBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nimport InputToolbar from \"./toolbar/InputToolbar.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n InputBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@aswin.dev/types\";\nimport { isCustomBlock } from \"@aswin.dev/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 <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <InputToolbar\n v-else-if=\"blockType === 'input'\"\n :block=\"block as InputBlock\"\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 { ACCESSIBILITY_LINT_KEY } from \"../keys\";\nimport type { Block, TemplateSettings } from \"@aswin.dev/types\";\nimport { Accessibility, LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, inject, ref, watch } from \"vue\";\n\nconst AccessibilityPanel = defineAsyncComponent(\n () => import(\"./sidebar/AccessibilityPanel.vue\"),\n);\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\" | \"accessibility\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst lint = inject(ACCESSIBILITY_LINT_KEY, null);\nconst a11yEnabled = computed(() => lint !== null);\nconst a11yIssueCount = computed(() => lint?.issues.value.length ?? 0);\n\nfunction tabClass(tab: Tab): string {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return \"tpl:flex-1 tpl:text-[var(--tpl-primary)]\";\n }\n return \"tpl:shrink-0 tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\";\n}\n\nfunction tabStyle(tab: Tab): Record<string, string> {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return {\n backgroundColor: \"var(--tpl-bg)\",\n boxShadow: \"var(--tpl-shadow-md)\",\n };\n }\n return { backgroundColor: \"transparent\" };\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:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-active)] tpl:p-1.5\"\n >\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n :aria-label=\"t.sidebar.content\"\n :title=\"t.sidebar.content\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('content')\"\n :style=\"tabStyle('content')\"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n <span v-if=\"activeTab === 'content'\">{{ t.sidebar.content }}</span>\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n :aria-label=\"t.sidebar.settings\"\n :title=\"t.sidebar.settings\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('settings')\"\n :style=\"tabStyle('settings')\"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'settings'\">{{ t.sidebar.settings }}</span>\n </button>\n <button\n v-if=\"a11yEnabled\"\n id=\"tpl-tab-accessibility\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'accessibility'\"\n aria-controls=\"tpl-tabpanel-accessibility\"\n :aria-label=\"t.accessibility.panelTabLabel\"\n :title=\"t.accessibility.panelTabLabel\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('accessibility')\"\n :style=\"tabStyle('accessibility')\"\n @click=\"activeTab = 'accessibility'\"\n >\n <Accessibility :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'accessibility'\">\n {{ t.accessibility.panelTabLabel }}\n </span>\n <span\n v-if=\"a11yIssueCount > 0\"\n class=\"tpl:ml-1 tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-1.5 tpl:text-[10px]\"\n >\n {{ a11yIssueCount }}\n </span>\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\n <div\n v-if=\"activeTab === 'accessibility' && a11yEnabled\"\n id=\"tpl-tabpanel-accessibility\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-accessibility\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <AccessibilityPanel />\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 { ACCESSIBILITY_LINT_KEY } from \"../keys\";\nimport type { Block, TemplateSettings } from \"@aswin.dev/types\";\nimport { Accessibility, LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, inject, ref, watch } from \"vue\";\n\nconst AccessibilityPanel = defineAsyncComponent(\n () => import(\"./sidebar/AccessibilityPanel.vue\"),\n);\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\" | \"accessibility\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst lint = inject(ACCESSIBILITY_LINT_KEY, null);\nconst a11yEnabled = computed(() => lint !== null);\nconst a11yIssueCount = computed(() => lint?.issues.value.length ?? 0);\n\nfunction tabClass(tab: Tab): string {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return \"tpl:flex-1 tpl:text-[var(--tpl-primary)]\";\n }\n return \"tpl:shrink-0 tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\";\n}\n\nfunction tabStyle(tab: Tab): Record<string, string> {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return {\n backgroundColor: \"var(--tpl-bg)\",\n boxShadow: \"var(--tpl-shadow-md)\",\n };\n }\n return { backgroundColor: \"transparent\" };\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:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-active)] tpl:p-1.5\"\n >\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n :aria-label=\"t.sidebar.content\"\n :title=\"t.sidebar.content\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('content')\"\n :style=\"tabStyle('content')\"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n <span v-if=\"activeTab === 'content'\">{{ t.sidebar.content }}</span>\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n :aria-label=\"t.sidebar.settings\"\n :title=\"t.sidebar.settings\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('settings')\"\n :style=\"tabStyle('settings')\"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'settings'\">{{ t.sidebar.settings }}</span>\n </button>\n <button\n v-if=\"a11yEnabled\"\n id=\"tpl-tab-accessibility\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'accessibility'\"\n aria-controls=\"tpl-tabpanel-accessibility\"\n :aria-label=\"t.accessibility.panelTabLabel\"\n :title=\"t.accessibility.panelTabLabel\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('accessibility')\"\n :style=\"tabStyle('accessibility')\"\n @click=\"activeTab = 'accessibility'\"\n >\n <Accessibility :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'accessibility'\">\n {{ t.accessibility.panelTabLabel }}\n </span>\n <span\n v-if=\"a11yIssueCount > 0\"\n class=\"tpl:ml-1 tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-1.5 tpl:text-[10px]\"\n >\n {{ a11yIssueCount }}\n </span>\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\n <div\n v-if=\"activeTab === 'accessibility' && a11yEnabled\"\n id=\"tpl-tabpanel-accessibility\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-accessibility\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <AccessibilityPanel />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { Undo2, Redo2 } from \"@lucide/vue\";\n\nimport { HISTORY_KEY } from \"../keys\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n /** Cloud: show collaboration undo toast before applying undo (matches keyboard shortcut). */\n onBeforeUndo?: () => void;\n}>();\n\nconst history = inject(HISTORY_KEY, null);\nconst { t } = useI18n();\n\nconst canUndo = computed(() => history?.canUndo.value ?? false);\nconst canRedo = computed(() => history?.canRedo.value ?? false);\n\nfunction undo(): void {\n if (!history || !canUndo.value) return;\n props.onBeforeUndo?.();\n history.undo();\n}\n\nfunction redo(): void {\n if (!history || !canRedo.value) return;\n history.redo();\n}\n</script>\n\n<template>\n <div\n v-if=\"history\"\n class=\"tpl:flex tpl:items-center tpl:gap-0.5\"\n role=\"group\"\n :aria-label=\"`${t.history.undo} / ${t.history.redo}`\"\n >\n <button\n type=\"button\"\n class=\"tpl-history-btn 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:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150\"\n :disabled=\"!canUndo\"\n :aria-label=\"t.history.undo\"\n :title=\"t.history.undo\"\n @click=\"undo\"\n >\n <Undo2 :size=\"18\" :stroke-width=\"1.5\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl-history-btn 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:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150\"\n :disabled=\"!canRedo\"\n :aria-label=\"t.history.redo\"\n :title=\"t.history.redo\"\n @click=\"redo\"\n >\n <Redo2 :size=\"18\" :stroke-width=\"1.5\" />\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-history-btn:hover:not(:disabled) {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-history-btn:active:not(:disabled) {\n transform: scale(0.92);\n}\n\n.tpl-history-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport { Undo2, Redo2 } from \"@lucide/vue\";\n\nimport { HISTORY_KEY } from \"../keys\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n /** Cloud: show collaboration undo toast before applying undo (matches keyboard shortcut). */\n onBeforeUndo?: () => void;\n}>();\n\nconst history = inject(HISTORY_KEY, null);\nconst { t } = useI18n();\n\nconst canUndo = computed(() => history?.canUndo.value ?? false);\nconst canRedo = computed(() => history?.canRedo.value ?? false);\n\nfunction undo(): void {\n if (!history || !canUndo.value) return;\n props.onBeforeUndo?.();\n history.undo();\n}\n\nfunction redo(): void {\n if (!history || !canRedo.value) return;\n history.redo();\n}\n</script>\n\n<template>\n <div\n v-if=\"history\"\n class=\"tpl:flex tpl:items-center tpl:gap-0.5\"\n role=\"group\"\n :aria-label=\"`${t.history.undo} / ${t.history.redo}`\"\n >\n <button\n type=\"button\"\n class=\"tpl-history-btn 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:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150\"\n :disabled=\"!canUndo\"\n :aria-label=\"t.history.undo\"\n :title=\"t.history.undo\"\n @click=\"undo\"\n >\n <Undo2 :size=\"18\" :stroke-width=\"1.5\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl-history-btn 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:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150\"\n :disabled=\"!canRedo\"\n :aria-label=\"t.history.redo\"\n :title=\"t.history.redo\"\n @click=\"redo\"\n >\n <Redo2 :size=\"18\" :stroke-width=\"1.5\" />\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-history-btn:hover:not(:disabled) {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-history-btn:active:not(:disabled) {\n transform: scale(0.92);\n}\n\n.tpl-history-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@aswin.dev/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 \"@aswin.dev/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","/**\n * Guided tour types for Templatical Editor `init()` / `initCloud()`.\n */\n\n/** Reserved tooltip placement hints for layout inside the viewport. */\nexport type EditorTourStepPlacement =\n | \"canvas\"\n | \"sidebar\"\n | \"rightSidebar\"\n | \"below\";\n\nexport interface EditorTourStep {\n /** Any valid `document.querySelector` selector (editor internals or host DOM). */\n target: string;\n title: string;\n text: string;\n placement?: EditorTourStepPlacement;\n /**\n * When the target is absent, skip this step. Default `true`.\n * Set `false` to keep the step even if the node is missing (rect will be null — avoided).\n */\n skipIfMissing?: boolean;\n}\n\nexport interface EditorTourConfig {\n /**\n * Steps to show in order. Omit to use built-in defaults (canvas, block rail, right sidebar).\n */\n steps?: EditorTourStep[];\n /** Start automatically after mount when the tour has not been dismissed. Default `false`. */\n autoStart?: boolean;\n /**\n * `localStorage` key for dismissal persistence.\n * Set to `false` to disable persistence (tour can restart every load).\n * Default key is `DEFAULT_EDITOR_TOUR_STORAGE_KEY` when omitted.\n */\n storageKey?: string | false;\n}\n\nexport interface EditorTourStartOptions {\n /** Zero-based index into the active (filtered) step list */\n stepIndex?: number;\n}\n\nexport const DEFAULT_EDITOR_TOUR_STORAGE_KEY =\n \"templatical-editor-tour-dismissed\";\n","import type { EditorTourConfig } from \"../types/editor-tour\";\nimport { DEFAULT_EDITOR_TOUR_STORAGE_KEY } from \"../types/editor-tour\";\n\nexport function resolveTourStorageKey(\n storageKey: EditorTourConfig[\"storageKey\"],\n): string | null {\n if (storageKey === false) return null;\n return storageKey ?? DEFAULT_EDITOR_TOUR_STORAGE_KEY;\n}\n\nfunction safeGetItem(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction safeSetItem(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n /* ignore quota / private mode */\n }\n}\n\nfunction safeRemoveItem(key: string): void {\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n\n/** Whether the user has dismissed the tour (only when persistence is enabled). */\nexport function readTourDismissedState(\n tour: EditorTourConfig | undefined,\n): boolean {\n if (!tour) return true;\n const key = resolveTourStorageKey(tour.storageKey);\n if (key === null) return false;\n const raw = safeGetItem(key);\n if (raw === null) return false;\n try {\n return JSON.parse(raw) === true;\n } catch {\n return raw === \"true\";\n }\n}\n\nexport function writeTourDismissedState(\n tour: EditorTourConfig | undefined,\n): void {\n if (!tour) return;\n const key = resolveTourStorageKey(tour.storageKey);\n if (key === null) return;\n safeSetItem(key, JSON.stringify(true));\n}\n\nexport function clearTourDismissedState(\n tour: EditorTourConfig | undefined,\n): void {\n if (!tour) return;\n const key = resolveTourStorageKey(tour.storageKey);\n if (key === null) return;\n safeRemoveItem(key);\n}\n","<script setup lang=\"ts\">\nimport { ChevronRight } from \"@lucide/vue\";\nimport {\n useIntervalFn,\n usePreferredReducedMotion,\n useScrollLock,\n} from \"@vueuse/core\";\nimport {\n computed,\n nextTick,\n onMounted,\n onUnmounted,\n ref,\n shallowRef,\n watch,\n} from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n EditorTourConfig,\n EditorTourStartOptions,\n EditorTourStep,\n EditorTourStepPlacement,\n} from \"../types/editor-tour\";\nimport {\n clearTourDismissedState,\n readTourDismissedState,\n writeTourDismissedState,\n} from \"../utils/tourStorage\";\n\nconst props = withDefaults(\n defineProps<{\n tourConfig: EditorTourConfig;\n /**\n * Align tooltip palette with the editor chrome (Teleport is outside `.tpl`,\n * so theme CSS variables do not apply unless we mirror light/dark here).\n */\n darkMode?: boolean;\n }>(),\n { darkMode: false },\n);\n\nconst { t, format } = useI18n();\n\nconst tourActive = ref(false);\nconst stepIndex = ref(0);\nconst targetRect = ref<DOMRect | null>(null);\nconst typedText = ref(\"\");\nconst prefersReducedMotion = usePreferredReducedMotion();\nconst activeSteps = shallowRef<EditorTourStep[]>([]);\n\nconst bodyScrollLocked = useScrollLock(document.body);\n\nwatch(\n tourActive,\n (active, _was, onCleanup) => {\n bodyScrollLocked.value = active;\n if (!active) return;\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n dismiss(true);\n }\n };\n document.addEventListener(\"keydown\", onKey, true);\n onCleanup(() => document.removeEventListener(\"keydown\", onKey, true));\n },\n { immediate: true },\n);\n\nlet typewriterCharIndex = 0;\nconst { pause: stopTypewriter, resume: resumeTypewriter } = useIntervalFn(\n () => {\n const fullText = currentStepText.value;\n if (typewriterCharIndex < fullText.length) {\n typedText.value = fullText.slice(0, typewriterCharIndex + 1);\n typewriterCharIndex++;\n } else {\n stopTypewriter();\n }\n },\n 25,\n { immediate: false },\n);\n\nconst defaultSteps = computed<EditorTourStep[]>(() => [\n {\n target: \".tpl-body\",\n placement: \"canvas\",\n title: t.tour.defaults.canvas.title,\n text: t.tour.defaults.canvas.text,\n },\n {\n target: \".tpl-sidebar-rail\",\n placement: \"sidebar\",\n title: t.tour.defaults.sidebar.title,\n text: t.tour.defaults.sidebar.text,\n },\n {\n target: \".tpl-right-sidebar\",\n placement: \"rightSidebar\",\n title: t.tour.defaults.rightSidebar.title,\n text: t.tour.defaults.rightSidebar.text,\n },\n]);\n\nfunction filterResolvableSteps(steps: EditorTourStep[]): EditorTourStep[] {\n return steps.filter((s) => {\n const el = document.querySelector(s.target);\n if (el) return true;\n return s.skipIfMissing === false;\n });\n}\n\nconst currentStep = computed(() => activeSteps.value[stepIndex.value] ?? null);\n\nconst currentStepTitle = computed(() => currentStep.value?.title ?? \"\");\nconst currentStepText = computed(() => currentStep.value?.text ?? \"\");\n\nfunction resolvePlacement(\n step: EditorTourStep | null,\n): EditorTourStepPlacement {\n return step?.placement ?? \"below\";\n}\n\nfunction updateTargetRect(): void {\n const step = currentStep.value;\n if (!step) {\n targetRect.value = null;\n return;\n }\n const el = document.querySelector<HTMLElement>(step.target);\n if (!el) {\n targetRect.value = null;\n return;\n }\n const placement = resolvePlacement(step);\n if (placement === \"below\") {\n el.scrollIntoView({\n behavior: \"instant\",\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n targetRect.value = el.getBoundingClientRect();\n}\n\nfunction startTypewriter(): void {\n stopTypewriter();\n const fullText = currentStepText.value;\n typedText.value = \"\";\n if (prefersReducedMotion.value === \"reduce\") {\n typedText.value = fullText;\n return;\n }\n typewriterCharIndex = 0;\n resumeTypewriter();\n}\n\nfunction buildActiveStepsForRun(): EditorTourStep[] {\n const raw =\n props.tourConfig.steps !== undefined\n ? props.tourConfig.steps\n : defaultSteps.value;\n return filterResolvableSteps(raw);\n}\n\nfunction start(options?: EditorTourStartOptions): void {\n const steps = buildActiveStepsForRun();\n activeSteps.value = steps;\n if (steps.length === 0) {\n tourActive.value = false;\n return;\n }\n const nextIdx = Math.min(\n Math.max(0, options?.stepIndex ?? 0),\n steps.length - 1,\n );\n stepIndex.value = nextIdx;\n tourActive.value = true;\n nextTick(() => {\n updateTargetRect();\n startTypewriter();\n });\n}\n\nfunction dismiss(persist: boolean): void {\n stopTypewriter();\n tourActive.value = false;\n targetRect.value = null;\n if (persist) {\n writeTourDismissedState(props.tourConfig);\n }\n}\n\nfunction nextStep(): void {\n if (stepIndex.value < activeSteps.value.length - 1) {\n stepIndex.value++;\n nextTick(() => {\n updateTargetRect();\n startTypewriter();\n });\n } else {\n dismiss(true);\n }\n}\n\nfunction isDismissed(): boolean {\n return readTourDismissedState(props.tourConfig);\n}\n\nfunction resetDismissed(): void {\n clearTourDismissedState(props.tourConfig);\n}\n\nconst tooltipStyle = computed(() => {\n const rect = targetRect.value;\n if (!rect) return {};\n const placement = resolvePlacement(currentStep.value);\n const pad = 12;\n\n if (placement === \"canvas\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.left + rect.width / 2}px`,\n transform: \"translate(-50%, -50%)\",\n };\n }\n if (placement === \"sidebar\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.right + pad}px`,\n };\n }\n if (placement === \"rightSidebar\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.left - 300 - pad}px`,\n };\n }\n return {\n top: `${rect.bottom + pad}px`,\n left: `${Math.min(rect.left, typeof window !== \"undefined\" ? window.innerWidth - 320 : rect.left)}px`,\n };\n});\n\nconst spotlightStyle = computed(() => {\n const rect = targetRect.value;\n if (!rect) return {};\n const placement = resolvePlacement(currentStep.value);\n const pad = 6;\n const isLargePanel =\n placement === \"canvas\" ||\n placement === \"sidebar\" ||\n placement === \"rightSidebar\";\n const w = rect.width + pad * 2;\n const h = rect.height + pad * 2;\n const x = rect.left - pad;\n const y = rect.top - pad;\n return {\n width: `${w}px`,\n height: `${h}px`,\n transform: `translate(${x}px, ${y}px)`,\n borderRadius: isLargePanel ? \"14px\" : \"8px\",\n };\n});\n\nonMounted(() => {\n if (props.tourConfig.autoStart && !readTourDismissedState(props.tourConfig)) {\n start();\n }\n});\n\nonUnmounted(() => {\n stopTypewriter();\n bodyScrollLocked.value = false;\n});\n\ndefineExpose({\n start,\n dismiss: () => dismiss(true),\n resetDismissed,\n isDismissed,\n});\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"tpl-editor-tour\">\n <div\n v-if=\"tourActive\"\n class=\"tpl-editor-tour-overlay tpl:fixed tpl:inset-0 tpl:z-[10001]\"\n :class=\"{ 'tpl-editor-tour-overlay--dark': darkMode }\"\n data-testid=\"editor-tour-overlay\"\n @click.self=\"dismiss(true)\"\n >\n <div\n v-if=\"targetRect\"\n data-testid=\"editor-tour-spotlight\"\n class=\"tpl-editor-tour-spotlight tpl:absolute tpl:top-0 tpl:left-0\"\n :style=\"spotlightStyle\"\n @click=\"dismiss(true)\"\n />\n\n <div\n v-if=\"targetRect && currentStep\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"currentStepTitle\"\n data-testid=\"editor-tour-tooltip\"\n class=\"tpl-editor-tour-tooltip tpl-editor-tour-tooltip-shell tpl:fixed tpl:z-[10002]\"\n :style=\"tooltipStyle\"\n >\n <div class=\"tpl-editor-tour-tooltip-body\">\n <div class=\"tpl-editor-tour-step-meta\">\n {{\n format(t.tour.stepCounter, {\n current: String(stepIndex + 1),\n total: String(activeSteps.length),\n })\n }}\n </div>\n <div class=\"tpl-editor-tour-title\">\n {{ currentStepTitle }}\n </div>\n <div class=\"tpl-editor-tour-text\">\n {{ typedText\n }}<span\n v-if=\"typedText.length < currentStepText.length\"\n class=\"tpl-editor-tour-cursor\"\n >|</span\n >\n </div>\n </div>\n <div class=\"tpl-editor-tour-footer\">\n <button\n type=\"button\"\n data-testid=\"editor-tour-skip\"\n class=\"tpl-editor-tour-skip\"\n @click=\"dismiss(true)\"\n >\n {{ t.tour.skip }}\n </button>\n <button\n type=\"button\"\n data-testid=\"editor-tour-next\"\n class=\"tpl-editor-tour-next\"\n @click=\"nextStep\"\n >\n {{\n stepIndex < activeSteps.length - 1 ? t.tour.next : t.tour.done\n }}\n <ChevronRight\n v-if=\"stepIndex < activeSteps.length - 1\"\n :size=\"12\"\n aria-hidden=\"true\"\n />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n/* Playground-aligned palette: Teleport targets `body`, outside `.tpl` theme vars. */\n\n.tpl-editor-tour-overlay {\n --tour-primary: oklch(70% 0.16 55);\n --tour-primary-hover: oklch(63% 0.17 55);\n --tour-surface: #ffffff;\n --tour-title: #111827;\n --tour-body: #6b7280;\n --tour-skip: #9ca3af;\n --tour-skip-hover: #4b5563;\n --tour-border: #e5e7eb;\n --tour-footer-bg: rgb(249 250 251 / 0.5);\n --tour-next-fg: #ffffff;\n --tour-shadow-card: 0 16px 48px rgb(0 0 0 / 0.15);\n}\n\n.tpl-editor-tour-overlay--dark {\n --tour-primary: oklch(78% 0.14 55);\n --tour-primary-hover: oklch(70% 0.16 55);\n --tour-surface: #1f2937;\n --tour-title: #f9fafb;\n --tour-body: #9ca3af;\n --tour-skip: #6b7280;\n --tour-skip-hover: #d1d5db;\n --tour-border: #374151;\n --tour-footer-bg: rgb(31 41 55 / 0.5);\n --tour-next-fg: #ffffff;\n --tour-shadow-card: 0 16px 48px rgb(0 0 0 / 0.35);\n}\n\n.tpl-editor-tour-overlay:focus {\n outline: none;\n}\n\n.tpl-editor-tour-enter-active,\n.tpl-editor-tour-leave-active {\n transition: opacity 200ms ease;\n}\n\n.tpl-editor-tour-enter-from,\n.tpl-editor-tour-leave-to {\n opacity: 0;\n}\n\n/* Same ring/glow as legacy playground `.pg-onboarding-spotlight` */\n.tpl-editor-tour-spotlight {\n box-shadow:\n 0 0 0 2px oklch(70% 0.16 55 / 0.5),\n 0 0 20px 6px oklch(70% 0.16 55 / 0.12),\n 0 0 0 9999px rgba(0, 0, 0, 0.5);\n transition:\n transform 500ms cubic-bezier(0.4, 0, 0.2, 1),\n border-radius 500ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 400ms ease;\n pointer-events: auto;\n}\n\n@keyframes tpl-editor-tour-tooltip-in {\n from {\n opacity: 0;\n transform: translateY(10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.tpl-editor-tour-tooltip-shell {\n width: 300px;\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid var(--tour-border);\n background-color: var(--tour-surface);\n box-shadow: var(--tour-shadow-card);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n Segoe UI,\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n animation: tpl-editor-tour-tooltip-in 400ms cubic-bezier(0.4, 0, 0.2, 1) both;\n}\n\n.tpl-editor-tour-tooltip-body {\n padding: 16px 16px 12px;\n}\n\n.tpl-editor-tour-step-meta {\n margin-bottom: 4px;\n font-size: 12px;\n font-weight: 500;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--tour-primary);\n}\n\n.tpl-editor-tour-title {\n margin-bottom: 6px;\n font-size: 15px;\n font-weight: 600;\n color: var(--tour-title);\n}\n\n.tpl-editor-tour-text {\n min-height: 40px;\n font-size: 13px;\n line-height: 1.625;\n color: var(--tour-body);\n}\n\n.tpl-editor-tour-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-top: 1px solid var(--tour-border);\n background-color: var(--tour-footer-bg);\n}\n\n.tpl-editor-tour-skip {\n cursor: pointer;\n border: none;\n background: transparent;\n padding: 0;\n font-size: 13px;\n font-family: inherit;\n color: var(--tour-skip);\n transition: color 150ms ease;\n}\n\n.tpl-editor-tour-skip:hover {\n color: var(--tour-skip-hover);\n}\n\n.tpl-editor-tour-next {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n cursor: pointer;\n border: none;\n border-radius: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n color: var(--tour-next-fg);\n background-color: var(--tour-primary);\n transition:\n background-color 150ms ease,\n box-shadow 150ms ease;\n}\n\n.tpl-editor-tour-next:hover {\n background-color: var(--tour-primary-hover);\n box-shadow:\n 0 0 0 3px oklch(70% 0.16 55 / 0.15),\n 0 4px 16px oklch(70% 0.16 55 / 0.25);\n}\n\n.tpl-editor-tour-overlay--dark .tpl-editor-tour-next:hover {\n box-shadow:\n 0 0 0 3px oklch(78% 0.14 55 / 0.2),\n 0 4px 16px oklch(70% 0.16 55 / 0.2);\n}\n\n@keyframes tpl-editor-tour-cursor-blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n}\n\n.tpl-editor-tour-cursor {\n animation: tpl-editor-tour-cursor-blink 800ms ease-in-out infinite;\n font-weight: 300;\n color: oklch(70% 0.16 55);\n}\n\n.tpl-editor-tour-overlay--dark .tpl-editor-tour-cursor {\n color: oklch(78% 0.14 55);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-editor-tour-spotlight {\n transition-duration: 0.01ms !important;\n }\n\n .tpl-editor-tour-tooltip-shell {\n animation-duration: 0.01ms !important;\n }\n\n .tpl-editor-tour-cursor {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ChevronRight } from \"@lucide/vue\";\nimport {\n useIntervalFn,\n usePreferredReducedMotion,\n useScrollLock,\n} from \"@vueuse/core\";\nimport {\n computed,\n nextTick,\n onMounted,\n onUnmounted,\n ref,\n shallowRef,\n watch,\n} from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n EditorTourConfig,\n EditorTourStartOptions,\n EditorTourStep,\n EditorTourStepPlacement,\n} from \"../types/editor-tour\";\nimport {\n clearTourDismissedState,\n readTourDismissedState,\n writeTourDismissedState,\n} from \"../utils/tourStorage\";\n\nconst props = withDefaults(\n defineProps<{\n tourConfig: EditorTourConfig;\n /**\n * Align tooltip palette with the editor chrome (Teleport is outside `.tpl`,\n * so theme CSS variables do not apply unless we mirror light/dark here).\n */\n darkMode?: boolean;\n }>(),\n { darkMode: false },\n);\n\nconst { t, format } = useI18n();\n\nconst tourActive = ref(false);\nconst stepIndex = ref(0);\nconst targetRect = ref<DOMRect | null>(null);\nconst typedText = ref(\"\");\nconst prefersReducedMotion = usePreferredReducedMotion();\nconst activeSteps = shallowRef<EditorTourStep[]>([]);\n\nconst bodyScrollLocked = useScrollLock(document.body);\n\nwatch(\n tourActive,\n (active, _was, onCleanup) => {\n bodyScrollLocked.value = active;\n if (!active) return;\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n dismiss(true);\n }\n };\n document.addEventListener(\"keydown\", onKey, true);\n onCleanup(() => document.removeEventListener(\"keydown\", onKey, true));\n },\n { immediate: true },\n);\n\nlet typewriterCharIndex = 0;\nconst { pause: stopTypewriter, resume: resumeTypewriter } = useIntervalFn(\n () => {\n const fullText = currentStepText.value;\n if (typewriterCharIndex < fullText.length) {\n typedText.value = fullText.slice(0, typewriterCharIndex + 1);\n typewriterCharIndex++;\n } else {\n stopTypewriter();\n }\n },\n 25,\n { immediate: false },\n);\n\nconst defaultSteps = computed<EditorTourStep[]>(() => [\n {\n target: \".tpl-body\",\n placement: \"canvas\",\n title: t.tour.defaults.canvas.title,\n text: t.tour.defaults.canvas.text,\n },\n {\n target: \".tpl-sidebar-rail\",\n placement: \"sidebar\",\n title: t.tour.defaults.sidebar.title,\n text: t.tour.defaults.sidebar.text,\n },\n {\n target: \".tpl-right-sidebar\",\n placement: \"rightSidebar\",\n title: t.tour.defaults.rightSidebar.title,\n text: t.tour.defaults.rightSidebar.text,\n },\n]);\n\nfunction filterResolvableSteps(steps: EditorTourStep[]): EditorTourStep[] {\n return steps.filter((s) => {\n const el = document.querySelector(s.target);\n if (el) return true;\n return s.skipIfMissing === false;\n });\n}\n\nconst currentStep = computed(() => activeSteps.value[stepIndex.value] ?? null);\n\nconst currentStepTitle = computed(() => currentStep.value?.title ?? \"\");\nconst currentStepText = computed(() => currentStep.value?.text ?? \"\");\n\nfunction resolvePlacement(\n step: EditorTourStep | null,\n): EditorTourStepPlacement {\n return step?.placement ?? \"below\";\n}\n\nfunction updateTargetRect(): void {\n const step = currentStep.value;\n if (!step) {\n targetRect.value = null;\n return;\n }\n const el = document.querySelector<HTMLElement>(step.target);\n if (!el) {\n targetRect.value = null;\n return;\n }\n const placement = resolvePlacement(step);\n if (placement === \"below\") {\n el.scrollIntoView({\n behavior: \"instant\",\n block: \"nearest\",\n inline: \"nearest\",\n });\n }\n targetRect.value = el.getBoundingClientRect();\n}\n\nfunction startTypewriter(): void {\n stopTypewriter();\n const fullText = currentStepText.value;\n typedText.value = \"\";\n if (prefersReducedMotion.value === \"reduce\") {\n typedText.value = fullText;\n return;\n }\n typewriterCharIndex = 0;\n resumeTypewriter();\n}\n\nfunction buildActiveStepsForRun(): EditorTourStep[] {\n const raw =\n props.tourConfig.steps !== undefined\n ? props.tourConfig.steps\n : defaultSteps.value;\n return filterResolvableSteps(raw);\n}\n\nfunction start(options?: EditorTourStartOptions): void {\n const steps = buildActiveStepsForRun();\n activeSteps.value = steps;\n if (steps.length === 0) {\n tourActive.value = false;\n return;\n }\n const nextIdx = Math.min(\n Math.max(0, options?.stepIndex ?? 0),\n steps.length - 1,\n );\n stepIndex.value = nextIdx;\n tourActive.value = true;\n nextTick(() => {\n updateTargetRect();\n startTypewriter();\n });\n}\n\nfunction dismiss(persist: boolean): void {\n stopTypewriter();\n tourActive.value = false;\n targetRect.value = null;\n if (persist) {\n writeTourDismissedState(props.tourConfig);\n }\n}\n\nfunction nextStep(): void {\n if (stepIndex.value < activeSteps.value.length - 1) {\n stepIndex.value++;\n nextTick(() => {\n updateTargetRect();\n startTypewriter();\n });\n } else {\n dismiss(true);\n }\n}\n\nfunction isDismissed(): boolean {\n return readTourDismissedState(props.tourConfig);\n}\n\nfunction resetDismissed(): void {\n clearTourDismissedState(props.tourConfig);\n}\n\nconst tooltipStyle = computed(() => {\n const rect = targetRect.value;\n if (!rect) return {};\n const placement = resolvePlacement(currentStep.value);\n const pad = 12;\n\n if (placement === \"canvas\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.left + rect.width / 2}px`,\n transform: \"translate(-50%, -50%)\",\n };\n }\n if (placement === \"sidebar\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.right + pad}px`,\n };\n }\n if (placement === \"rightSidebar\") {\n return {\n top: `${rect.top + rect.height / 3}px`,\n left: `${rect.left - 300 - pad}px`,\n };\n }\n return {\n top: `${rect.bottom + pad}px`,\n left: `${Math.min(rect.left, typeof window !== \"undefined\" ? window.innerWidth - 320 : rect.left)}px`,\n };\n});\n\nconst spotlightStyle = computed(() => {\n const rect = targetRect.value;\n if (!rect) return {};\n const placement = resolvePlacement(currentStep.value);\n const pad = 6;\n const isLargePanel =\n placement === \"canvas\" ||\n placement === \"sidebar\" ||\n placement === \"rightSidebar\";\n const w = rect.width + pad * 2;\n const h = rect.height + pad * 2;\n const x = rect.left - pad;\n const y = rect.top - pad;\n return {\n width: `${w}px`,\n height: `${h}px`,\n transform: `translate(${x}px, ${y}px)`,\n borderRadius: isLargePanel ? \"14px\" : \"8px\",\n };\n});\n\nonMounted(() => {\n if (props.tourConfig.autoStart && !readTourDismissedState(props.tourConfig)) {\n start();\n }\n});\n\nonUnmounted(() => {\n stopTypewriter();\n bodyScrollLocked.value = false;\n});\n\ndefineExpose({\n start,\n dismiss: () => dismiss(true),\n resetDismissed,\n isDismissed,\n});\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"tpl-editor-tour\">\n <div\n v-if=\"tourActive\"\n class=\"tpl-editor-tour-overlay tpl:fixed tpl:inset-0 tpl:z-[10001]\"\n :class=\"{ 'tpl-editor-tour-overlay--dark': darkMode }\"\n data-testid=\"editor-tour-overlay\"\n @click.self=\"dismiss(true)\"\n >\n <div\n v-if=\"targetRect\"\n data-testid=\"editor-tour-spotlight\"\n class=\"tpl-editor-tour-spotlight tpl:absolute tpl:top-0 tpl:left-0\"\n :style=\"spotlightStyle\"\n @click=\"dismiss(true)\"\n />\n\n <div\n v-if=\"targetRect && currentStep\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"currentStepTitle\"\n data-testid=\"editor-tour-tooltip\"\n class=\"tpl-editor-tour-tooltip tpl-editor-tour-tooltip-shell tpl:fixed tpl:z-[10002]\"\n :style=\"tooltipStyle\"\n >\n <div class=\"tpl-editor-tour-tooltip-body\">\n <div class=\"tpl-editor-tour-step-meta\">\n {{\n format(t.tour.stepCounter, {\n current: String(stepIndex + 1),\n total: String(activeSteps.length),\n })\n }}\n </div>\n <div class=\"tpl-editor-tour-title\">\n {{ currentStepTitle }}\n </div>\n <div class=\"tpl-editor-tour-text\">\n {{ typedText\n }}<span\n v-if=\"typedText.length < currentStepText.length\"\n class=\"tpl-editor-tour-cursor\"\n >|</span\n >\n </div>\n </div>\n <div class=\"tpl-editor-tour-footer\">\n <button\n type=\"button\"\n data-testid=\"editor-tour-skip\"\n class=\"tpl-editor-tour-skip\"\n @click=\"dismiss(true)\"\n >\n {{ t.tour.skip }}\n </button>\n <button\n type=\"button\"\n data-testid=\"editor-tour-next\"\n class=\"tpl-editor-tour-next\"\n @click=\"nextStep\"\n >\n {{\n stepIndex < activeSteps.length - 1 ? t.tour.next : t.tour.done\n }}\n <ChevronRight\n v-if=\"stepIndex < activeSteps.length - 1\"\n :size=\"12\"\n aria-hidden=\"true\"\n />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n/* Playground-aligned palette: Teleport targets `body`, outside `.tpl` theme vars. */\n\n.tpl-editor-tour-overlay {\n --tour-primary: oklch(70% 0.16 55);\n --tour-primary-hover: oklch(63% 0.17 55);\n --tour-surface: #ffffff;\n --tour-title: #111827;\n --tour-body: #6b7280;\n --tour-skip: #9ca3af;\n --tour-skip-hover: #4b5563;\n --tour-border: #e5e7eb;\n --tour-footer-bg: rgb(249 250 251 / 0.5);\n --tour-next-fg: #ffffff;\n --tour-shadow-card: 0 16px 48px rgb(0 0 0 / 0.15);\n}\n\n.tpl-editor-tour-overlay--dark {\n --tour-primary: oklch(78% 0.14 55);\n --tour-primary-hover: oklch(70% 0.16 55);\n --tour-surface: #1f2937;\n --tour-title: #f9fafb;\n --tour-body: #9ca3af;\n --tour-skip: #6b7280;\n --tour-skip-hover: #d1d5db;\n --tour-border: #374151;\n --tour-footer-bg: rgb(31 41 55 / 0.5);\n --tour-next-fg: #ffffff;\n --tour-shadow-card: 0 16px 48px rgb(0 0 0 / 0.35);\n}\n\n.tpl-editor-tour-overlay:focus {\n outline: none;\n}\n\n.tpl-editor-tour-enter-active,\n.tpl-editor-tour-leave-active {\n transition: opacity 200ms ease;\n}\n\n.tpl-editor-tour-enter-from,\n.tpl-editor-tour-leave-to {\n opacity: 0;\n}\n\n/* Same ring/glow as legacy playground `.pg-onboarding-spotlight` */\n.tpl-editor-tour-spotlight {\n box-shadow:\n 0 0 0 2px oklch(70% 0.16 55 / 0.5),\n 0 0 20px 6px oklch(70% 0.16 55 / 0.12),\n 0 0 0 9999px rgba(0, 0, 0, 0.5);\n transition:\n transform 500ms cubic-bezier(0.4, 0, 0.2, 1),\n border-radius 500ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 400ms ease;\n pointer-events: auto;\n}\n\n@keyframes tpl-editor-tour-tooltip-in {\n from {\n opacity: 0;\n transform: translateY(10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.tpl-editor-tour-tooltip-shell {\n width: 300px;\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid var(--tour-border);\n background-color: var(--tour-surface);\n box-shadow: var(--tour-shadow-card);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n Segoe UI,\n Roboto,\n Helvetica,\n Arial,\n sans-serif;\n animation: tpl-editor-tour-tooltip-in 400ms cubic-bezier(0.4, 0, 0.2, 1) both;\n}\n\n.tpl-editor-tour-tooltip-body {\n padding: 16px 16px 12px;\n}\n\n.tpl-editor-tour-step-meta {\n margin-bottom: 4px;\n font-size: 12px;\n font-weight: 500;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--tour-primary);\n}\n\n.tpl-editor-tour-title {\n margin-bottom: 6px;\n font-size: 15px;\n font-weight: 600;\n color: var(--tour-title);\n}\n\n.tpl-editor-tour-text {\n min-height: 40px;\n font-size: 13px;\n line-height: 1.625;\n color: var(--tour-body);\n}\n\n.tpl-editor-tour-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-top: 1px solid var(--tour-border);\n background-color: var(--tour-footer-bg);\n}\n\n.tpl-editor-tour-skip {\n cursor: pointer;\n border: none;\n background: transparent;\n padding: 0;\n font-size: 13px;\n font-family: inherit;\n color: var(--tour-skip);\n transition: color 150ms ease;\n}\n\n.tpl-editor-tour-skip:hover {\n color: var(--tour-skip-hover);\n}\n\n.tpl-editor-tour-next {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n cursor: pointer;\n border: none;\n border-radius: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n color: var(--tour-next-fg);\n background-color: var(--tour-primary);\n transition:\n background-color 150ms ease,\n box-shadow 150ms ease;\n}\n\n.tpl-editor-tour-next:hover {\n background-color: var(--tour-primary-hover);\n box-shadow:\n 0 0 0 3px oklch(70% 0.16 55 / 0.15),\n 0 4px 16px oklch(70% 0.16 55 / 0.25);\n}\n\n.tpl-editor-tour-overlay--dark .tpl-editor-tour-next:hover {\n box-shadow:\n 0 0 0 3px oklch(78% 0.14 55 / 0.2),\n 0 4px 16px oklch(70% 0.16 55 / 0.2);\n}\n\n@keyframes tpl-editor-tour-cursor-blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n}\n\n.tpl-editor-tour-cursor {\n animation: tpl-editor-tour-cursor-blink 800ms ease-in-out infinite;\n font-weight: 300;\n color: oklch(70% 0.16 55);\n}\n\n.tpl-editor-tour-overlay--dark .tpl-editor-tour-cursor {\n color: oklch(78% 0.14 55);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-editor-tour-spotlight {\n transition-duration: 0.01ms !important;\n }\n\n .tpl-editor-tour-tooltip-shell {\n animation-duration: 0.01ms !important;\n }\n\n .tpl-editor-tour-cursor {\n animation: none !important;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { Plus, X } from \"@lucide/vue\";\nimport type { PopupTriggerConditions } from \"@aswin.dev/types\";\nimport { ref, watch } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n conditions: PopupTriggerConditions;\n}>();\n\nconst emit = defineEmits<{\n patch: [partial: Partial<PopupTriggerConditions>];\n}>();\n\nconst { t } = useI18n();\n\n/** Second row (AND/OR + page views) — matches “Add rule” / delete UX in the reference UI. */\nconst showPageViewsRow = ref(props.conditions.pageViews > 0);\n\nwatch(\n () => props.conditions.pageViews,\n (n) => {\n if (n > 0) {\n showPageViewsRow.value = true;\n }\n },\n);\n\nfunction patchDelay(ev: Event): void {\n const raw = (ev.target as HTMLInputElement).value;\n emit(\"patch\", {\n delaySeconds: Math.max(0, Number(raw) || 0),\n });\n}\n\nfunction patchPageViews(ev: Event): void {\n const raw = (ev.target as HTMLInputElement).value.trim();\n emit(\"patch\", {\n pageViews: raw === \"\" ? 0 : Math.max(0, Number(raw) || 0),\n });\n}\n\nfunction setOperator(op: \"and\" | \"or\"): void {\n emit(\"patch\", { operator: op });\n}\n\nfunction addPageViewsRule(): void {\n showPageViewsRow.value = true;\n}\n\nfunction removePageViewsRule(): void {\n showPageViewsRow.value = false;\n emit(\"patch\", { pageViews: 0 });\n}\n</script>\n\n<template>\n <div\n class=\"tpl:mt-3 tpl:space-y-3 tpl:border-t tpl:border-[var(--tpl-border)] tpl:pt-4\"\n >\n <!-- Row 1: delay + unit (dropdown like reference) -->\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-2 tpl:gap-y-2 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n <span class=\"tpl:shrink-0 tpl:text-[var(--tpl-text)]\">{{\n t.popupDisplayRules.triggerShowOnlyAfter\n }}</span>\n <input\n type=\"number\"\n min=\"0\"\n inputmode=\"numeric\"\n class=\"tpl:h-9 tpl:w-14 tpl:shrink-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text)] tpl:tabular-nums tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n :value=\"props.conditions.delaySeconds\"\n @change=\"patchDelay\"\n />\n <select\n class=\"tpl:h-9 tpl:min-w-[10.5rem] tpl:shrink-0 tpl:cursor-pointer tpl:appearance-none tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:pl-3 tpl:pr-8 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n aria-label=\"Time condition unit\"\n >\n <option value=\"seconds\">\n {{ t.popupDisplayRules.triggerSecondsOnPage }}\n </option>\n </select>\n </div>\n\n <!-- Row 2: AND/OR + page views + delete -->\n <div\n v-if=\"showPageViewsRow\"\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-2 tpl:gap-y-2\"\n >\n <div\n class=\"trigger-operator-toggle\"\n role=\"group\"\n :aria-label=\"t.popupDisplayRules.triggerOperatorGroupAria\"\n >\n <button\n type=\"button\"\n class=\"trigger-operator-toggle__btn\"\n :class=\"{\n 'trigger-operator-toggle__btn--selected':\n props.conditions.operator === 'and',\n }\"\n @click=\"setOperator('and')\"\n >\n {{ t.popupDisplayRules.operatorAndShort }}\n </button>\n <button\n type=\"button\"\n class=\"trigger-operator-toggle__btn\"\n :class=\"{\n 'trigger-operator-toggle__btn--selected':\n props.conditions.operator === 'or',\n }\"\n @click=\"setOperator('or')\"\n >\n {{ t.popupDisplayRules.operatorOrShort }}\n </button>\n </div>\n <input\n type=\"number\"\n min=\"0\"\n inputmode=\"numeric\"\n class=\"tpl:h-9 tpl:w-14 tpl:shrink-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text)] tpl:tabular-nums tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n :value=\"\n props.conditions.pageViews > 0 ? props.conditions.pageViews : ''\n \"\n :placeholder=\"'0'\"\n @change=\"patchPageViews\"\n />\n <span class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">{{\n t.popupDisplayRules.triggerPageViewsSuffix\n }}</span>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-lg tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)]\"\n :aria-label=\"t.popupDisplayRules.triggerRemoveRuleAria\"\n @click=\"removePageViewsRule\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Add rule (when page-views row is hidden) -->\n <button\n v-if=\"!showPageViewsRow\"\n type=\"button\"\n class=\"trigger-add-rule-btn\"\n @click=\"addPageViewsRule\"\n >\n <span class=\"trigger-add-rule-btn__icon\" aria-hidden=\"true\">\n <Plus :size=\"15\" :stroke-width=\"2.25\" />\n </span>\n {{ t.popupDisplayRules.triggerAddRule }}\n </button>\n </div>\n</template>\n\n<style scoped>\n/* Segmented AND / OR — light rail, dark selected pill, white unselected with subtle border */\n.trigger-operator-toggle {\n display: inline-flex;\n flex-shrink: 0;\n align-items: stretch;\n gap: 0.25rem;\n padding: 0.1875rem;\n background: var(--tpl-bg, #ffffff);\n border: 1px solid var(--tpl-border, #d8d8d8);\n border-radius: 9999px;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n}\n\n.trigger-operator-toggle__btn {\n margin: 0;\n padding: 0.4375rem 0.8125rem;\n font-family: inherit;\n font-size: 0.6875rem;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--tpl-text-muted, #6b6b6b);\n background: transparent;\n border: 1px solid transparent;\n border-radius: 9999px;\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.trigger-operator-toggle__btn:hover:not(\n .trigger-operator-toggle__btn--selected\n ) {\n color: var(--tpl-text, #333333);\n background: rgba(15, 23, 42, 0.04);\n border-color: var(--tpl-border, #c8c8c8);\n}\n\n.trigger-operator-toggle__btn--selected {\n color: #ffffff;\n background: var(--tpl-text, #333333);\n border-color: transparent;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n}\n\n.trigger-operator-toggle__btn:not(.trigger-operator-toggle__btn--selected) {\n background: var(--tpl-bg, #ffffff);\n border-color: var(--tpl-border, #d8d8d8);\n}\n\n.trigger-operator-toggle__btn:focus {\n outline: none;\n}\n\n.trigger-operator-toggle__btn:focus-visible {\n outline: 2px solid var(--tpl-primary, #6366f1);\n outline-offset: 2px;\n}\n\n/* Matches reference “+ Add rule”: white pill, hairline border, soft shadow, muted + */\n.trigger-add-rule-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.375rem;\n margin: 0;\n padding: 0.5rem 0.875rem;\n font-family: inherit;\n font-size: 0.8125rem;\n font-weight: 500;\n line-height: 1.25;\n letter-spacing: 0.01em;\n color: var(--tpl-text, #3a3a3a);\n background: var(--tpl-bg, #ffffff);\n border: 1px solid var(--tpl-border, #d8d8d8);\n border-radius: 8px;\n cursor: pointer;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n transition:\n border-color 0.15s ease,\n background-color 0.15s ease,\n box-shadow 0.15s ease,\n color 0.15s ease;\n}\n\n.trigger-add-rule-btn:hover {\n background: var(--tpl-bg-hover, #f7f7f7);\n border-color: var(--tpl-border, #c8c8c8);\n box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);\n}\n\n.trigger-add-rule-btn:active {\n box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.06) inset;\n}\n\n.trigger-add-rule-btn:focus {\n outline: none;\n}\n\n.trigger-add-rule-btn:focus-visible {\n outline: 2px solid var(--tpl-primary, #6366f1);\n outline-offset: 2px;\n}\n\n.trigger-add-rule-btn__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tpl-text-muted, #6b6b6b);\n line-height: 0;\n}\n\n.trigger-add-rule-btn:hover .trigger-add-rule-btn__icon {\n color: var(--tpl-text, #3a3a3a);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { Plus, X } from \"@lucide/vue\";\nimport type { PopupTriggerConditions } from \"@aswin.dev/types\";\nimport { ref, watch } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\n\nconst props = defineProps<{\n conditions: PopupTriggerConditions;\n}>();\n\nconst emit = defineEmits<{\n patch: [partial: Partial<PopupTriggerConditions>];\n}>();\n\nconst { t } = useI18n();\n\n/** Second row (AND/OR + page views) — matches “Add rule” / delete UX in the reference UI. */\nconst showPageViewsRow = ref(props.conditions.pageViews > 0);\n\nwatch(\n () => props.conditions.pageViews,\n (n) => {\n if (n > 0) {\n showPageViewsRow.value = true;\n }\n },\n);\n\nfunction patchDelay(ev: Event): void {\n const raw = (ev.target as HTMLInputElement).value;\n emit(\"patch\", {\n delaySeconds: Math.max(0, Number(raw) || 0),\n });\n}\n\nfunction patchPageViews(ev: Event): void {\n const raw = (ev.target as HTMLInputElement).value.trim();\n emit(\"patch\", {\n pageViews: raw === \"\" ? 0 : Math.max(0, Number(raw) || 0),\n });\n}\n\nfunction setOperator(op: \"and\" | \"or\"): void {\n emit(\"patch\", { operator: op });\n}\n\nfunction addPageViewsRule(): void {\n showPageViewsRow.value = true;\n}\n\nfunction removePageViewsRule(): void {\n showPageViewsRow.value = false;\n emit(\"patch\", { pageViews: 0 });\n}\n</script>\n\n<template>\n <div\n class=\"tpl:mt-3 tpl:space-y-3 tpl:border-t tpl:border-[var(--tpl-border)] tpl:pt-4\"\n >\n <!-- Row 1: delay + unit (dropdown like reference) -->\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-2 tpl:gap-y-2 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n <span class=\"tpl:shrink-0 tpl:text-[var(--tpl-text)]\">{{\n t.popupDisplayRules.triggerShowOnlyAfter\n }}</span>\n <input\n type=\"number\"\n min=\"0\"\n inputmode=\"numeric\"\n class=\"tpl:h-9 tpl:w-14 tpl:shrink-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text)] tpl:tabular-nums tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n :value=\"props.conditions.delaySeconds\"\n @change=\"patchDelay\"\n />\n <select\n class=\"tpl:h-9 tpl:min-w-[10.5rem] tpl:shrink-0 tpl:cursor-pointer tpl:appearance-none tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:pl-3 tpl:pr-8 tpl:text-left tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n aria-label=\"Time condition unit\"\n >\n <option value=\"seconds\">\n {{ t.popupDisplayRules.triggerSecondsOnPage }}\n </option>\n </select>\n </div>\n\n <!-- Row 2: AND/OR + page views + delete -->\n <div\n v-if=\"showPageViewsRow\"\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-x-2 tpl:gap-y-2\"\n >\n <div\n class=\"trigger-operator-toggle\"\n role=\"group\"\n :aria-label=\"t.popupDisplayRules.triggerOperatorGroupAria\"\n >\n <button\n type=\"button\"\n class=\"trigger-operator-toggle__btn\"\n :class=\"{\n 'trigger-operator-toggle__btn--selected':\n props.conditions.operator === 'and',\n }\"\n @click=\"setOperator('and')\"\n >\n {{ t.popupDisplayRules.operatorAndShort }}\n </button>\n <button\n type=\"button\"\n class=\"trigger-operator-toggle__btn\"\n :class=\"{\n 'trigger-operator-toggle__btn--selected':\n props.conditions.operator === 'or',\n }\"\n @click=\"setOperator('or')\"\n >\n {{ t.popupDisplayRules.operatorOrShort }}\n </button>\n </div>\n <input\n type=\"number\"\n min=\"0\"\n inputmode=\"numeric\"\n class=\"tpl:h-9 tpl:w-14 tpl:shrink-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text)] tpl:tabular-nums tpl:outline-none focus:tpl:ring-2 focus:tpl:ring-[var(--tpl-primary)]/25\"\n :value=\"\n props.conditions.pageViews > 0 ? props.conditions.pageViews : ''\n \"\n :placeholder=\"'0'\"\n @change=\"patchPageViews\"\n />\n <span class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">{{\n t.popupDisplayRules.triggerPageViewsSuffix\n }}</span>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-lg tpl:text-[var(--tpl-text-muted)] tpl:transition-colors hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)]\"\n :aria-label=\"t.popupDisplayRules.triggerRemoveRuleAria\"\n @click=\"removePageViewsRule\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Add rule (when page-views row is hidden) -->\n <button\n v-if=\"!showPageViewsRow\"\n type=\"button\"\n class=\"trigger-add-rule-btn\"\n @click=\"addPageViewsRule\"\n >\n <span class=\"trigger-add-rule-btn__icon\" aria-hidden=\"true\">\n <Plus :size=\"15\" :stroke-width=\"2.25\" />\n </span>\n {{ t.popupDisplayRules.triggerAddRule }}\n </button>\n </div>\n</template>\n\n<style scoped>\n/* Segmented AND / OR — light rail, dark selected pill, white unselected with subtle border */\n.trigger-operator-toggle {\n display: inline-flex;\n flex-shrink: 0;\n align-items: stretch;\n gap: 0.25rem;\n padding: 0.1875rem;\n background: var(--tpl-bg, #ffffff);\n border: 1px solid var(--tpl-border, #d8d8d8);\n border-radius: 9999px;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n}\n\n.trigger-operator-toggle__btn {\n margin: 0;\n padding: 0.4375rem 0.8125rem;\n font-family: inherit;\n font-size: 0.6875rem;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--tpl-text-muted, #6b6b6b);\n background: transparent;\n border: 1px solid transparent;\n border-radius: 9999px;\n cursor: pointer;\n transition:\n background-color 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.trigger-operator-toggle__btn:hover:not(\n .trigger-operator-toggle__btn--selected\n ) {\n color: var(--tpl-text, #333333);\n background: rgba(15, 23, 42, 0.04);\n border-color: var(--tpl-border, #c8c8c8);\n}\n\n.trigger-operator-toggle__btn--selected {\n color: #ffffff;\n background: var(--tpl-text, #333333);\n border-color: transparent;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n}\n\n.trigger-operator-toggle__btn:not(.trigger-operator-toggle__btn--selected) {\n background: var(--tpl-bg, #ffffff);\n border-color: var(--tpl-border, #d8d8d8);\n}\n\n.trigger-operator-toggle__btn:focus {\n outline: none;\n}\n\n.trigger-operator-toggle__btn:focus-visible {\n outline: 2px solid var(--tpl-primary, #6366f1);\n outline-offset: 2px;\n}\n\n/* Matches reference “+ Add rule”: white pill, hairline border, soft shadow, muted + */\n.trigger-add-rule-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.375rem;\n margin: 0;\n padding: 0.5rem 0.875rem;\n font-family: inherit;\n font-size: 0.8125rem;\n font-weight: 500;\n line-height: 1.25;\n letter-spacing: 0.01em;\n color: var(--tpl-text, #3a3a3a);\n background: var(--tpl-bg, #ffffff);\n border: 1px solid var(--tpl-border, #d8d8d8);\n border-radius: 8px;\n cursor: pointer;\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n transition:\n border-color 0.15s ease,\n background-color 0.15s ease,\n box-shadow 0.15s ease,\n color 0.15s ease;\n}\n\n.trigger-add-rule-btn:hover {\n background: var(--tpl-bg-hover, #f7f7f7);\n border-color: var(--tpl-border, #c8c8c8);\n box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);\n}\n\n.trigger-add-rule-btn:active {\n box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.06) inset;\n}\n\n.trigger-add-rule-btn:focus {\n outline: none;\n}\n\n.trigger-add-rule-btn:focus-visible {\n outline: 2px solid var(--tpl-primary, #6366f1);\n outline-offset: 2px;\n}\n\n.trigger-add-rule-btn__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tpl-text-muted, #6b6b6b);\n line-height: 0;\n}\n\n.trigger-add-rule-btn:hover .trigger-add-rule-btn__icon {\n color: var(--tpl-text, #3a3a3a);\n}\n</style>\n","<script setup lang=\"ts\">\nimport {\n ArrowDownFromLine,\n Cog,\n LogOut,\n MousePointerClick,\n MousePointer2,\n LayoutTemplate,\n} from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport type {\n PopupEmbedSettings,\n PopupTriggerConditions,\n PopupTriggersSettings,\n} from \"@aswin.dev/types\";\nimport {\n createDefaultTriggerConditions,\n normalizeTriggerConditions,\n toPopupTriggersEmbedJson,\n} from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupTriggerConditionDetails from \"./PopupTriggerConditionDetails.vue\";\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupDisplayRulesView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rulesTab = ref<\n \"trigger\" | \"pages\" | \"audience\" | \"frequency\" | \"advanced\"\n>(\"trigger\");\n\nconst rulesTabs = computed(() => [\n { id: \"trigger\" as const, label: t.popupDisplayRules.tabTrigger },\n { id: \"pages\" as const, label: t.popupDisplayRules.tabPages },\n { id: \"audience\" as const, label: t.popupDisplayRules.tabAudience },\n { id: \"frequency\" as const, label: t.popupDisplayRules.tabFrequency },\n { id: \"advanced\" as const, label: t.popupDisplayRules.tabAdvanced },\n]);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nfunction commit(next: PopupEmbedSettings): void {\n editor!.updateSettings({ popup: next });\n}\n\nfunction patchTrigger<K extends keyof PopupTriggersSettings>(\n key: K,\n patch: Partial<PopupTriggersSettings[K]>,\n): void {\n const cur = popup.value;\n commit({\n ...cur,\n triggers: {\n ...cur.triggers,\n [key]: { ...cur.triggers[key], ...patch },\n },\n });\n}\n\nconst triggerDefs = computed(() => [\n {\n key: \"onLanding\" as const,\n icon: LayoutTemplate,\n title: t.popupDisplayRules.triggerOnLandingTitle,\n desc: t.popupDisplayRules.triggerOnLandingDesc,\n kind: \"landing\" as const,\n },\n {\n key: \"onExit\" as const,\n icon: LogOut,\n title: t.popupDisplayRules.triggerOnExitTitle,\n desc: t.popupDisplayRules.triggerOnExitDesc,\n kind: \"exit\" as const,\n },\n {\n key: \"onScroll\" as const,\n icon: ArrowDownFromLine,\n title: t.popupDisplayRules.triggerOnScrollTitle,\n desc: t.popupDisplayRules.triggerOnScrollDesc,\n kind: \"scroll\" as const,\n },\n {\n key: \"onClick\" as const,\n icon: MousePointerClick,\n title: t.popupDisplayRules.triggerOnClickTitle,\n desc: t.popupDisplayRules.triggerOnClickDesc,\n kind: \"click\" as const,\n },\n {\n key: \"onHover\" as const,\n icon: MousePointer2,\n title: t.popupDisplayRules.triggerOnHoverTitle,\n desc: t.popupDisplayRules.triggerOnHoverDesc,\n kind: \"hover\" as const,\n },\n {\n key: \"onCustomEvent\" as const,\n icon: Cog,\n title: t.popupDisplayRules.triggerOnCustomTitle,\n desc: t.popupDisplayRules.triggerOnCustomDesc,\n kind: \"custom\" as const,\n },\n]);\n\nconst debugTriggersOpen = ref(false);\nconst debugCopyDone = ref(false);\n\nfunction triggerSlotConditions<K extends keyof PopupTriggersSettings>(\n key: K,\n): PopupTriggerConditions {\n return normalizeTriggerConditions(\n popup.value.triggers[key].triggerConditions,\n );\n}\n\nfunction patchConditions<K extends keyof PopupTriggersSettings>(\n key: K,\n partial: Partial<PopupTriggerConditions>,\n): void {\n const prev = triggerSlotConditions(key);\n patchTrigger(key, {\n triggerConditions: { ...prev, ...partial },\n } as Partial<PopupTriggersSettings[K]>);\n}\n\nfunction setConditionsPackEnabled<K extends keyof PopupTriggersSettings>(\n key: K,\n enabled: boolean,\n): void {\n if (enabled) {\n patchTrigger(key, {\n triggerConditions: {\n ...createDefaultTriggerConditions(),\n enabled: true,\n delaySeconds: 5,\n pageViews: 3,\n operator: \"and\",\n },\n } as Partial<PopupTriggersSettings[K]>);\n } else {\n patchTrigger(key, {\n triggerConditions: {\n ...triggerSlotConditions(key),\n enabled: false,\n },\n } as Partial<PopupTriggersSettings[K]>);\n }\n}\n\nconst embedTriggersWrappedJson = computed(() =>\n JSON.stringify(\n { triggers: toPopupTriggersEmbedJson(popup.value.triggers) },\n null,\n 2,\n ),\n);\n\nasync function copyEmbedTriggersJson(): Promise<void> {\n try {\n await navigator.clipboard.writeText(embedTriggersWrappedJson.value);\n debugCopyDone.value = true;\n window.setTimeout(() => {\n debugCopyDone.value = false;\n }, 2000);\n } catch {\n debugCopyDone.value = false;\n }\n}\n</script>\n\n<template>\n <div\n class=\"tpl-popup-display-rules tpl:mx-auto tpl:max-w-3xl tpl:px-6 tpl:py-8 tpl:pb-14 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:mb-6 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-3\"\n >\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:p-1\"\n >\n <button\n v-for=\"tab in rulesTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n rulesTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:shadow-sm'\n : 'tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"rulesTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"debugTriggersOpen = true\"\n >\n {{ t.popupDisplayRules.debug }}\n </button>\n </div>\n\n <template v-if=\"rulesTab !== 'trigger'\">\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.placeholderSubtab }}\n </p>\n </template>\n\n <template v-else>\n <h1\n class=\"tpl:mb-2 tpl:text-2xl tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDisplayRules.triggerHeading }}\n </h1>\n <p class=\"tpl:mb-1 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.triggerIntro }}\n </p>\n <p class=\"tpl:mb-8 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.triggerIntroSecondary }}\n <a\n href=\"#\"\n class=\"tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n </p>\n\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <div\n v-for=\"def in triggerDefs\"\n :key=\"def.key\"\n class=\"tpl:rounded-xl tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-4 tpl:shadow-sm\"\n >\n <div class=\"tpl:flex tpl:gap-4 tpl:items-start\">\n <input\n type=\"checkbox\"\n class=\"tpl:mt-1 tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:shrink-0 tpl:rounded\"\n :checked=\"popup.triggers[def.key].enabled\"\n @change=\"\n patchTrigger(def.key, {\n enabled: ($event.target as HTMLInputElement).checked,\n })\n \"\n />\n <component\n :is=\"def.icon\"\n :size=\"22\"\n :stroke-width=\"1.75\"\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:text-[var(--tpl-text-muted)]\"\n />\n <div class=\"tpl:min-w-0 tpl:flex-1\">\n <h2\n class=\"tpl:text-[15px] tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ def.title }}\n </h2>\n <p\n class=\"tpl:mt-1 tpl:text-sm tpl:text-[var(--tpl-text-muted)] tpl:leading-relaxed\"\n >\n {{ def.desc }}\n <template v-if=\"def.kind === 'exit'\">\n {{ \" \" }}\n <a\n href=\"#\"\n class=\"tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n </template>\n </p>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'landing'\"\n class=\"tpl:mt-4 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onLanding').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onLanding',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onLanding').enabled\"\n :conditions=\"triggerSlotConditions('onLanding')\"\n @patch=\"(p) => patchConditions('onLanding', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'exit'\"\n class=\"tpl:mt-4 tpl:space-y-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"popup.triggers.onExit.triggerOnScrollUp === true\"\n @change=\"\n patchTrigger('onExit', {\n triggerOnScrollUp: ($event.target as HTMLInputElement)\n .checked,\n })\n \"\n />\n {{ t.popupDisplayRules.triggerOnScrollUpLabel }}\n </label>\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"\n popup.triggers.onExit.triggerOnBackButton === true\n \"\n @change=\"\n patchTrigger('onExit', {\n triggerOnBackButton: ($event.target as HTMLInputElement)\n .checked,\n })\n \"\n />\n {{ t.popupDisplayRules.triggerOnBackButtonLabel }}\n </label>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onExit').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onExit',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onExit').enabled\"\n :conditions=\"triggerSlotConditions('onExit')\"\n @patch=\"(p) => patchConditions('onExit', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'scroll'\"\n class=\"tpl:mt-4 tpl:space-y-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDisplayRules.scrollShowAfter }}\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n class=\"tpl:w-16 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:py-1 tpl:text-sm\"\n :value=\"popup.triggers.onScroll.scrollPercent ?? 50\"\n @change=\"\n patchTrigger('onScroll', {\n scrollPercent: Number(\n ($event.target as HTMLInputElement).value,\n ),\n })\n \"\n />\n {{ t.popupDisplayRules.scrollPercentSuffix }}\n </label>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onScroll').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onScroll',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onScroll').enabled\"\n :conditions=\"triggerSlotConditions('onScroll')\"\n @patch=\"(p) => patchConditions('onScroll', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'click'\"\n class=\"tpl:mt-4 tpl:flex tpl:flex-col tpl:gap-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <div\n class=\"tpl:inline-flex tpl:rounded-full tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1 tpl:text-sm tpl:font-mono tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"text\"\n class=\"tpl:w-36 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:outline-none\"\n :value=\"popup.triggers.onClick.clickAnchor ?? ''\"\n @change=\"\n patchTrigger('onClick', {\n clickAnchor: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n </div>\n <p\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDisplayRules.clickHint }}\n </p>\n <a\n href=\"#\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-primary)] tpl:underline\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onClick').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onClick',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onClick').enabled\"\n :conditions=\"triggerSlotConditions('onClick')\"\n @patch=\"(p) => patchConditions('onClick', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'hover'\"\n class=\"tpl:mt-4 tpl:flex tpl:flex-col tpl:gap-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <div class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n class=\"tpl:min-w-[12rem] tpl:flex-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm\"\n :placeholder=\"t.popupDisplayRules.hoverPlaceholder\"\n :value=\"popup.triggers.onHover.hoverElementId ?? ''\"\n @change=\"\n patchTrigger('onHover', {\n hoverElementId: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n <a\n href=\"#\"\n class=\"tpl:text-sm tpl:text-[var(--tpl-primary)] tpl:underline\"\n >{{ t.popupDisplayRules.help }}</a\n >\n </div>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onHover').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onHover',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onHover').enabled\"\n :conditions=\"triggerSlotConditions('onHover')\"\n @patch=\"(p) => patchConditions('onHover', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'custom'\"\n class=\"tpl:mt-4 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.popupDisplayRules.customEventLabel }}</label\n >\n <input\n type=\"text\"\n class=\"tpl:mt-2 tpl:w-full tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm\"\n :placeholder=\"t.popupDisplayRules.customEventPlaceholder\"\n :value=\"popup.triggers.onCustomEvent.customEventName ?? ''\"\n @change=\"\n patchTrigger('onCustomEvent', {\n customEventName: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n <hr class=\"tpl:mt-3 tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:mt-3 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onCustomEvent').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onCustomEvent',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onCustomEvent').enabled\"\n :conditions=\"triggerSlotConditions('onCustomEvent')\"\n @patch=\"(p) => patchConditions('onCustomEvent', p)\"\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"debugTriggersOpen\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-[1000] tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/50 tpl:p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"t.popupDisplayRules.debugJsonTitle\"\n @click.self=\"debugTriggersOpen = false\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[85vh] tpl:w-full tpl:max-w-2xl tpl:flex-col tpl:overflow-hidden tpl:rounded-xl tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:shadow-xl\"\n >\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <h2 class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ t.popupDisplayRules.debugJsonTitle }}\n </h2>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"copyEmbedTriggersJson\"\n >\n {{\n debugCopyDone\n ? t.popupDisplayRules.copyJsonDone\n : t.popupDisplayRules.copyJson\n }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"debugTriggersOpen = false\"\n >\n {{ t.popupDisplayRules.debugJsonClose }}\n </button>\n </div>\n </div>\n <pre\n class=\"tpl:m-0 tpl:flex-1 tpl:overflow-auto tpl:p-4 tpl:text-left tpl:font-mono tpl:text-[11px] tpl:leading-relaxed tpl:text-[var(--tpl-text)]\"\n >{{ embedTriggersWrappedJson }}</pre\n >\n <p\n class=\"tpl:m-0 tpl:border-t tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDisplayRules.debugJsonHint }}\n </p>\n </div>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport {\n ArrowDownFromLine,\n Cog,\n LogOut,\n MousePointerClick,\n MousePointer2,\n LayoutTemplate,\n} from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport type {\n PopupEmbedSettings,\n PopupTriggerConditions,\n PopupTriggersSettings,\n} from \"@aswin.dev/types\";\nimport {\n createDefaultTriggerConditions,\n normalizeTriggerConditions,\n toPopupTriggersEmbedJson,\n} from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupTriggerConditionDetails from \"./PopupTriggerConditionDetails.vue\";\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupDisplayRulesView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rulesTab = ref<\n \"trigger\" | \"pages\" | \"audience\" | \"frequency\" | \"advanced\"\n>(\"trigger\");\n\nconst rulesTabs = computed(() => [\n { id: \"trigger\" as const, label: t.popupDisplayRules.tabTrigger },\n { id: \"pages\" as const, label: t.popupDisplayRules.tabPages },\n { id: \"audience\" as const, label: t.popupDisplayRules.tabAudience },\n { id: \"frequency\" as const, label: t.popupDisplayRules.tabFrequency },\n { id: \"advanced\" as const, label: t.popupDisplayRules.tabAdvanced },\n]);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nfunction commit(next: PopupEmbedSettings): void {\n editor!.updateSettings({ popup: next });\n}\n\nfunction patchTrigger<K extends keyof PopupTriggersSettings>(\n key: K,\n patch: Partial<PopupTriggersSettings[K]>,\n): void {\n const cur = popup.value;\n commit({\n ...cur,\n triggers: {\n ...cur.triggers,\n [key]: { ...cur.triggers[key], ...patch },\n },\n });\n}\n\nconst triggerDefs = computed(() => [\n {\n key: \"onLanding\" as const,\n icon: LayoutTemplate,\n title: t.popupDisplayRules.triggerOnLandingTitle,\n desc: t.popupDisplayRules.triggerOnLandingDesc,\n kind: \"landing\" as const,\n },\n {\n key: \"onExit\" as const,\n icon: LogOut,\n title: t.popupDisplayRules.triggerOnExitTitle,\n desc: t.popupDisplayRules.triggerOnExitDesc,\n kind: \"exit\" as const,\n },\n {\n key: \"onScroll\" as const,\n icon: ArrowDownFromLine,\n title: t.popupDisplayRules.triggerOnScrollTitle,\n desc: t.popupDisplayRules.triggerOnScrollDesc,\n kind: \"scroll\" as const,\n },\n {\n key: \"onClick\" as const,\n icon: MousePointerClick,\n title: t.popupDisplayRules.triggerOnClickTitle,\n desc: t.popupDisplayRules.triggerOnClickDesc,\n kind: \"click\" as const,\n },\n {\n key: \"onHover\" as const,\n icon: MousePointer2,\n title: t.popupDisplayRules.triggerOnHoverTitle,\n desc: t.popupDisplayRules.triggerOnHoverDesc,\n kind: \"hover\" as const,\n },\n {\n key: \"onCustomEvent\" as const,\n icon: Cog,\n title: t.popupDisplayRules.triggerOnCustomTitle,\n desc: t.popupDisplayRules.triggerOnCustomDesc,\n kind: \"custom\" as const,\n },\n]);\n\nconst debugTriggersOpen = ref(false);\nconst debugCopyDone = ref(false);\n\nfunction triggerSlotConditions<K extends keyof PopupTriggersSettings>(\n key: K,\n): PopupTriggerConditions {\n return normalizeTriggerConditions(\n popup.value.triggers[key].triggerConditions,\n );\n}\n\nfunction patchConditions<K extends keyof PopupTriggersSettings>(\n key: K,\n partial: Partial<PopupTriggerConditions>,\n): void {\n const prev = triggerSlotConditions(key);\n patchTrigger(key, {\n triggerConditions: { ...prev, ...partial },\n } as Partial<PopupTriggersSettings[K]>);\n}\n\nfunction setConditionsPackEnabled<K extends keyof PopupTriggersSettings>(\n key: K,\n enabled: boolean,\n): void {\n if (enabled) {\n patchTrigger(key, {\n triggerConditions: {\n ...createDefaultTriggerConditions(),\n enabled: true,\n delaySeconds: 5,\n pageViews: 3,\n operator: \"and\",\n },\n } as Partial<PopupTriggersSettings[K]>);\n } else {\n patchTrigger(key, {\n triggerConditions: {\n ...triggerSlotConditions(key),\n enabled: false,\n },\n } as Partial<PopupTriggersSettings[K]>);\n }\n}\n\nconst embedTriggersWrappedJson = computed(() =>\n JSON.stringify(\n { triggers: toPopupTriggersEmbedJson(popup.value.triggers) },\n null,\n 2,\n ),\n);\n\nasync function copyEmbedTriggersJson(): Promise<void> {\n try {\n await navigator.clipboard.writeText(embedTriggersWrappedJson.value);\n debugCopyDone.value = true;\n window.setTimeout(() => {\n debugCopyDone.value = false;\n }, 2000);\n } catch {\n debugCopyDone.value = false;\n }\n}\n</script>\n\n<template>\n <div\n class=\"tpl-popup-display-rules tpl:mx-auto tpl:max-w-3xl tpl:px-6 tpl:py-8 tpl:pb-14 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:mb-6 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-3\"\n >\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:p-1\"\n >\n <button\n v-for=\"tab in rulesTabs\"\n :key=\"tab.id\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n rulesTab === tab.id\n ? 'tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:shadow-sm'\n : 'tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]'\n \"\n @click=\"rulesTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"debugTriggersOpen = true\"\n >\n {{ t.popupDisplayRules.debug }}\n </button>\n </div>\n\n <template v-if=\"rulesTab !== 'trigger'\">\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.placeholderSubtab }}\n </p>\n </template>\n\n <template v-else>\n <h1\n class=\"tpl:mb-2 tpl:text-2xl tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDisplayRules.triggerHeading }}\n </h1>\n <p class=\"tpl:mb-1 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.triggerIntro }}\n </p>\n <p class=\"tpl:mb-8 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ t.popupDisplayRules.triggerIntroSecondary }}\n <a\n href=\"#\"\n class=\"tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n </p>\n\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <div\n v-for=\"def in triggerDefs\"\n :key=\"def.key\"\n class=\"tpl:rounded-xl tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-4 tpl:shadow-sm\"\n >\n <div class=\"tpl:flex tpl:gap-4 tpl:items-start\">\n <input\n type=\"checkbox\"\n class=\"tpl:mt-1 tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:shrink-0 tpl:rounded\"\n :checked=\"popup.triggers[def.key].enabled\"\n @change=\"\n patchTrigger(def.key, {\n enabled: ($event.target as HTMLInputElement).checked,\n })\n \"\n />\n <component\n :is=\"def.icon\"\n :size=\"22\"\n :stroke-width=\"1.75\"\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:text-[var(--tpl-text-muted)]\"\n />\n <div class=\"tpl:min-w-0 tpl:flex-1\">\n <h2\n class=\"tpl:text-[15px] tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ def.title }}\n </h2>\n <p\n class=\"tpl:mt-1 tpl:text-sm tpl:text-[var(--tpl-text-muted)] tpl:leading-relaxed\"\n >\n {{ def.desc }}\n <template v-if=\"def.kind === 'exit'\">\n {{ \" \" }}\n <a\n href=\"#\"\n class=\"tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n </template>\n </p>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'landing'\"\n class=\"tpl:mt-4 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onLanding').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onLanding',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onLanding').enabled\"\n :conditions=\"triggerSlotConditions('onLanding')\"\n @patch=\"(p) => patchConditions('onLanding', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'exit'\"\n class=\"tpl:mt-4 tpl:space-y-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"popup.triggers.onExit.triggerOnScrollUp === true\"\n @change=\"\n patchTrigger('onExit', {\n triggerOnScrollUp: ($event.target as HTMLInputElement)\n .checked,\n })\n \"\n />\n {{ t.popupDisplayRules.triggerOnScrollUpLabel }}\n </label>\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"\n popup.triggers.onExit.triggerOnBackButton === true\n \"\n @change=\"\n patchTrigger('onExit', {\n triggerOnBackButton: ($event.target as HTMLInputElement)\n .checked,\n })\n \"\n />\n {{ t.popupDisplayRules.triggerOnBackButtonLabel }}\n </label>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onExit').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onExit',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onExit').enabled\"\n :conditions=\"triggerSlotConditions('onExit')\"\n @patch=\"(p) => patchConditions('onExit', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'scroll'\"\n class=\"tpl:mt-4 tpl:space-y-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupDisplayRules.scrollShowAfter }}\n <input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n class=\"tpl:w-16 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-2 tpl:py-1 tpl:text-sm\"\n :value=\"popup.triggers.onScroll.scrollPercent ?? 50\"\n @change=\"\n patchTrigger('onScroll', {\n scrollPercent: Number(\n ($event.target as HTMLInputElement).value,\n ),\n })\n \"\n />\n {{ t.popupDisplayRules.scrollPercentSuffix }}\n </label>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onScroll').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onScroll',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onScroll').enabled\"\n :conditions=\"triggerSlotConditions('onScroll')\"\n @patch=\"(p) => patchConditions('onScroll', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'click'\"\n class=\"tpl:mt-4 tpl:flex tpl:flex-col tpl:gap-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <div\n class=\"tpl:inline-flex tpl:rounded-full tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1 tpl:text-sm tpl:font-mono tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"text\"\n class=\"tpl:w-36 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:outline-none\"\n :value=\"popup.triggers.onClick.clickAnchor ?? ''\"\n @change=\"\n patchTrigger('onClick', {\n clickAnchor: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n </div>\n <p\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDisplayRules.clickHint }}\n </p>\n <a\n href=\"#\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-primary)] tpl:underline\"\n >{{ t.popupDisplayRules.learnMore }}</a\n >\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onClick').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onClick',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onClick').enabled\"\n :conditions=\"triggerSlotConditions('onClick')\"\n @patch=\"(p) => patchConditions('onClick', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'hover'\"\n class=\"tpl:mt-4 tpl:flex tpl:flex-col tpl:gap-3 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <div class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n class=\"tpl:min-w-[12rem] tpl:flex-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm\"\n :placeholder=\"t.popupDisplayRules.hoverPlaceholder\"\n :value=\"popup.triggers.onHover.hoverElementId ?? ''\"\n @change=\"\n patchTrigger('onHover', {\n hoverElementId: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n <a\n href=\"#\"\n class=\"tpl:text-sm tpl:text-[var(--tpl-primary)] tpl:underline\"\n >{{ t.popupDisplayRules.help }}</a\n >\n </div>\n <hr class=\"tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onHover').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onHover',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onHover').enabled\"\n :conditions=\"triggerSlotConditions('onHover')\"\n @patch=\"(p) => patchConditions('onHover', p)\"\n />\n </div>\n\n <div\n v-if=\"popup.triggers[def.key].enabled && def.kind === 'custom'\"\n class=\"tpl:mt-4 tpl:rounded-lg tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-3\"\n >\n <label\n class=\"tpl:block tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.popupDisplayRules.customEventLabel }}</label\n >\n <input\n type=\"text\"\n class=\"tpl:mt-2 tpl:w-full tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm\"\n :placeholder=\"t.popupDisplayRules.customEventPlaceholder\"\n :value=\"popup.triggers.onCustomEvent.customEventName ?? ''\"\n @change=\"\n patchTrigger('onCustomEvent', {\n customEventName: (\n $event.target as HTMLInputElement\n ).value.trim(),\n })\n \"\n />\n <hr class=\"tpl:mt-3 tpl:border-[var(--tpl-border)]\" />\n <label\n class=\"tpl:mt-3 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:h-4 tpl:w-4 tpl:accent-[var(--tpl-primary)] tpl:rounded\"\n :checked=\"triggerSlotConditions('onCustomEvent').enabled\"\n @change=\"\n setConditionsPackEnabled(\n 'onCustomEvent',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.popupDisplayRules.addTriggerConditions }}\n </label>\n <PopupTriggerConditionDetails\n v-if=\"triggerSlotConditions('onCustomEvent').enabled\"\n :conditions=\"triggerSlotConditions('onCustomEvent')\"\n @patch=\"(p) => patchConditions('onCustomEvent', p)\"\n />\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"debugTriggersOpen\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-[1000] tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/50 tpl:p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"t.popupDisplayRules.debugJsonTitle\"\n @click.self=\"debugTriggersOpen = false\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[85vh] tpl:w-full tpl:max-w-2xl tpl:flex-col tpl:overflow-hidden tpl:rounded-xl tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:shadow-xl\"\n >\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <h2 class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ t.popupDisplayRules.debugJsonTitle }}\n </h2>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"copyEmbedTriggersJson\"\n >\n {{\n debugCopyDone\n ? t.popupDisplayRules.copyJsonDone\n : t.popupDisplayRules.copyJson\n }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:hover:bg-[var(--tpl-bg-hover)]\"\n @click=\"debugTriggersOpen = false\"\n >\n {{ t.popupDisplayRules.debugJsonClose }}\n </button>\n </div>\n </div>\n <pre\n class=\"tpl:m-0 tpl:flex-1 tpl:overflow-auto tpl:p-4 tpl:text-left tpl:font-mono tpl:text-[11px] tpl:leading-relaxed tpl:text-[var(--tpl-text)]\"\n >{{ embedTriggersWrappedJson }}</pre\n >\n <p\n class=\"tpl:m-0 tpl:border-t tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.popupDisplayRules.debugJsonHint }}\n </p>\n </div>\n </div>\n </Teleport>\n</template>\n","/**\n * Copies `--tpl-*` CSS variables and base typography from the nearest editor\n * theme root so portaled / fixed UI matches the editor surface (see MergeTagSuggestion).\n */\nexport function syncEditorThemeToPortal(\n trigger: HTMLElement | null | undefined,\n panel: HTMLElement | null | undefined,\n): void {\n if (!trigger || !panel) return;\n const themeRoot = trigger.closest<HTMLElement>(\"[data-tpl-theme]\");\n if (!themeRoot) return;\n const themeValue = themeRoot.getAttribute(\"data-tpl-theme\");\n if (themeValue) panel.setAttribute(\"data-tpl-theme\", themeValue);\n else panel.removeAttribute(\"data-tpl-theme\");\n\n const computed = window.getComputedStyle(themeRoot);\n for (let i = 0; i < computed.length; i++) {\n const prop = computed[i];\n if (prop.startsWith(\"--tpl-\")) {\n panel.style.setProperty(prop, computed.getPropertyValue(prop));\n }\n }\n panel.style.fontFamily = computed.fontFamily;\n panel.style.fontSize = computed.fontSize;\n panel.style.lineHeight = computed.lineHeight;\n}\n","<script setup lang=\"ts\">\nimport { CalendarDays, ChevronLeft, ChevronRight } from \"@lucide/vue\";\nimport {\n onClickOutside,\n useElementBounding,\n useEventListener,\n} from \"@vueuse/core\";\nimport { computed, nextTick, ref, watch } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { syncEditorThemeToPortal } from \"../utils/syncEditorThemeToPortal\";\n\nconst props = defineProps<{\n modelValue: string;\n placeholder: string;\n inputId: string;\n clearLabel: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst triggerRef = ref<HTMLElement | null>(null);\nconst popoverRef = ref<HTMLElement | null>(null);\n\nconst viewYear = ref(new Date().getFullYear());\nconst viewMonth = ref(new Date().getMonth());\n\nconst { bottom, left, width, update } = useElementBounding(triggerRef);\n\nconst popoverStyle = computed(() => ({\n top: `${bottom.value + 4}px`,\n left: `${left.value}px`,\n width: `${Math.max(width.value, 268)}px`,\n}));\n\nfunction pad2(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\nfunction toISO(y: number, monthIndex: number, day: number): string {\n return `${y}-${pad2(monthIndex + 1)}-${pad2(day)}`;\n}\n\nfunction parseISO(s: string): { y: number; m: number; d: number } | null {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s.trim());\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]) - 1;\n const d = Number(m[3]);\n const dt = new Date(y, mo, d);\n if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) {\n return null;\n }\n return { y, m: mo, d };\n}\n\nfunction isoToday(): string {\n const n = new Date();\n return toISO(n.getFullYear(), n.getMonth(), n.getDate());\n}\n\nfunction buildCalendarCells(y: number, monthIndex: number) {\n type Cell = { iso: string; day: number; inMonth: boolean };\n const cells: Cell[] = [];\n const first = new Date(y, monthIndex, 1);\n const startPad = first.getDay();\n const daysInMonth = new Date(y, monthIndex + 1, 0).getDate();\n\n const prevLast = new Date(y, monthIndex, 0);\n const prevDays = prevLast.getDate();\n const prevM = monthIndex === 0 ? 11 : monthIndex - 1;\n const prevY = monthIndex === 0 ? y - 1 : y;\n\n for (let i = startPad - 1; i >= 0; i--) {\n const day = prevDays - i;\n cells.push({\n iso: toISO(prevY, prevM, day),\n day,\n inMonth: false,\n });\n }\n\n for (let d = 1; d <= daysInMonth; d++) {\n cells.push({\n iso: toISO(y, monthIndex, d),\n day: d,\n inMonth: true,\n });\n }\n\n const nextY = monthIndex === 11 ? y + 1 : y;\n const nextM = monthIndex === 11 ? 0 : monthIndex + 1;\n let nextD = 1;\n while (cells.length % 7 !== 0) {\n cells.push({\n iso: toISO(nextY, nextM, nextD),\n day: nextD++,\n inMonth: false,\n });\n }\n return cells;\n}\n\nconst weekdayLabels = computed(() => {\n const labels: string[] = [];\n const sun = new Date(2024, 0, 7);\n for (let i = 0; i < 7; i++) {\n const d = new Date(sun);\n d.setDate(sun.getDate() + i);\n labels.push(\n new Intl.DateTimeFormat(undefined, { weekday: \"short\" }).format(d),\n );\n }\n return labels;\n});\n\nconst monthTitle = computed(() =>\n new Intl.DateTimeFormat(undefined, {\n month: \"long\",\n year: \"numeric\",\n }).format(new Date(viewYear.value, viewMonth.value, 1)),\n);\n\nconst cells = computed(() =>\n buildCalendarCells(viewYear.value, viewMonth.value),\n);\n\nconst todayStr = computed(() => isoToday());\n\nfunction formatDisplay(iso: string): string {\n const p = parseISO(iso);\n if (!p) return iso;\n return new Intl.DateTimeFormat(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n }).format(new Date(p.y, p.m, p.d));\n}\n\nfunction syncViewToModel(): void {\n const p = parseISO(props.modelValue);\n if (p) {\n viewYear.value = p.y;\n viewMonth.value = p.m;\n } else {\n const n = new Date();\n viewYear.value = n.getFullYear();\n viewMonth.value = n.getMonth();\n }\n}\n\nfunction toggleOpen(): void {\n open.value = !open.value;\n}\n\nwatch(open, async (isOpen) => {\n if (!isOpen) return;\n syncViewToModel();\n await nextTick();\n await nextTick();\n update();\n syncEditorThemeToPortal(triggerRef.value, popoverRef.value);\n});\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [triggerRef] },\n);\n\nuseEventListener(window, \"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Escape\") open.value = false;\n});\n\nuseEventListener(window, \"scroll\", () => open.value && update(), {\n capture: true,\n});\n\nuseEventListener(window, \"resize\", () => open.value && update());\n\nfunction prevMonth(): void {\n if (viewMonth.value === 0) {\n viewMonth.value = 11;\n viewYear.value--;\n } else {\n viewMonth.value--;\n }\n}\n\nfunction nextMonth(): void {\n if (viewMonth.value === 11) {\n viewMonth.value = 0;\n viewYear.value++;\n } else {\n viewMonth.value++;\n }\n}\n\nfunction pick(iso: string): void {\n emit(\"update:modelValue\", iso);\n open.value = false;\n}\n\nfunction clear(): void {\n emit(\"update:modelValue\", \"\");\n open.value = false;\n}\n\nfunction pickToday(): void {\n pick(isoToday());\n}\n</script>\n\n<template>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-0.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:pr-1\"\n >\n <button\n :id=\"inputId\"\n ref=\"triggerRef\"\n type=\"button\"\n class=\"tpl:flex tpl:min-w-0 tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:rounded-lg tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2.5 tpl:text-left tpl:text-sm tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :aria-expanded=\"open\"\n aria-haspopup=\"dialog\"\n @click=\"toggleOpen\"\n >\n <span\n :class=\"\n modelValue\n ? 'tpl:text-[var(--tpl-text)]'\n : 'tpl:text-[var(--tpl-text-muted)]'\n \"\n >\n {{ modelValue ? formatDisplay(modelValue) : placeholder }}\n </span>\n <CalendarDays\n :size=\"16\"\n :stroke-width=\"1.5\"\n class=\"tpl:ml-auto tpl:shrink-0 tpl:opacity-60\"\n aria-hidden=\"true\"\n />\n </button>\n <button\n v-if=\"modelValue\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"clearLabel\"\n @click=\"clear\"\n >\n ×\n </button>\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n class=\"tpl:fixed tpl:z-[100] tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:shadow-[var(--tpl-shadow-md)]\"\n :style=\"popoverStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"t.popupSchedule.calendarDialogLabel\"\n >\n <div\n class=\"tpl:mb-3 tpl:flex tpl:items-center tpl:justify-between tpl:gap-2\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.calendarPrevMonth\"\n @click=\"prevMonth\"\n >\n <ChevronLeft :size=\"18\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n <span\n class=\"tpl:flex-1 tpl:text-center tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ monthTitle }}\n </span>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.calendarNextMonth\"\n @click=\"nextMonth\"\n >\n <ChevronRight :size=\"18\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n class=\"tpl:grid tpl:grid-cols-7 tpl:gap-x-0 tpl:gap-y-1 tpl:text-[11px] tpl:font-medium tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n <span\n v-for=\"(wd, i) in weekdayLabels\"\n :key=\"`wd-${i}`\"\n class=\"tpl:block tpl:text-center\"\n >\n {{ wd }}\n </span>\n </div>\n\n <div class=\"tpl:mt-1 tpl:grid tpl:grid-cols-7 tpl:gap-y-1\">\n <button\n v-for=\"(cell, i) in cells\"\n :key=\"`${cell.iso}-${i}`\"\n type=\"button\"\n class=\"tpl:flex tpl:h-9 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-sm tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"[\n cell.inMonth\n ? 'tpl:text-[var(--tpl-text)]'\n : 'tpl:text-[var(--tpl-text-muted)] tpl:opacity-70',\n modelValue === cell.iso\n ? 'tpl:bg-[var(--tpl-primary)] tpl:font-medium tpl:text-white hover:tpl:bg-[var(--tpl-primary)]'\n : cell.iso === todayStr\n ? 'tpl:ring-1 tpl:ring-[var(--tpl-border)] tpl:ring-inset'\n : 'hover:tpl:bg-[var(--tpl-bg-hover)]',\n ]\"\n @click=\"pick(cell.iso)\"\n >\n {{ cell.day }}\n </button>\n </div>\n\n <div\n class=\"tpl:mt-3 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2 tpl:border-t tpl:border-[var(--tpl-border)] tpl:pt-3\"\n >\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2 hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"pickToday\"\n >\n {{ t.popupSchedule.calendarToday }}\n </button>\n <button\n v-if=\"modelValue\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"clear\"\n >\n {{ t.popupSchedule.calendarClear }}\n </button>\n </div>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { CalendarDays, ChevronLeft, ChevronRight } from \"@lucide/vue\";\nimport {\n onClickOutside,\n useElementBounding,\n useEventListener,\n} from \"@vueuse/core\";\nimport { computed, nextTick, ref, watch } from \"vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { syncEditorThemeToPortal } from \"../utils/syncEditorThemeToPortal\";\n\nconst props = defineProps<{\n modelValue: string;\n placeholder: string;\n inputId: string;\n clearLabel: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst open = ref(false);\nconst triggerRef = ref<HTMLElement | null>(null);\nconst popoverRef = ref<HTMLElement | null>(null);\n\nconst viewYear = ref(new Date().getFullYear());\nconst viewMonth = ref(new Date().getMonth());\n\nconst { bottom, left, width, update } = useElementBounding(triggerRef);\n\nconst popoverStyle = computed(() => ({\n top: `${bottom.value + 4}px`,\n left: `${left.value}px`,\n width: `${Math.max(width.value, 268)}px`,\n}));\n\nfunction pad2(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\nfunction toISO(y: number, monthIndex: number, day: number): string {\n return `${y}-${pad2(monthIndex + 1)}-${pad2(day)}`;\n}\n\nfunction parseISO(s: string): { y: number; m: number; d: number } | null {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s.trim());\n if (!m) return null;\n const y = Number(m[1]);\n const mo = Number(m[2]) - 1;\n const d = Number(m[3]);\n const dt = new Date(y, mo, d);\n if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) {\n return null;\n }\n return { y, m: mo, d };\n}\n\nfunction isoToday(): string {\n const n = new Date();\n return toISO(n.getFullYear(), n.getMonth(), n.getDate());\n}\n\nfunction buildCalendarCells(y: number, monthIndex: number) {\n type Cell = { iso: string; day: number; inMonth: boolean };\n const cells: Cell[] = [];\n const first = new Date(y, monthIndex, 1);\n const startPad = first.getDay();\n const daysInMonth = new Date(y, monthIndex + 1, 0).getDate();\n\n const prevLast = new Date(y, monthIndex, 0);\n const prevDays = prevLast.getDate();\n const prevM = monthIndex === 0 ? 11 : monthIndex - 1;\n const prevY = monthIndex === 0 ? y - 1 : y;\n\n for (let i = startPad - 1; i >= 0; i--) {\n const day = prevDays - i;\n cells.push({\n iso: toISO(prevY, prevM, day),\n day,\n inMonth: false,\n });\n }\n\n for (let d = 1; d <= daysInMonth; d++) {\n cells.push({\n iso: toISO(y, monthIndex, d),\n day: d,\n inMonth: true,\n });\n }\n\n const nextY = monthIndex === 11 ? y + 1 : y;\n const nextM = monthIndex === 11 ? 0 : monthIndex + 1;\n let nextD = 1;\n while (cells.length % 7 !== 0) {\n cells.push({\n iso: toISO(nextY, nextM, nextD),\n day: nextD++,\n inMonth: false,\n });\n }\n return cells;\n}\n\nconst weekdayLabels = computed(() => {\n const labels: string[] = [];\n const sun = new Date(2024, 0, 7);\n for (let i = 0; i < 7; i++) {\n const d = new Date(sun);\n d.setDate(sun.getDate() + i);\n labels.push(\n new Intl.DateTimeFormat(undefined, { weekday: \"short\" }).format(d),\n );\n }\n return labels;\n});\n\nconst monthTitle = computed(() =>\n new Intl.DateTimeFormat(undefined, {\n month: \"long\",\n year: \"numeric\",\n }).format(new Date(viewYear.value, viewMonth.value, 1)),\n);\n\nconst cells = computed(() =>\n buildCalendarCells(viewYear.value, viewMonth.value),\n);\n\nconst todayStr = computed(() => isoToday());\n\nfunction formatDisplay(iso: string): string {\n const p = parseISO(iso);\n if (!p) return iso;\n return new Intl.DateTimeFormat(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n }).format(new Date(p.y, p.m, p.d));\n}\n\nfunction syncViewToModel(): void {\n const p = parseISO(props.modelValue);\n if (p) {\n viewYear.value = p.y;\n viewMonth.value = p.m;\n } else {\n const n = new Date();\n viewYear.value = n.getFullYear();\n viewMonth.value = n.getMonth();\n }\n}\n\nfunction toggleOpen(): void {\n open.value = !open.value;\n}\n\nwatch(open, async (isOpen) => {\n if (!isOpen) return;\n syncViewToModel();\n await nextTick();\n await nextTick();\n update();\n syncEditorThemeToPortal(triggerRef.value, popoverRef.value);\n});\n\nonClickOutside(\n popoverRef,\n () => {\n open.value = false;\n },\n { ignore: [triggerRef] },\n);\n\nuseEventListener(window, \"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Escape\") open.value = false;\n});\n\nuseEventListener(window, \"scroll\", () => open.value && update(), {\n capture: true,\n});\n\nuseEventListener(window, \"resize\", () => open.value && update());\n\nfunction prevMonth(): void {\n if (viewMonth.value === 0) {\n viewMonth.value = 11;\n viewYear.value--;\n } else {\n viewMonth.value--;\n }\n}\n\nfunction nextMonth(): void {\n if (viewMonth.value === 11) {\n viewMonth.value = 0;\n viewYear.value++;\n } else {\n viewMonth.value++;\n }\n}\n\nfunction pick(iso: string): void {\n emit(\"update:modelValue\", iso);\n open.value = false;\n}\n\nfunction clear(): void {\n emit(\"update:modelValue\", \"\");\n open.value = false;\n}\n\nfunction pickToday(): void {\n pick(isoToday());\n}\n</script>\n\n<template>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-0.5 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:pr-1\"\n >\n <button\n :id=\"inputId\"\n ref=\"triggerRef\"\n type=\"button\"\n class=\"tpl:flex tpl:min-w-0 tpl:flex-1 tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:rounded-lg tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-2.5 tpl:text-left tpl:text-sm tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :aria-expanded=\"open\"\n aria-haspopup=\"dialog\"\n @click=\"toggleOpen\"\n >\n <span\n :class=\"\n modelValue\n ? 'tpl:text-[var(--tpl-text)]'\n : 'tpl:text-[var(--tpl-text-muted)]'\n \"\n >\n {{ modelValue ? formatDisplay(modelValue) : placeholder }}\n </span>\n <CalendarDays\n :size=\"16\"\n :stroke-width=\"1.5\"\n class=\"tpl:ml-auto tpl:shrink-0 tpl:opacity-60\"\n aria-hidden=\"true\"\n />\n </button>\n <button\n v-if=\"modelValue\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"clearLabel\"\n @click=\"clear\"\n >\n ×\n </button>\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"open\"\n ref=\"popoverRef\"\n class=\"tpl:fixed tpl:z-[100] tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:shadow-[var(--tpl-shadow-md)]\"\n :style=\"popoverStyle\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"t.popupSchedule.calendarDialogLabel\"\n >\n <div\n class=\"tpl:mb-3 tpl:flex tpl:items-center tpl:justify-between tpl:gap-2\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.calendarPrevMonth\"\n @click=\"prevMonth\"\n >\n <ChevronLeft :size=\"18\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n <span\n class=\"tpl:flex-1 tpl:text-center tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ monthTitle }}\n </span>\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.calendarNextMonth\"\n @click=\"nextMonth\"\n >\n <ChevronRight :size=\"18\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n class=\"tpl:grid tpl:grid-cols-7 tpl:gap-x-0 tpl:gap-y-1 tpl:text-[11px] tpl:font-medium tpl:uppercase tpl:tracking-wide tpl:text-[var(--tpl-text-muted)]\"\n >\n <span\n v-for=\"(wd, i) in weekdayLabels\"\n :key=\"`wd-${i}`\"\n class=\"tpl:block tpl:text-center\"\n >\n {{ wd }}\n </span>\n </div>\n\n <div class=\"tpl:mt-1 tpl:grid tpl:grid-cols-7 tpl:gap-y-1\">\n <button\n v-for=\"(cell, i) in cells\"\n :key=\"`${cell.iso}-${i}`\"\n type=\"button\"\n class=\"tpl:flex tpl:h-9 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-sm tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\"\n :class=\"[\n cell.inMonth\n ? 'tpl:text-[var(--tpl-text)]'\n : 'tpl:text-[var(--tpl-text-muted)] tpl:opacity-70',\n modelValue === cell.iso\n ? 'tpl:bg-[var(--tpl-primary)] tpl:font-medium tpl:text-white hover:tpl:bg-[var(--tpl-primary)]'\n : cell.iso === todayStr\n ? 'tpl:ring-1 tpl:ring-[var(--tpl-border)] tpl:ring-inset'\n : 'hover:tpl:bg-[var(--tpl-bg-hover)]',\n ]\"\n @click=\"pick(cell.iso)\"\n >\n {{ cell.day }}\n </button>\n </div>\n\n <div\n class=\"tpl:mt-3 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2 tpl:border-t tpl:border-[var(--tpl-border)] tpl:pt-3\"\n >\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-primary)] tpl:underline tpl:underline-offset-2 hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"pickToday\"\n >\n {{ t.popupSchedule.calendarToday }}\n </button>\n <button\n v-if=\"modelValue\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"clear\"\n >\n {{ t.popupSchedule.calendarClear }}\n </button>\n </div>\n </div>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport type {\n PopupEmbedSettings,\n PopupScheduleDayPreset,\n PopupScheduleSlot,\n} from \"@aswin.dev/types\";\nimport { POPUP_SCHEDULE_TIMEZONES } from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupDesignSwitch from \"./PopupDesignSwitch.vue\";\nimport PopupScheduleDatePicker from \"./PopupScheduleDatePicker.vue\";\n\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupScheduleView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rootClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl-popup-schedule tpl:w-full tpl:overflow-x-hidden tpl:px-4 tpl:pb-6 tpl:pt-4 tpl:bg-[var(--tpl-bg)]\"\n : \"tpl-popup-schedule tpl:mx-auto tpl:w-full tpl:max-w-3xl tpl:px-6 tpl:py-8 tpl:pb-14 tpl:bg-[var(--tpl-bg)]\",\n);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nconst selectClass =\n \"tpl:w-full tpl:min-w-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\";\n\nconst labelClass =\n \"tpl:block tpl:mb-1.5 tpl:text-[13px] tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\";\n\nfunction patchSchedule(patch: Partial<PopupEmbedSettings[\"schedule\"]>): void {\n const cur = popup.value;\n const schedule = { ...cur.schedule, ...patch };\n editor!.updateSettings({ popup: { ...cur, schedule } } as Parameters<\n BaseEditorReturn[\"updateSettings\"]\n >[0]);\n}\n\nfunction replaceSlots(slots: PopupScheduleSlot[]): void {\n patchSchedule({ slots });\n}\n\nfunction updateSlot(id: string, patch: Partial<PopupScheduleSlot>): void {\n replaceSlots(\n popup.value.schedule.slots.map((s) =>\n s.id === id ? { ...s, ...patch } : s,\n ),\n );\n}\n\nfunction removeSlot(id: string): void {\n replaceSlots(popup.value.schedule.slots.filter((s) => s.id !== id));\n}\n\nfunction addSlot(): void {\n const id =\n globalThis.crypto?.randomUUID?.() ?? `slot-${Date.now().toString(36)}`;\n replaceSlots([\n ...popup.value.schedule.slots,\n {\n id,\n dayPreset: \"everyday\",\n startTime: \"00:00\",\n endTime: \"23:59\",\n },\n ]);\n}\n\nconst quarterHourTimes = computed(() => {\n const out: string[] = [];\n for (let h = 0; h < 24; h++) {\n for (const m of [0, 15, 30, 45]) {\n out.push(`${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}`);\n }\n }\n if (!out.includes(\"23:59\")) out.push(\"23:59\");\n return out;\n});\n\nconst timezoneChoices = computed(() => {\n const cur = popup.value.schedule.timeZone;\n const known = POPUP_SCHEDULE_TIMEZONES.some((z) => z.id === cur);\n if (known) return [...POPUP_SCHEDULE_TIMEZONES];\n return [{ id: cur, label: cur }, ...POPUP_SCHEDULE_TIMEZONES];\n});\n\nconst dayOptions = computed(() =>\n (\n [\n \"everyday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\",\n \"sunday\",\n ] as PopupScheduleDayPreset[]\n ).map((id) => ({\n id,\n label:\n t.popupSchedule.dayLabels[id as keyof typeof t.popupSchedule.dayLabels],\n })),\n);\n\nfunction onTimezoneChange(e: Event): void {\n patchSchedule({\n timeZone: (e.target as HTMLSelectElement).value,\n });\n}\n\nfunction onSlotDayChange(id: string, e: Event): void {\n updateSlot(id, {\n dayPreset: (e.target as HTMLSelectElement).value as PopupScheduleDayPreset,\n });\n}\n\nfunction onSlotStartChange(id: string, e: Event): void {\n updateSlot(id, { startTime: (e.target as HTMLSelectElement).value });\n}\n\nfunction onSlotEndChange(id: string, e: Event): void {\n updateSlot(id, { endTime: (e.target as HTMLSelectElement).value });\n}\n</script>\n\n<template>\n <div :class=\"rootClass\">\n <div class=\"tpl:mb-5\">\n <label class=\"tpl:sr-only\" for=\"popup-schedule-timezone\">{{\n t.popupSchedule.timeZone\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.timeZone }}</span>\n <select\n id=\"popup-schedule-timezone\"\n :class=\"selectClass\"\n :value=\"popup.schedule.timeZone\"\n @change=\"onTimezoneChange\"\n >\n <option v-for=\"z in timezoneChoices\" :key=\"z.id\" :value=\"z.id\">\n {{ z.label }}\n </option>\n </select>\n </div>\n\n <div class=\"tpl:mb-3 tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div>\n <label class=\"tpl:sr-only\" for=\"popup-schedule-start-date\">{{\n t.popupSchedule.startDate\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.startDate }}</span>\n <PopupScheduleDatePicker\n input-id=\"popup-schedule-start-date\"\n :model-value=\"popup.schedule.startDate\"\n :placeholder=\"t.popupSchedule.startImmediate\"\n :clear-label=\"t.popupSchedule.clearStartDate\"\n @update:model-value=\"patchSchedule({ startDate: $event })\"\n />\n </div>\n <div>\n <label class=\"tpl:sr-only\" for=\"popup-schedule-end-date\">{{\n t.popupSchedule.endDate\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.endDate }}</span>\n <PopupScheduleDatePicker\n input-id=\"popup-schedule-end-date\"\n :model-value=\"popup.schedule.endDate\"\n :placeholder=\"t.popupSchedule.endNone\"\n :clear-label=\"t.popupSchedule.clearEndDate\"\n @update:model-value=\"patchSchedule({ endDate: $event })\"\n />\n </div>\n </div>\n\n <a\n href=\"https://docs.templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:mb-6 tpl:inline-block tpl:text-[13px] tpl:underline tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupSchedule.needHelp }}\n </a>\n\n <div class=\"tpl:mb-5 tpl:flex tpl:items-center tpl:gap-3\">\n <PopupDesignSwitch\n tone=\"neutral\"\n :checked=\"popup.schedule.detailedScheduleEnabled\"\n @toggle=\"\n patchSchedule({\n detailedScheduleEnabled: !popup.schedule.detailedScheduleEnabled,\n })\n \"\n />\n <span class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\">{{\n t.popupSchedule.detailedSchedule\n }}</span>\n </div>\n\n <template v-if=\"popup.schedule.detailedScheduleEnabled\">\n <p :class=\"`${labelClass} tpl:mb-2 tpl:mt-1`\">\n {{ t.popupSchedule.detailedScheduleHeading }}\n </p>\n\n <div class=\"tpl:space-y-2 tpl:mb-4\">\n <div\n v-for=\"slot in popup.schedule.slots\"\n :key=\"slot.id\"\n class=\"tpl:grid tpl:grid-cols-[auto_minmax(0,1fr)_4.5rem_4.5rem] tpl:items-center tpl:gap-2\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-9 tpl:w-[26px] tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.removeSlot\"\n @click=\"removeSlot(slot.id)\"\n >\n <Trash2 :size=\"16\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n <select\n :class=\"selectClass\"\n :value=\"slot.dayPreset\"\n @change=\"onSlotDayChange(slot.id, $event)\"\n >\n <option v-for=\"opt in dayOptions\" :key=\"opt.id\" :value=\"opt.id\">\n {{ opt.label }}\n </option>\n </select>\n <select\n :class=\"selectClass\"\n :value=\"slot.startTime\"\n @change=\"onSlotStartChange(slot.id, $event)\"\n >\n <option\n v-for=\"tm in quarterHourTimes\"\n :key=\"`s-${slot.id}-${tm}`\"\n :value=\"tm\"\n >\n {{ tm }}\n </option>\n </select>\n <select\n :class=\"selectClass\"\n :value=\"slot.endTime\"\n @change=\"onSlotEndChange(slot.id, $event)\"\n >\n <option\n v-for=\"tm in quarterHourTimes\"\n :key=\"`e-${slot.id}-${tm}`\"\n :value=\"tm\"\n >\n {{ tm }}\n </option>\n </select>\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-lg tpl:border tpl:border-[var(--tpl-text)] tpl:bg-transparent tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)] hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-offset-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"addSlot\"\n >\n {{ t.popupSchedule.addTimeSlot }}\n </button>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport type {\n PopupEmbedSettings,\n PopupScheduleDayPreset,\n PopupScheduleSlot,\n} from \"@aswin.dev/types\";\nimport { POPUP_SCHEDULE_TIMEZONES } from \"@aswin.dev/types\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { BaseEditorReturn } from \"../composables/useEditorCore\";\nimport { resolvePopupEmbed } from \"../utils/resolvePopupEmbed\";\nimport PopupDesignSwitch from \"./PopupDesignSwitch.vue\";\nimport PopupScheduleDatePicker from \"./PopupScheduleDatePicker.vue\";\n\nconst props = withDefaults(\n defineProps<{ layout?: \"standalone\" | \"popupAdjacent\" }>(),\n { layout: \"standalone\" },\n);\n\nconst editor = inject(EDITOR_KEY) as BaseEditorReturn | null;\nif (!editor) {\n throw new Error(\"PopupScheduleView requires EDITOR_KEY\");\n}\n\nconst { t } = useI18n();\n\nconst rootClass = computed(() =>\n props.layout === \"popupAdjacent\"\n ? \"tpl-popup-schedule tpl:w-full tpl:overflow-x-hidden tpl:px-4 tpl:pb-6 tpl:pt-4 tpl:bg-[var(--tpl-bg)]\"\n : \"tpl-popup-schedule tpl:mx-auto tpl:w-full tpl:max-w-3xl tpl:px-6 tpl:py-8 tpl:pb-14 tpl:bg-[var(--tpl-bg)]\",\n);\n\nconst popup = computed(() => resolvePopupEmbed(editor!.content.value.settings));\n\nconst selectClass =\n \"tpl:w-full tpl:min-w-0 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none focus-visible:tpl:ring-2 focus-visible:tpl:ring-[var(--tpl-primary)] focus-visible:tpl:ring-offset-2 focus-visible:tpl:ring-offset-[var(--tpl-bg)]\";\n\nconst labelClass =\n \"tpl:block tpl:mb-1.5 tpl:text-[13px] tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\";\n\nfunction patchSchedule(patch: Partial<PopupEmbedSettings[\"schedule\"]>): void {\n const cur = popup.value;\n const schedule = { ...cur.schedule, ...patch };\n editor!.updateSettings({ popup: { ...cur, schedule } } as Parameters<\n BaseEditorReturn[\"updateSettings\"]\n >[0]);\n}\n\nfunction replaceSlots(slots: PopupScheduleSlot[]): void {\n patchSchedule({ slots });\n}\n\nfunction updateSlot(id: string, patch: Partial<PopupScheduleSlot>): void {\n replaceSlots(\n popup.value.schedule.slots.map((s) =>\n s.id === id ? { ...s, ...patch } : s,\n ),\n );\n}\n\nfunction removeSlot(id: string): void {\n replaceSlots(popup.value.schedule.slots.filter((s) => s.id !== id));\n}\n\nfunction addSlot(): void {\n const id =\n globalThis.crypto?.randomUUID?.() ?? `slot-${Date.now().toString(36)}`;\n replaceSlots([\n ...popup.value.schedule.slots,\n {\n id,\n dayPreset: \"everyday\",\n startTime: \"00:00\",\n endTime: \"23:59\",\n },\n ]);\n}\n\nconst quarterHourTimes = computed(() => {\n const out: string[] = [];\n for (let h = 0; h < 24; h++) {\n for (const m of [0, 15, 30, 45]) {\n out.push(`${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}`);\n }\n }\n if (!out.includes(\"23:59\")) out.push(\"23:59\");\n return out;\n});\n\nconst timezoneChoices = computed(() => {\n const cur = popup.value.schedule.timeZone;\n const known = POPUP_SCHEDULE_TIMEZONES.some((z) => z.id === cur);\n if (known) return [...POPUP_SCHEDULE_TIMEZONES];\n return [{ id: cur, label: cur }, ...POPUP_SCHEDULE_TIMEZONES];\n});\n\nconst dayOptions = computed(() =>\n (\n [\n \"everyday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\",\n \"sunday\",\n ] as PopupScheduleDayPreset[]\n ).map((id) => ({\n id,\n label:\n t.popupSchedule.dayLabels[id as keyof typeof t.popupSchedule.dayLabels],\n })),\n);\n\nfunction onTimezoneChange(e: Event): void {\n patchSchedule({\n timeZone: (e.target as HTMLSelectElement).value,\n });\n}\n\nfunction onSlotDayChange(id: string, e: Event): void {\n updateSlot(id, {\n dayPreset: (e.target as HTMLSelectElement).value as PopupScheduleDayPreset,\n });\n}\n\nfunction onSlotStartChange(id: string, e: Event): void {\n updateSlot(id, { startTime: (e.target as HTMLSelectElement).value });\n}\n\nfunction onSlotEndChange(id: string, e: Event): void {\n updateSlot(id, { endTime: (e.target as HTMLSelectElement).value });\n}\n</script>\n\n<template>\n <div :class=\"rootClass\">\n <div class=\"tpl:mb-5\">\n <label class=\"tpl:sr-only\" for=\"popup-schedule-timezone\">{{\n t.popupSchedule.timeZone\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.timeZone }}</span>\n <select\n id=\"popup-schedule-timezone\"\n :class=\"selectClass\"\n :value=\"popup.schedule.timeZone\"\n @change=\"onTimezoneChange\"\n >\n <option v-for=\"z in timezoneChoices\" :key=\"z.id\" :value=\"z.id\">\n {{ z.label }}\n </option>\n </select>\n </div>\n\n <div class=\"tpl:mb-3 tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div>\n <label class=\"tpl:sr-only\" for=\"popup-schedule-start-date\">{{\n t.popupSchedule.startDate\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.startDate }}</span>\n <PopupScheduleDatePicker\n input-id=\"popup-schedule-start-date\"\n :model-value=\"popup.schedule.startDate\"\n :placeholder=\"t.popupSchedule.startImmediate\"\n :clear-label=\"t.popupSchedule.clearStartDate\"\n @update:model-value=\"patchSchedule({ startDate: $event })\"\n />\n </div>\n <div>\n <label class=\"tpl:sr-only\" for=\"popup-schedule-end-date\">{{\n t.popupSchedule.endDate\n }}</label>\n <span :class=\"labelClass\">{{ t.popupSchedule.endDate }}</span>\n <PopupScheduleDatePicker\n input-id=\"popup-schedule-end-date\"\n :model-value=\"popup.schedule.endDate\"\n :placeholder=\"t.popupSchedule.endNone\"\n :clear-label=\"t.popupSchedule.clearEndDate\"\n @update:model-value=\"patchSchedule({ endDate: $event })\"\n />\n </div>\n </div>\n\n <a\n href=\"https://docs.templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:mb-6 tpl:inline-block tpl:text-[13px] tpl:underline tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\"\n >\n {{ t.popupSchedule.needHelp }}\n </a>\n\n <div class=\"tpl:mb-5 tpl:flex tpl:items-center tpl:gap-3\">\n <PopupDesignSwitch\n tone=\"neutral\"\n :checked=\"popup.schedule.detailedScheduleEnabled\"\n @toggle=\"\n patchSchedule({\n detailedScheduleEnabled: !popup.schedule.detailedScheduleEnabled,\n })\n \"\n />\n <span class=\"tpl:text-sm tpl:leading-snug tpl:text-[var(--tpl-text)]\">{{\n t.popupSchedule.detailedSchedule\n }}</span>\n </div>\n\n <template v-if=\"popup.schedule.detailedScheduleEnabled\">\n <p :class=\"`${labelClass} tpl:mb-2 tpl:mt-1`\">\n {{ t.popupSchedule.detailedScheduleHeading }}\n </p>\n\n <div class=\"tpl:space-y-2 tpl:mb-4\">\n <div\n v-for=\"slot in popup.schedule.slots\"\n :key=\"slot.id\"\n class=\"tpl:grid tpl:grid-cols-[auto_minmax(0,1fr)_4.5rem_4.5rem] tpl:items-center tpl:gap-2\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:h-9 tpl:w-[26px] tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n :aria-label=\"t.popupSchedule.removeSlot\"\n @click=\"removeSlot(slot.id)\"\n >\n <Trash2 :size=\"16\" :stroke-width=\"1.75\" aria-hidden=\"true\" />\n </button>\n <select\n :class=\"selectClass\"\n :value=\"slot.dayPreset\"\n @change=\"onSlotDayChange(slot.id, $event)\"\n >\n <option v-for=\"opt in dayOptions\" :key=\"opt.id\" :value=\"opt.id\">\n {{ opt.label }}\n </option>\n </select>\n <select\n :class=\"selectClass\"\n :value=\"slot.startTime\"\n @change=\"onSlotStartChange(slot.id, $event)\"\n >\n <option\n v-for=\"tm in quarterHourTimes\"\n :key=\"`s-${slot.id}-${tm}`\"\n :value=\"tm\"\n >\n {{ tm }}\n </option>\n </select>\n <select\n :class=\"selectClass\"\n :value=\"slot.endTime\"\n @change=\"onSlotEndChange(slot.id, $event)\"\n >\n <option\n v-for=\"tm in quarterHourTimes\"\n :key=\"`e-${slot.id}-${tm}`\"\n :value=\"tm\"\n >\n {{ tm }}\n </option>\n </select>\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"tpl:inline-flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-lg tpl:border tpl:border-[var(--tpl-text)] tpl:bg-transparent tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)] hover:tpl:bg-[var(--tpl-bg-hover)] focus-visible:tpl:outline focus-visible:tpl:outline-2 focus-visible:tpl:outline-offset-2 focus-visible:tpl:outline-[var(--tpl-primary)]\"\n @click=\"addSlot\"\n >\n {{ t.popupSchedule.addTimeSlot }}\n </button>\n </template>\n </div>\n</template>\n"],"mappings":"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"}