@templatical/editor 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/dist/{AiChatSidebar-a8TPBAl2.js → AiChatSidebar-DqSoJAXx.js} +1 -1
  2. package/dist/{AiFeatureMenu-CroDMqRG.js → AiFeatureMenu-CoBvE72q.js} +1 -1
  3. package/dist/{BlockPreviewCanvas-Z9SZIgfA.js → BlockPreviewCanvas-W6-MRpWz.js} +33 -33
  4. package/dist/{CloudHeaderExtras-CVqHeqc6.js → CloudHeaderExtras-AbsNuL9T.js} +1 -1
  5. package/dist/{CloudPanels-CmEpQUJB.js → CloudPanels-BNL6rSeB.js} +2 -2
  6. package/dist/{CollaboratorBar-AIQC9fEF.js → CollaboratorBar-DlZ-wOL_.js} +1 -1
  7. package/dist/{CountdownToolbar-DkC-qSgq.js → CountdownToolbar-Bnq25sR-.js} +3 -3
  8. package/dist/{DesignReferenceSidebar-OoiYJAKS.js → DesignReferenceSidebar-Cl5eE8Kn.js} +1 -1
  9. package/dist/{LogicTagInsertButton-BimKobaG.js → LogicTagInsertButton-SI6irEtn.js} +243 -219
  10. package/dist/{MergeTagInput-DqpVzhy7.js → MergeTagInput-BMQGNh1k.js} +1 -1
  11. package/dist/{MergeTagModeToggle-BkIiAgpQ.js → MergeTagModeToggle-Cm9s-w6o.js} +1 -1
  12. package/dist/{ParagraphEditor-DaZ03d1U.js → ParagraphEditor-CnBPQCVo.js} +19 -19
  13. package/dist/{RestoreVersionDialog-DHb1mPoF.js → RestoreVersionDialog-B13whpxH.js} +1 -1
  14. package/dist/{RichTextEditorContent-AEtfKvLz.js → RichTextEditorContent-DOvhgc-7.js} +3 -3
  15. package/dist/{SaveBlockDialog-hGwLDWfE.js → SaveBlockDialog-b5yrQfoN.js} +5 -5
  16. package/dist/{SavedBlocksBrowserModal-C0lYVBFi.js → SavedBlocksBrowserModal-DpqK4Zqp.js} +4 -4
  17. package/dist/{SavedBlocksPanels-CaTbeVDv.js → SavedBlocksPanels-C2wURIlT.js} +1 -1
  18. package/dist/{SavedBlocksPickBar-DR2afid2.js → SavedBlocksPickBar-Bx9_SXVA.js} +1 -1
  19. package/dist/{TemplateSettings-BD1WNP4C.js → TemplateSettings-Gn9ps1R6.js} +3 -3
  20. package/dist/{TestEmailModal-DqoosG1u.js → TestEmailModal-DaIaklcg.js} +5 -5
  21. package/dist/{TestEmailPanel-DpozqyHF.js → TestEmailPanel-CKSvzakC.js} +1 -1
  22. package/dist/{TitleEditor-BCBdZLt_.js → TitleEditor-C66Jk73n.js} +7 -7
  23. package/dist/{ToggleSwitch-CJfQ8u57.js → ToggleSwitch-BqttX9lY.js} +1 -1
  24. package/dist/{Toolbar-C7SjilzH.js → Toolbar-DVsKYQaC.js} +846 -553
  25. package/dist/{TplModal-CVO-2KrU.js → TplModal-DRQln7xH.js} +2 -2
  26. package/dist/{VersionHistoryPanels-BzF7dmwa.js → VersionHistoryPanels-Df5WjNah.js} +1 -1
  27. package/dist/{WrapperBlock-BDjOn3fY.js → WrapperBlock-B0cUORGz.js} +4 -4
  28. package/dist/{blockTypeIcons-B0Bq1WT1.js → blockTypeIcons-DfQNZnl7.js} +1 -1
  29. package/dist/bundle-stats.json +6 -6
  30. package/dist/{ca-Ckgw9yd0.js → ca--zGWn9AU.js} +14 -0
  31. package/dist/cdn/chunks/{AiChatSidebar-mKImUXi6.js → AiChatSidebar-DD17BwXn.js} +10 -10
  32. package/dist/cdn/chunks/{AiChatSidebar-mKImUXi6.js.map → AiChatSidebar-DD17BwXn.js.map} +1 -1
  33. package/dist/cdn/chunks/{AiFeatureMenu-Cx3qdStQ.js → AiFeatureMenu-Di381OsL.js} +5 -5
  34. package/dist/cdn/chunks/{AiFeatureMenu-Cx3qdStQ.js.map → AiFeatureMenu-Di381OsL.js.map} +1 -1
  35. package/dist/cdn/chunks/{BlockIssueBadge-DS1pUf5g.js → BlockIssueBadge-BR_N3nRh.js} +2 -2
  36. package/dist/cdn/chunks/{BlockIssueBadge-DS1pUf5g.js.map → BlockIssueBadge-BR_N3nRh.js.map} +1 -1
  37. package/dist/cdn/chunks/{BlockPreviewCanvas-D31TNib3.js → BlockPreviewCanvas-BXpcAiiY.js} +28 -28
  38. package/dist/cdn/chunks/{BlockPreviewCanvas-D31TNib3.js.map → BlockPreviewCanvas-BXpcAiiY.js.map} +1 -1
  39. package/dist/cdn/chunks/{CloudHeaderExtras-0zdlKiq7.js → CloudHeaderExtras-BEM2eWfF.js} +3 -3
  40. package/dist/cdn/chunks/{CloudHeaderExtras-0zdlKiq7.js.map → CloudHeaderExtras-BEM2eWfF.js.map} +1 -1
  41. package/dist/cdn/chunks/{CloudPanels-DPh71yOO.js → CloudPanels-D7nK1-bm.js} +4 -4
  42. package/dist/cdn/chunks/{CloudPanels-DPh71yOO.js.map → CloudPanels-D7nK1-bm.js.map} +1 -1
  43. package/dist/cdn/chunks/{CollaboratorBar-lCZU8qkg.js → CollaboratorBar-D1SUwahw.js} +3 -3
  44. package/dist/cdn/chunks/{CollaboratorBar-lCZU8qkg.js.map → CollaboratorBar-D1SUwahw.js.map} +1 -1
  45. package/dist/cdn/chunks/{CommentsPanel-BtAGZfF6.js → CommentsPanel-qsLMAlJU.js} +2 -2
  46. package/dist/cdn/chunks/{CommentsPanel-BtAGZfF6.js.map → CommentsPanel-qsLMAlJU.js.map} +1 -1
  47. package/dist/cdn/chunks/{CommentsSidebar-Ws07MXYj.js → CommentsSidebar-CTwEE-s_.js} +16 -16
  48. package/dist/cdn/chunks/{CommentsSidebar-Ws07MXYj.js.map → CommentsSidebar-CTwEE-s_.js.map} +1 -1
  49. package/dist/cdn/chunks/{CountdownToolbar-Bu93WiS8.js → CountdownToolbar-BJTcnIQ-.js} +4 -4
  50. package/dist/cdn/chunks/{CountdownToolbar-Bu93WiS8.js.map → CountdownToolbar-BJTcnIQ-.js.map} +1 -1
  51. package/dist/cdn/chunks/{DesignReferenceSidebar-BlSdh5B7.js → DesignReferenceSidebar-Dl3eEGtS.js} +9 -9
  52. package/dist/cdn/chunks/{DesignReferenceSidebar-BlSdh5B7.js.map → DesignReferenceSidebar-Dl3eEGtS.js.map} +1 -1
  53. package/dist/cdn/chunks/{IssuesPanel-CKkv9z9g.js → IssuesPanel-L9Gq_T8n.js} +8 -8
  54. package/dist/cdn/chunks/{IssuesPanel-CKkv9z9g.js.map → IssuesPanel-L9Gq_T8n.js.map} +1 -1
  55. package/dist/cdn/chunks/{LogicTagInsertButton-CHc1yZ9v.js → LogicTagInsertButton-3pIbVE9p.js} +250 -226
  56. package/dist/cdn/chunks/LogicTagInsertButton-3pIbVE9p.js.map +1 -0
  57. package/dist/cdn/chunks/{MediaEditModal-Cw9MhjVz.js → MediaEditModal-rTjYl-fV.js} +2 -2
  58. package/dist/cdn/chunks/{MediaEditModal-Cw9MhjVz.js.map → MediaEditModal-rTjYl-fV.js.map} +1 -1
  59. package/dist/cdn/chunks/{MediaPanels-FD_aX3H5.js → MediaPanels-DMXRQVqg.js} +2 -2
  60. package/dist/cdn/chunks/{MediaPanels-FD_aX3H5.js.map → MediaPanels-DMXRQVqg.js.map} +1 -1
  61. package/dist/cdn/chunks/{MergeTagInput--qTYfCWk.js → MergeTagInput-JcBzr77a.js} +2 -2
  62. package/dist/cdn/chunks/{MergeTagInput--qTYfCWk.js.map → MergeTagInput-JcBzr77a.js.map} +1 -1
  63. package/dist/cdn/chunks/{MergeTagModeToggle-CdWIGo3Q.js → MergeTagModeToggle-CYmueXS2.js} +10 -10
  64. package/dist/cdn/chunks/{MergeTagModeToggle-CdWIGo3Q.js.map → MergeTagModeToggle-CYmueXS2.js.map} +1 -1
  65. package/dist/cdn/chunks/{ParagraphEditor-w2qEZTkM.js → ParagraphEditor-DbjFDnYm.js} +39 -39
  66. package/dist/cdn/chunks/{ParagraphEditor-w2qEZTkM.js.map → ParagraphEditor-DbjFDnYm.js.map} +1 -1
  67. package/dist/cdn/chunks/{RestoreVersionDialog-cnWVN3nX.js → RestoreVersionDialog-pANCWcce.js} +3 -3
  68. package/dist/cdn/chunks/{RestoreVersionDialog-cnWVN3nX.js.map → RestoreVersionDialog-pANCWcce.js.map} +1 -1
  69. package/dist/cdn/chunks/{RichTextEditorContent-Fzuu9pLP.js → RichTextEditorContent-BtM1or8A.js} +5 -5
  70. package/dist/cdn/chunks/{RichTextEditorContent-Fzuu9pLP.js.map → RichTextEditorContent-BtM1or8A.js.map} +1 -1
  71. package/dist/cdn/chunks/{SaveBlockDialog-jxQtKnVs.js → SaveBlockDialog-CGM3mrGj.js} +38 -38
  72. package/dist/cdn/chunks/{SaveBlockDialog-jxQtKnVs.js.map → SaveBlockDialog-CGM3mrGj.js.map} +1 -1
  73. package/dist/cdn/chunks/{SavedBlocksBrowserModal-DpXWf4Pi.js → SavedBlocksBrowserModal-C6Z8AvtD.js} +10 -10
  74. package/dist/cdn/chunks/{SavedBlocksBrowserModal-DpXWf4Pi.js.map → SavedBlocksBrowserModal-C6Z8AvtD.js.map} +1 -1
  75. package/dist/cdn/chunks/{SavedBlocksPanels-B-ASmf1P.js → SavedBlocksPanels-DNwKmnhp.js} +2 -2
  76. package/dist/cdn/chunks/{SavedBlocksPanels-B-ASmf1P.js.map → SavedBlocksPanels-DNwKmnhp.js.map} +1 -1
  77. package/dist/cdn/chunks/{SavedBlocksPickBar-CEFIQ8Nd.js → SavedBlocksPickBar-D2p1ufB4.js} +3 -3
  78. package/dist/cdn/chunks/{SavedBlocksPickBar-CEFIQ8Nd.js.map → SavedBlocksPickBar-D2p1ufB4.js.map} +1 -1
  79. package/dist/cdn/chunks/{TemplateScoringPanel-d_Ppowsq.js → TemplateScoringPanel-BDB4RErx.js} +19 -19
  80. package/dist/cdn/chunks/{TemplateScoringPanel-d_Ppowsq.js.map → TemplateScoringPanel-BDB4RErx.js.map} +1 -1
  81. package/dist/cdn/chunks/{TemplateSettings-Dr5OJ0Gi.js → TemplateSettings-DVN1t3yF.js} +9 -9
  82. package/dist/cdn/chunks/{TemplateSettings-Dr5OJ0Gi.js.map → TemplateSettings-DVN1t3yF.js.map} +1 -1
  83. package/dist/cdn/chunks/{TestEmailModal-CrjzVw8e.js → TestEmailModal-DZBCmBtT.js} +7 -7
  84. package/dist/cdn/chunks/{TestEmailModal-CrjzVw8e.js.map → TestEmailModal-DZBCmBtT.js.map} +1 -1
  85. package/dist/cdn/chunks/{TestEmailPanel-k9vdulTu.js → TestEmailPanel-1ibwYCUl.js} +2 -2
  86. package/dist/cdn/chunks/{TestEmailPanel-k9vdulTu.js.map → TestEmailPanel-1ibwYCUl.js.map} +1 -1
  87. package/dist/cdn/chunks/{TitleEditor-laILWfKh.js → TitleEditor-BRl2qKbd.js} +13 -13
  88. package/dist/cdn/chunks/{TitleEditor-laILWfKh.js.map → TitleEditor-BRl2qKbd.js.map} +1 -1
  89. package/dist/cdn/chunks/{ToggleSwitch-0dGCYnRY.js → ToggleSwitch-niSL2uog.js} +2 -2
  90. package/dist/cdn/chunks/{ToggleSwitch-0dGCYnRY.js.map → ToggleSwitch-niSL2uog.js.map} +1 -1
  91. package/dist/cdn/chunks/{Toolbar-BsiHW9gY.js → Toolbar-tTl0P-rr.js} +814 -566
  92. package/dist/cdn/chunks/Toolbar-tTl0P-rr.js.map +1 -0
  93. package/dist/cdn/chunks/{TplModal-D8DoPbFa.js → TplModal-C_eIPykE.js} +3 -3
  94. package/dist/cdn/chunks/{TplModal-D8DoPbFa.js.map → TplModal-C_eIPykE.js.map} +1 -1
  95. package/dist/cdn/chunks/{VersionHistoryMenu-B3-hNu9c.js → VersionHistoryMenu-Ww57XAzj.js} +7 -7
  96. package/dist/cdn/chunks/{VersionHistoryMenu-B3-hNu9c.js.map → VersionHistoryMenu-Ww57XAzj.js.map} +1 -1
  97. package/dist/cdn/chunks/{VersionHistoryPanels-zw1AtzGN.js → VersionHistoryPanels-w0EG_qVW.js} +2 -2
  98. package/dist/cdn/chunks/{VersionHistoryPanels-zw1AtzGN.js.map → VersionHistoryPanels-w0EG_qVW.js.map} +1 -1
  99. package/dist/cdn/chunks/{VersionPreviewBanner-PzVQSBJE.js → VersionPreviewBanner-Bbd4C-AN.js} +2 -2
  100. package/dist/cdn/chunks/{VersionPreviewBanner-PzVQSBJE.js.map → VersionPreviewBanner-Bbd4C-AN.js.map} +1 -1
  101. package/dist/cdn/chunks/{WrapperBlock-CpRvfAV0.js → WrapperBlock-Ch4KeMIb.js} +4 -4
  102. package/dist/cdn/chunks/{WrapperBlock-CpRvfAV0.js.map → WrapperBlock-Ch4KeMIb.js.map} +1 -1
  103. package/dist/cdn/chunks/blockTypeIcons-CyPXFh7L.js +21 -0
  104. package/dist/cdn/chunks/{blockTypeIcons-raBjStuv.js.map → blockTypeIcons-CyPXFh7L.js.map} +1 -1
  105. package/dist/cdn/chunks/{ca-Ckgw9yd0.js → ca--zGWn9AU.js} +15 -1
  106. package/dist/cdn/chunks/ca--zGWn9AU.js.map +1 -0
  107. package/dist/cdn/chunks/{cloud-CIT4yPp4.js → cloud-De-eh1u6.js} +2 -2
  108. package/dist/cdn/chunks/{cloud-CIT4yPp4.js.map → cloud-De-eh1u6.js.map} +1 -1
  109. package/dist/cdn/chunks/{createCloudRuntime-COUxbPvd.js → createCloudRuntime-DsaUaWvr.js} +3 -3
  110. package/dist/cdn/chunks/{createCloudRuntime-COUxbPvd.js.map → createCloudRuntime-DsaUaWvr.js.map} +1 -1
  111. package/dist/cdn/chunks/{de-D4dweQw8.js → de-BRNLKcDl.js} +15 -1
  112. package/dist/cdn/chunks/de-BRNLKcDl.js.map +1 -0
  113. package/dist/cdn/chunks/{editor-modal-CKu8HhiB.js → editor-modal-BdI0iA5r.js} +88 -88
  114. package/dist/cdn/chunks/{editor-modal-CKu8HhiB.js.map → editor-modal-BdI0iA5r.js.map} +1 -1
  115. package/dist/cdn/chunks/en-Cs-nw16K.js +2 -0
  116. package/dist/{en-VE7R7NyL.js → cdn/chunks/en-JJjqHMjy.js} +16 -0
  117. package/dist/cdn/chunks/en-JJjqHMjy.js.map +1 -0
  118. package/dist/cdn/chunks/{es-B9OhP8H3.js → es-BIh8BxV9.js} +15 -1
  119. package/dist/cdn/chunks/es-BIh8BxV9.js.map +1 -0
  120. package/dist/cdn/chunks/{extensions-CafoGF65.js → extensions-DK_9-6Bd.js} +4 -4
  121. package/dist/cdn/chunks/{extensions-CafoGF65.js.map → extensions-DK_9-6Bd.js.map} +1 -1
  122. package/dist/{fr-wrXhIi00.js → cdn/chunks/fr-C5VUZogP.js} +16 -0
  123. package/dist/cdn/chunks/fr-C5VUZogP.js.map +1 -0
  124. package/dist/cdn/chunks/{icons-BKkJ-5xF.js → icons-Bad3WZ3G.js} +49 -8
  125. package/dist/cdn/chunks/icons-Bad3WZ3G.js.map +1 -0
  126. package/dist/cdn/chunks/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +15 -1
  127. package/dist/cdn/chunks/ja-x5JYxaCW.js.map +1 -0
  128. package/dist/cdn/chunks/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +15 -1
  129. package/dist/cdn/chunks/{nl-DeFatKJe.js.map → nl-UXxFyXV6.js.map} +1 -1
  130. package/dist/cdn/chunks/{preRenderCustomBlocks-Dpj-_cOj.js → preRenderCustomBlocks-h4ZFBKbD.js} +2 -2
  131. package/dist/cdn/chunks/{preRenderCustomBlocks-Dpj-_cOj.js.map → preRenderCustomBlocks-h4ZFBKbD.js.map} +1 -1
  132. package/dist/cdn/chunks/{pt-BR-Bh1J8ey6.js → pt-BR-DHm3SP6W.js} +15 -1
  133. package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +1 -0
  134. package/dist/cdn/chunks/{socialIcons-BvYNMn1G.js → socialIcons-ZOCwCCyn.js} +3 -3
  135. package/dist/cdn/chunks/{socialIcons-BvYNMn1G.js.map → socialIcons-ZOCwCCyn.js.map} +1 -1
  136. package/dist/cdn/chunks/{src-DveVYoWS.js → src-BR3I3gOW.js} +265 -224
  137. package/dist/cdn/chunks/src-BR3I3gOW.js.map +1 -0
  138. package/dist/cdn/chunks/{src-L6HEdwFn.js → src-BxoLKnkl.js} +178 -165
  139. package/dist/cdn/chunks/src-BxoLKnkl.js.map +1 -0
  140. package/dist/cdn/chunks/{src-Cnc5E71L.js → src-fZSLjhjy.js} +12 -12
  141. package/dist/cdn/chunks/{src-Cnc5E71L.js.map → src-fZSLjhjy.js.map} +1 -1
  142. package/dist/cdn/chunks/{useEditorCore-DZmtKwJh.js → useEditorCore-yd40TFKy.js} +458 -446
  143. package/dist/cdn/chunks/useEditorCore-yd40TFKy.js.map +1 -0
  144. package/dist/cdn/chunks/{useLogicTag-DF7-l66G.js → useLogicTag-J_i_hvZg.js} +2 -2
  145. package/dist/cdn/chunks/{useLogicTag-DF7-l66G.js.map → useLogicTag-J_i_hvZg.js.map} +1 -1
  146. package/dist/cdn/chunks/{useMergeTag-DbIHw4NG.js → useMergeTag-C5MyHYxJ.js} +2 -2
  147. package/dist/cdn/chunks/{useMergeTag-DbIHw4NG.js.map → useMergeTag-C5MyHYxJ.js.map} +1 -1
  148. package/dist/cdn/chunks/{usePopoverPosition-BJRvAb-h.js → usePopoverPosition-DGXjXKjM.js} +25 -12
  149. package/dist/cdn/chunks/usePopoverPosition-DGXjXKjM.js.map +1 -0
  150. package/dist/cdn/editor.css +1 -1
  151. package/dist/cdn/editor.js +64 -64
  152. package/dist/{cloud-DsqqUfzh.js → cloud-AO_X8T9g.js} +1 -1
  153. package/dist/{createCloudRuntime-iLflkYzP.js → createCloudRuntime-D641nhri.js} +2 -2
  154. package/dist/{de-D4dweQw8.js → de-BRNLKcDl.js} +14 -0
  155. package/dist/{dist-DbUZvK04.js → dist-iLUVPysa.js} +263 -222
  156. package/dist/{cdn/chunks/en-VE7R7NyL.js → en-JJjqHMjy.js} +14 -2
  157. package/dist/{es-B9OhP8H3.js → es-BIh8BxV9.js} +14 -0
  158. package/dist/{extensions-DDhjuVKy.js → extensions-DoT2-_pz.js} +11 -11
  159. package/dist/{cdn/chunks/fr-wrXhIi00.js → fr-C5VUZogP.js} +14 -2
  160. package/dist/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +14 -0
  161. package/dist/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +14 -0
  162. package/dist/{preRenderCustomBlocks-BBSqQgke.js → preRenderCustomBlocks-CTstI3Q-.js} +4 -4
  163. package/dist/{pt-BR-Bh1J8ey6.js → pt-BR-DHm3SP6W.js} +14 -0
  164. package/dist/{socialIcons-O7PZCW2P.js → socialIcons-CACVa6m_.js} +46 -46
  165. package/dist/style.css +1 -1
  166. package/dist/templatical-editor.js +108 -108
  167. package/dist/{useEditorCore-dcdHB_7H.js → useEditorCore-LyNoGs0m.js} +894 -882
  168. package/dist/{useLogicTag-D9pvgxQR.js → useLogicTag-BaH8gey0.js} +1 -1
  169. package/dist/{useMergeTag-BaVFPdlq.js → useMergeTag-Cyad1yBE.js} +1 -1
  170. package/dist/{usePopoverPosition-DH7z3Ghm.js → usePopoverPosition-BYmd-lhR.js} +24 -11
  171. package/package.json +7 -7
  172. package/dist/cdn/chunks/LogicTagInsertButton-CHc1yZ9v.js.map +0 -1
  173. package/dist/cdn/chunks/Toolbar-BsiHW9gY.js.map +0 -1
  174. package/dist/cdn/chunks/blockTypeIcons-raBjStuv.js +0 -21
  175. package/dist/cdn/chunks/ca-Ckgw9yd0.js.map +0 -1
  176. package/dist/cdn/chunks/de-D4dweQw8.js.map +0 -1
  177. package/dist/cdn/chunks/en-DqzAdO8F.js +0 -2
  178. package/dist/cdn/chunks/en-VE7R7NyL.js.map +0 -1
  179. package/dist/cdn/chunks/es-B9OhP8H3.js.map +0 -1
  180. package/dist/cdn/chunks/fr-wrXhIi00.js.map +0 -1
  181. package/dist/cdn/chunks/icons-BKkJ-5xF.js.map +0 -1
  182. package/dist/cdn/chunks/ja-fKnVWXHC.js.map +0 -1
  183. package/dist/cdn/chunks/pt-BR-Bh1J8ey6.js.map +0 -1
  184. package/dist/cdn/chunks/src-DveVYoWS.js.map +0 -1
  185. package/dist/cdn/chunks/src-L6HEdwFn.js.map +0 -1
  186. package/dist/cdn/chunks/useEditorCore-DZmtKwJh.js.map +0 -1
  187. package/dist/cdn/chunks/usePopoverPosition-BJRvAb-h.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateScoringPanel-d_Ppowsq.js","names":[],"sources":["../../../src/cloud/utils/scoringStyles.ts","../../../src/cloud/components/TemplateScoringPanel.vue","../../../src/cloud/components/TemplateScoringPanel.vue"],"sourcesContent":["export type ScoringSeverity = \"high\" | \"medium\" | \"low\" | string;\n\nexport function scoreColor(score: number): string {\n if (score >= 80) return \"var(--tpl-success)\";\n if (score >= 60) return \"var(--tpl-warning)\";\n return \"var(--tpl-danger)\";\n}\n\nexport function scoreBgColor(score: number): string {\n if (score >= 80) return \"var(--tpl-success-light)\";\n if (score >= 60) return \"var(--tpl-warning-light)\";\n return \"var(--tpl-danger-light)\";\n}\n\nexport function severityColor(severity: ScoringSeverity): string {\n if (severity === \"high\") return \"var(--tpl-danger)\";\n if (severity === \"medium\") return \"var(--tpl-warning)\";\n return \"var(--tpl-text-muted)\";\n}\n\nexport function severityBgColor(severity: ScoringSeverity): string {\n if (severity === \"high\") return \"var(--tpl-danger-light)\";\n if (severity === \"medium\") return \"var(--tpl-warning-light)\";\n return \"var(--tpl-bg-hover)\";\n}\n","<script setup lang=\"ts\">\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport {\n EDITOR_KEY,\n SCORING_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n requireInject,\n} from \"../../keys\";\nimport {\n scoreColor,\n scoreBgColor,\n severityColor,\n severityBgColor,\n} from \"../utils/scoringStyles\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { ScoringCategory, ScoringFinding } from \"@templatical/types\";\nimport {\n CircleAlert,\n TriangleAlert,\n ChevronDown,\n Eye,\n Info,\n LoaderCircle,\n RefreshCw,\n ShieldCheck,\n ShieldX,\n Sparkles,\n X,\n Zap,\n} from \"@lucide/vue\";\nimport { inject, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"TemplateScoringPanel\");\nconst scoring = requireInject(SCORING_KEY, \"TemplateScoringPanel\");\nconst mergeTags = inject(MERGE_TAGS_KEY, NO_MERGE_TAGS);\n\nconst expandedCategories = ref<Record<string, boolean>>({\n spam: true,\n readability: true,\n accessibility: true,\n bestPractices: true,\n});\n\nfunction toggleCategory(category: string): void {\n expandedCategories.value[category] = !expandedCategories.value[category];\n}\n\nconst categoryOrder: ScoringCategory[] = [\n \"spam\",\n \"readability\",\n \"accessibility\",\n \"bestPractices\",\n];\n\nconst categoryIcons: Record<ScoringCategory, typeof ShieldCheck> = {\n spam: ShieldX,\n readability: Eye,\n accessibility: Sparkles,\n bestPractices: Zap,\n};\n\nfunction triggerScore(): void {\n scoring.score(editor.content.value, mergeTags.value);\n}\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible && !scoring.scoringResult.value && !scoring.isScoring.value) {\n triggerScore();\n }\n },\n);\n\nasync function handleFix(finding: ScoringFinding): Promise<void> {\n if (!finding.blockId) {\n return;\n }\n\n // Get the block content from the editor\n const block = editor.content.value.blocks.find(\n (b) => b.id === finding.blockId,\n );\n if (!block) {\n return;\n }\n\n // Extract HTML content from the block (text blocks have content property)\n const blockContent = (block as unknown as Record<string, unknown>).content as\n string | undefined;\n if (!blockContent) {\n return;\n }\n\n const fixedContent = await scoring.fixFinding(\n blockContent,\n finding,\n mergeTags.value,\n );\n\n if (fixedContent) {\n editor.updateBlock(finding.blockId, { content: fixedContent });\n scoring.removeFinding(finding.category, finding.id);\n }\n}\n\nfunction totalFindings(): number {\n if (!scoring.scoringResult.value) {\n return 0;\n }\n return categoryOrder.reduce((sum, cat) => {\n return (\n sum + (scoring.scoringResult.value?.categories[cat]?.findings.length ?? 0)\n );\n }, 0);\n}\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-scoring-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-scoring-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n role=\"complementary\"\n :aria-label=\"cloudT.scoring.title\"\n class=\"tpl-scoring-panel tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\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\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ShieldCheck :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.scoring.title }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <button\n v-if=\"scoring.scoringResult.value && !scoring.isScoring.value\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.rescore\"\n :title=\"cloudT.scoring.rescore\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"scoring.isScoring.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.scoring.scoring }}\n </p>\n <LoadingTrack class=\"tpl:w-3/4\" />\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"scoring.error.value && !scoring.scoringResult.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.error }}\n </p>\n <button\n class=\"tpl:mt-2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.scoring.rescore }}\n </button>\n </div>\n\n <!-- Results -->\n <div\n v-else-if=\"scoring.scoringResult.value\"\n class=\"tpl:flex tpl:flex-col tpl:gap-4\"\n >\n <!-- Overall score -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:p-5\"\n :style=\"{\n backgroundColor: scoreBgColor(scoring.scoringResult.value.score),\n }\"\n >\n <span\n class=\"tpl:text-4xl tpl:font-bold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(scoring.scoringResult.value.score),\n }\"\n >\n {{ scoring.scoringResult.value.score }}\n </span>\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:uppercase tpl:tracking-wider tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.overallScore }}\n </span>\n <span\n v-if=\"totalFindings() > 0\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ totalFindings() }}\n {{ cloudT.scoring.findings }}\n </span>\n </div>\n\n <!-- Fix error -->\n <div\n v-if=\"scoring.fixError.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.scoring.fixError }}</span>\n </div>\n\n <!-- Category cards -->\n <div\n v-for=\"category in categoryOrder\"\n :key=\"category\"\n class=\"tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)]\"\n >\n <!-- Category header -->\n <button\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-2.5 tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:bg-[var(--tpl-bg)]\"\n @click=\"toggleCategory(category)\"\n >\n <component\n :is=\"categoryIcons[category]\"\n :size=\"14\"\n :stroke-width=\"2\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n />\n <span\n class=\"tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.scoring.categories[category] }}\n </span>\n <span\n class=\"tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-semibold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n backgroundColor: scoreBgColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n >\n {{ scoring.scoringResult.value.categories[category].score }}\n </span>\n <span\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length > 0\n \"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n scoring.scoringResult.value.categories[category].findings\n .length\n }}\n </span>\n <ChevronDown\n :size=\"12\"\n :stroke-width=\"2\"\n class=\"tpl:transition-transform tpl:duration-200 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"\n expandedCategories[category]\n ? 'tpl:rotate-0'\n : 'tpl:-rotate-90'\n \"\n />\n </button>\n\n <!-- Findings list -->\n <div\n v-if=\"expandedCategories[category]\"\n class=\"tpl:border-t tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length === 0\n \"\n class=\"tpl:px-3 tpl:py-3 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.scoring.noFindings }}\n </div>\n <div\n v-for=\"finding in scoring.scoringResult.value.categories[\n category\n ].findings\"\n :key=\"finding.id\"\n class=\"tpl:border-t tpl:px-3 tpl:py-2.5 tpl:first:border-t-0 tpl:border-[var(--tpl-border-light)]\"\n >\n <div class=\"tpl:flex tpl:items-start tpl:gap-2\">\n <!-- Severity icon -->\n <component\n :is=\"\n finding.severity === 'high'\n ? CircleAlert\n : finding.severity === 'medium'\n ? TriangleAlert\n : Info\n \"\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n :style=\"{\n color: severityColor(finding.severity),\n }\"\n />\n <div class=\"tpl:flex-1 tpl:min-w-0\">\n <!-- Severity badge + message -->\n <div class=\"tpl:flex tpl:items-start tpl:gap-1.5\">\n <span\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:rounded tpl:px-1 tpl:py-px tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n :style=\"{\n color: severityColor(finding.severity),\n backgroundColor: severityBgColor(finding.severity),\n }\"\n >\n {{ cloudT.scoring.severity[finding.severity] }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ finding.message }}\n </span>\n </div>\n <!-- Suggestion -->\n <p\n v-if=\"finding.suggestion\"\n class=\"tpl:mt-1 tpl:text-[10px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ finding.suggestion }}\n </p>\n <!-- Fix button -->\n <div\n v-if=\"finding.blockId\"\n class=\"tpl:mt-2 tpl:flex tpl:justify-center\"\n >\n <button\n class=\"tpl-scoring-fix-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)]\"\n style=\"background-color: transparent\"\n :disabled=\"scoring.fixingFindingId.value !== null\"\n @click=\"handleFix(finding)\"\n >\n <LoaderCircle\n v-if=\"scoring.fixingFindingId.value === finding.id\"\n class=\"tpl-spinner\"\n :size=\"11\"\n :stroke-width=\"2\"\n />\n <Sparkles v-else :size=\"11\" :stroke-width=\"2\" />\n {{\n scoring.fixingFindingId.value === finding.id\n ? cloudT.scoring.fixing\n : cloudT.scoring.fix\n }}\n </button>\n <p\n v-if=\"scoring.fixError.value\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ scoring.fixError.value }}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state (no result, no loading, no error) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <ShieldCheck\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.emptyState }}\n </p>\n </div>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:px-4 tpl:pb-2 tpl:pt-2 tpl:text-center tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-scoring-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-fix-btn:not(:disabled):hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport {\n EDITOR_KEY,\n SCORING_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n requireInject,\n} from \"../../keys\";\nimport {\n scoreColor,\n scoreBgColor,\n severityColor,\n severityBgColor,\n} from \"../utils/scoringStyles\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { ScoringCategory, ScoringFinding } from \"@templatical/types\";\nimport {\n CircleAlert,\n TriangleAlert,\n ChevronDown,\n Eye,\n Info,\n LoaderCircle,\n RefreshCw,\n ShieldCheck,\n ShieldX,\n Sparkles,\n X,\n Zap,\n} from \"@lucide/vue\";\nimport { inject, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"TemplateScoringPanel\");\nconst scoring = requireInject(SCORING_KEY, \"TemplateScoringPanel\");\nconst mergeTags = inject(MERGE_TAGS_KEY, NO_MERGE_TAGS);\n\nconst expandedCategories = ref<Record<string, boolean>>({\n spam: true,\n readability: true,\n accessibility: true,\n bestPractices: true,\n});\n\nfunction toggleCategory(category: string): void {\n expandedCategories.value[category] = !expandedCategories.value[category];\n}\n\nconst categoryOrder: ScoringCategory[] = [\n \"spam\",\n \"readability\",\n \"accessibility\",\n \"bestPractices\",\n];\n\nconst categoryIcons: Record<ScoringCategory, typeof ShieldCheck> = {\n spam: ShieldX,\n readability: Eye,\n accessibility: Sparkles,\n bestPractices: Zap,\n};\n\nfunction triggerScore(): void {\n scoring.score(editor.content.value, mergeTags.value);\n}\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible && !scoring.scoringResult.value && !scoring.isScoring.value) {\n triggerScore();\n }\n },\n);\n\nasync function handleFix(finding: ScoringFinding): Promise<void> {\n if (!finding.blockId) {\n return;\n }\n\n // Get the block content from the editor\n const block = editor.content.value.blocks.find(\n (b) => b.id === finding.blockId,\n );\n if (!block) {\n return;\n }\n\n // Extract HTML content from the block (text blocks have content property)\n const blockContent = (block as unknown as Record<string, unknown>).content as\n string | undefined;\n if (!blockContent) {\n return;\n }\n\n const fixedContent = await scoring.fixFinding(\n blockContent,\n finding,\n mergeTags.value,\n );\n\n if (fixedContent) {\n editor.updateBlock(finding.blockId, { content: fixedContent });\n scoring.removeFinding(finding.category, finding.id);\n }\n}\n\nfunction totalFindings(): number {\n if (!scoring.scoringResult.value) {\n return 0;\n }\n return categoryOrder.reduce((sum, cat) => {\n return (\n sum + (scoring.scoringResult.value?.categories[cat]?.findings.length ?? 0)\n );\n }, 0);\n}\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-scoring-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-scoring-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n role=\"complementary\"\n :aria-label=\"cloudT.scoring.title\"\n class=\"tpl-scoring-panel tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\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\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ShieldCheck :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.scoring.title }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <button\n v-if=\"scoring.scoringResult.value && !scoring.isScoring.value\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.rescore\"\n :title=\"cloudT.scoring.rescore\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"scoring.isScoring.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.scoring.scoring }}\n </p>\n <LoadingTrack class=\"tpl:w-3/4\" />\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"scoring.error.value && !scoring.scoringResult.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.error }}\n </p>\n <button\n class=\"tpl:mt-2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.scoring.rescore }}\n </button>\n </div>\n\n <!-- Results -->\n <div\n v-else-if=\"scoring.scoringResult.value\"\n class=\"tpl:flex tpl:flex-col tpl:gap-4\"\n >\n <!-- Overall score -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:p-5\"\n :style=\"{\n backgroundColor: scoreBgColor(scoring.scoringResult.value.score),\n }\"\n >\n <span\n class=\"tpl:text-4xl tpl:font-bold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(scoring.scoringResult.value.score),\n }\"\n >\n {{ scoring.scoringResult.value.score }}\n </span>\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:uppercase tpl:tracking-wider tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.overallScore }}\n </span>\n <span\n v-if=\"totalFindings() > 0\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ totalFindings() }}\n {{ cloudT.scoring.findings }}\n </span>\n </div>\n\n <!-- Fix error -->\n <div\n v-if=\"scoring.fixError.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.scoring.fixError }}</span>\n </div>\n\n <!-- Category cards -->\n <div\n v-for=\"category in categoryOrder\"\n :key=\"category\"\n class=\"tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)]\"\n >\n <!-- Category header -->\n <button\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-2.5 tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:bg-[var(--tpl-bg)]\"\n @click=\"toggleCategory(category)\"\n >\n <component\n :is=\"categoryIcons[category]\"\n :size=\"14\"\n :stroke-width=\"2\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n />\n <span\n class=\"tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.scoring.categories[category] }}\n </span>\n <span\n class=\"tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-semibold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n backgroundColor: scoreBgColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n >\n {{ scoring.scoringResult.value.categories[category].score }}\n </span>\n <span\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length > 0\n \"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n scoring.scoringResult.value.categories[category].findings\n .length\n }}\n </span>\n <ChevronDown\n :size=\"12\"\n :stroke-width=\"2\"\n class=\"tpl:transition-transform tpl:duration-200 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"\n expandedCategories[category]\n ? 'tpl:rotate-0'\n : 'tpl:-rotate-90'\n \"\n />\n </button>\n\n <!-- Findings list -->\n <div\n v-if=\"expandedCategories[category]\"\n class=\"tpl:border-t tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length === 0\n \"\n class=\"tpl:px-3 tpl:py-3 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.scoring.noFindings }}\n </div>\n <div\n v-for=\"finding in scoring.scoringResult.value.categories[\n category\n ].findings\"\n :key=\"finding.id\"\n class=\"tpl:border-t tpl:px-3 tpl:py-2.5 tpl:first:border-t-0 tpl:border-[var(--tpl-border-light)]\"\n >\n <div class=\"tpl:flex tpl:items-start tpl:gap-2\">\n <!-- Severity icon -->\n <component\n :is=\"\n finding.severity === 'high'\n ? CircleAlert\n : finding.severity === 'medium'\n ? TriangleAlert\n : Info\n \"\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n :style=\"{\n color: severityColor(finding.severity),\n }\"\n />\n <div class=\"tpl:flex-1 tpl:min-w-0\">\n <!-- Severity badge + message -->\n <div class=\"tpl:flex tpl:items-start tpl:gap-1.5\">\n <span\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:rounded tpl:px-1 tpl:py-px tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n :style=\"{\n color: severityColor(finding.severity),\n backgroundColor: severityBgColor(finding.severity),\n }\"\n >\n {{ cloudT.scoring.severity[finding.severity] }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ finding.message }}\n </span>\n </div>\n <!-- Suggestion -->\n <p\n v-if=\"finding.suggestion\"\n class=\"tpl:mt-1 tpl:text-[10px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ finding.suggestion }}\n </p>\n <!-- Fix button -->\n <div\n v-if=\"finding.blockId\"\n class=\"tpl:mt-2 tpl:flex tpl:justify-center\"\n >\n <button\n class=\"tpl-scoring-fix-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)]\"\n style=\"background-color: transparent\"\n :disabled=\"scoring.fixingFindingId.value !== null\"\n @click=\"handleFix(finding)\"\n >\n <LoaderCircle\n v-if=\"scoring.fixingFindingId.value === finding.id\"\n class=\"tpl-spinner\"\n :size=\"11\"\n :stroke-width=\"2\"\n />\n <Sparkles v-else :size=\"11\" :stroke-width=\"2\" />\n {{\n scoring.fixingFindingId.value === finding.id\n ? cloudT.scoring.fixing\n : cloudT.scoring.fix\n }}\n </button>\n <p\n v-if=\"scoring.fixError.value\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ scoring.fixError.value }}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state (no result, no loading, no error) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <ShieldCheck\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.emptyState }}\n </p>\n </div>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:px-4 tpl:pb-2 tpl:pt-2 tpl:text-center tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-scoring-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-fix-btn:not(:disabled):hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n"],"mappings":";;;;;;;AAEA,SAAgB,EAAW,GAAuB;CAGhD,OAFI,KAAS,KAAW,uBACpB,KAAS,KAAW,uBACjB;AACT;AAEA,SAAgB,EAAa,GAAuB;CAGlD,OAFI,KAAS,KAAW,6BACpB,KAAS,KAAW,6BACjB;AACT;AAEA,SAAgB,EAAc,GAAmC;CAG/D,OAFI,MAAa,SAAe,sBAC5B,MAAa,WAAiB,uBAC3B;AACT;AAEA,SAAgB,EAAgB,GAAmC;CAGjE,OAFI,MAAa,SAAe,4BAC5B,MAAa,WAAiB,6BAC3B;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECSA,IAAM,IAAQ,GAIR,KAAO,GAIP,EAAE,GAAG,MAAW,EAAmB,GACnC,IAAS,EAAc,GAAY,sBAAsB,GACzD,IAAU,EAAc,IAAa,sBAAsB,GAC3D,IAAY,EAAO,IAAgB,CAAa,GAEhD,IAAqB,GAA6B;GACtD,MAAM;GACN,aAAa;GACb,eAAe;GACf,eAAe;EACjB,CAAC;EAED,SAAS,GAAe,GAAwB;GAC9C,EAAmB,MAAM,KAAY,CAAC,EAAmB,MAAM;EACjE;EAEA,IAAM,IAAmC;GACvC;GACA;GACA;GACA;EACF,GAEM,KAA6D;GACjE,MAAM;GACN,aAAa;GACb,eAAe;GACf,eAAe;EACjB;EAEA,SAAS,IAAqB;GAC5B,EAAQ,MAAM,EAAO,QAAQ,OAAO,EAAU,KAAK;EACrD;EAEA,QACQ,EAAM,UACX,MAAc;GACb,AAAI,KAAa,CAAC,EAAQ,cAAc,SAAS,CAAC,EAAQ,UAAU,SAClE,EAAa;EAEjB,CACF;EAEA,eAAe,GAAU,GAAwC;GAC/D,IAAI,CAAC,EAAQ,SACX;GAIF,IAAM,IAAQ,EAAO,QAAQ,MAAM,OAAO,MACvC,MAAM,EAAE,OAAO,EAAQ,OAC1B;GACA,IAAI,CAAC,GACH;GAIF,IAAM,IAAgB,EAA6C;GAEnE,IAAI,CAAC,GACH;GAGF,IAAM,IAAe,MAAM,EAAQ,WACjC,GACA,GACA,EAAU,KACZ;GAEA,AAAI,MACF,EAAO,YAAY,EAAQ,SAAS,EAAE,SAAS,EAAa,CAAC,GAC7D,EAAQ,cAAc,EAAQ,UAAU,EAAQ,EAAE;EAEtD;EAEA,SAAS,IAAwB;GAI/B,OAHK,EAAQ,cAAc,QAGpB,EAAc,QAAQ,GAAK,MAE9B,KAAO,EAAQ,cAAc,OAAO,WAAW,EAAI,EAAE,SAAS,UAAU,IAEzE,CAAC,IANK;EAOX;oBAIE,EAAA,GAAA,EA+Ta,IAAA;GA9TX,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;GACf,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;;GAEf,SAAA,QAsTM,CArTE,EAAA,WADR,EAAA,GAAA,EAsTM,OAAA;;IApTJ,MAAK;IACJ,cAAY,EAAA,CAAA,CAAM,CAAC,QAAQ;IAC5B,OAAM;GAGN,GAAA,CAAA,EA6BM,OA7BN,GA6BM,CA1BJ,EAKM,OALN,GAKM,CAFJ,EAA4C,EAAA,CAAA,GAAA;IAA9B,MAAM;IAAK,gBAAc;GACvC,CAAA,GAAA,EAAuC,QAAA,MAAA,EAA9B,EAAA,CAAA,CAAM,CAAC,QAAQ,KAAK,GAAA,CAAA,CAAA,CAAA,GAE/B,EAmBM,OAnBN,GAmBM,CAjBI,EAAA,CAAA,CAAO,CAAC,cAAc,SAAK,CAAK,EAAA,CAAA,CAAO,CAAC,UAAU,SAD1D,EAAA,GAAA,EASS,UAAA;;IAPP,MAAK;IACL,OAAM;IACL,cAAY,EAAA,CAAA,CAAM,CAAC,QAAQ;IAC3B,OAAO,EAAA,CAAA,CAAM,CAAC,QAAQ;IACtB,SAAK,AAAA,EAAA,QAAA,MAAE,EAAY;GAEpB,GAAA,CAAA,EAA0C,EAAA,CAAA,GAAA;IAA9B,MAAM;IAAK,gBAAc;GAEvC,CAAA,CAAA,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAAA,EAOS,UAAA;IANP,MAAK;IACL,OAAM;IACL,cAAY,EAAA,CAAA,CAAM,CAAC,QAAQ;IAC3B,SAAK,AAAA,EAAA,QAAA,MAAE,GAAI,OAAA;GAEZ,GAAA,CAAA,EAAkC,EAAA,EAAA,GAAA;IAA9B,MAAM;IAAK,gBAAc;GAMnC,CAAA,CAAA,GAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,EA8QM,OA9QN,GA8QM,CA3QI,EAAA,CAAA,CAAO,CAAC,UAAU,SAD1B,EAAA,GAAA,EAQM,OARN,GAQM,CAJJ,EAEI,KAFJ,GAEI,EADC,EAAA,CAAA,CAAM,CAAC,QAAQ,OAAO,GAAA,CAAA,GAE3B,EAAkC,GAAA,EAApB,OAAM,YAAW,CAAA,CAAA,CAAA,KAKpB,EAAA,CAAA,CAAO,CAAC,MAAM,SAAK,CAAK,EAAA,CAAA,CAAO,CAAC,cAAc,SAD3D,EAAA,GAAA,EAqBM,OArBN,GAqBM;IAjBJ,EAIE,EAAA,CAAA,GAAA;KAHC,MAAM;KACN,gBAAc;KACf,OAAM;;IAER,EAII,KAJJ,GAII,EADC,EAAA,CAAA,CAAM,CAAC,QAAQ,KAAK,GAAA,CAAA;IAEzB,EAMS,UAAA;KALP,OAAM;KACL,SAAK,AAAA,EAAA,QAAA,MAAE,EAAY;IAEpB,GAAA,CAAA,EAA0C,EAAA,CAAA,GAAA;KAA9B,MAAM;KAAK,gBAAc;IAAK,CAAA,GAAA,EAAA,MAC1C,EAAG,EAAA,CAAA,CAAM,CAAC,QAAQ,OAAO,GAAA,CAAA,CAAA,CAAA;GAMhB,CAAA,KAAA,EAAA,CAAA,CAAO,CAAC,cAAc,SADnC,EAAA,GAAA,EAgNM,OAhNN,IAgNM;IA3MJ,EA0BM,OAAA;KAzBJ,OAAM;KACL,OAAK,EAAA,EAAmC,iBAAA,EAAA,CAAA,CAAY,CAAC,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,KAAK,EAAA,CAAA;;KAIvF,EAOO,QAAA;MANL,OAAM;MACL,OAAK,EAAA,EAA2B,OAAA,EAAA,CAAA,CAAU,CAAC,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,KAAK,EAAA,CAAA;KAI1E,GAAA,EAAA,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,KAAK,GAAA,CAAA;KAEtC,EAIO,QAJP,IAIO,EADF,EAAA,CAAA,CAAM,CAAC,QAAQ,YAAY,GAAA,CAAA;KAGxB,EAAa,IAAA,KADrB,EAAA,GAAA,EAMO,QANP,IAMO,EAFF,EAAa,CAAA,IAAK,MACrB,EAAG,EAAA,CAAA,CAAM,CAAC,QAAQ,QAAQ,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA;;IAMtB,EAAA,CAAA,CAAO,CAAC,SAAS,SADzB,EAAA,GAAA,EAUM,OAVN,IAUM,CANJ,EAIE,EAAA,CAAA,GAAA;KAHC,MAAM;KACN,gBAAc;KACf,OAAM;IAER,CAAA,GAAA,EAA0C,QAAA,MAAA,EAAjC,EAAA,CAAA,CAAM,CAAC,QAAQ,QAAQ,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAIlC,EAAA,GAAA,EAgKM,GAAA,MAAA,EA/Je,IAAZ,MADT,EAgKM,OAAA;KA9JH,KAAK;KACN,OAAM;IAGN,GAAA,CAAA,EAsDS,UAAA;KArDP,OAAM;KACL,UAAK,MAAE,GAAe,CAAQ;;MAE/B,EAAA,GAAA,EASE,EARK,GAAc,EAAQ,GAAA;MAC1B,MAAM;MACN,gBAAc;MACd,OAAK,EAAA,EAA6B,OAAA,EAAA,CAAA,CAAU,CAAsB,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,KAAA,EAAA,CAAA;;KAMtH,EAIO,QAJP,IAIO,EADF,EAAA,CAAA,CAAM,CAAC,QAAQ,WAAW,EAAQ,GAAA,CAAA;KAEvC,EAYO,QAAA;MAXL,OAAM;MACL,OAAK,EAAA;OAA6B,OAAA,EAAA,CAAA,CAAU,CAAsB,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,KAAA;OAA+D,iBAAA,EAAA,CAAA,CAAY,CAAsB,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,KAAA;;KASnQ,GAAA,EAAA,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,KAAK,GAAA,CAAA;KAGhC,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,SAA8B,SAAM,KADhH,EAAA,GAAA,EAWO,QAXP,IAWO,EAHH,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,SAA8B,MAAM,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAIzF,EASE,EAAA,CAAA,GAAA;MARC,MAAM;MACN,gBAAc;MACf,OAAK,EAAA,CAAC,4EACqB,EAAA,MAAmB,KAAA,iBAAA,gBAAA,CAAA;;IAU1C,GAAA,GAAA,EAAA,GAAA,EAAA,MAAmB,MAD3B,EAAA,GAAA,EAgGM,OAhGN,IAgGM,CA3FuB,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAAW,EAAQ,CAAE,SAA8B,WAAM,KADhH,EAAA,GAAA,EAQM,OARN,IAQM,EADD,EAAA,CAAA,CAAM,CAAC,QAAQ,UAAU,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,IAE9B,EAAA,EAAA,GAAA,EAkFM,GAAA,MAAA,EAjFc,EAAA,CAAA,CAAO,CAAC,cAAc,MAAM,WAA8B,EAA2B,CAAA,WAAhG,OADT,EAAA,GAAA,EAkFM,OAAA;KA9EH,KAAK,EAAQ;KACd,OAAM;IAEN,GAAA,CAAA,EA0EM,OA1EN,IA0EM,EAxEJ,EAAA,GAAA,EAcE,EAbkF,EAAtD,EAAQ,aAAQ,SAAsC,IAAsC,EAAQ,aAAQ,WAA0C,KAA0C,EAAA,CAAA,GAAA;KAO3N,MAAM;KACN,gBAAc;KACf,OAAM;KACL,OAAK,EAAA,EAAiC,OAAA,EAAA,CAAA,CAAa,CAAC,EAAQ,QAAQ,EAAA,CAAA;IAIvE,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,IAAA,EAwDM,OAxDN,IAwDM;KAtDJ,EAeM,OAfN,IAeM,CAdJ,EAQO,QAAA;MAPL,OAAM;MACL,OAAK,EAAA;OAAqC,OAAA,EAAA,CAAA,CAAa,CAAC,EAAQ,QAAQ;OAA8C,iBAAA,EAAA,CAAA,CAAe,CAAC,EAAQ,QAAQ;;KAKpJ,GAAA,EAAA,EAAA,CAAA,CAAM,CAAC,QAAQ,SAAS,EAAQ,SAAQ,GAAA,CAAA,GAE7C,EAIO,QAJP,IAIO,EADF,EAAQ,OAAO,GAAA,CAAA,CAAA,CAAA;KAKd,EAAQ,cADhB,EAAA,GAAA,EAKI,KALJ,IAKI,EADC,EAAQ,UAAU,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA;KAIf,EAAQ,WADhB,EAAA,GAAA,EA6BM,OA7BN,IA6BM,CAzBJ,EAkBS,UAAA;MAjBP,OAAM;MACN,OAAA,EAAA,oBAAA,cAAA;MACC,UAAU,EAAA,CAAA,CAAO,CAAC,gBAAgB,UAAK;MACvC,UAAK,MAAE,GAAU,CAAO;KAGjB,GAAA,CAAA,EAAA,CAAA,CAAO,CAAC,gBAAgB,UAAU,EAAQ,MADlD,EAAA,GAAA,EAKE,EAAA,EAAA,GAAA;;MAHA,OAAM;MACL,MAAM;MACN,gBAAc;KAEjB,CAAA,MAAA,EAAA,GAAA,EAAgD,EAAA,CAAA,GAAA;;MAA9B,MAAM;MAAK,gBAAc;KAAK,CAAA,IAAA,EAAA,MAChD,EACE,EAAA,CAAA,CAAO,CAAC,gBAAgB,UAAU,EAAQ,KAAiC,EAAA,CAAA,CAAM,CAAC,QAAQ,SAAqC,EAAA,CAAA,CAAM,CAAC,QAAQ,GAAG,GAAA,CAAA,CAAA,GAAA,GAAA,EAAA,GAM7I,EAAA,CAAA,CAAO,CAAC,SAAS,SADzB,EAAA,GAAA,EAKI,KALJ,IAKI,EADC,EAAA,CAAA,CAAO,CAAC,SAAS,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;;GAWzC,CAAA,MAAA,EAAA,GAAA,EAcM,OAdN,IAcM,CAVJ,EAIE,EAAA,CAAA,GAAA;IAHC,MAAM;IACN,gBAAc;IACf,OAAM;GAER,CAAA,GAAA,EAII,KAJJ,IAII,EADC,EAAA,CAAA,CAAM,CAAC,QAAQ,UAAU,GAAA,CAAA,CAAA,CAAA,IAKhC,EAII,KAJJ,IAII,EADC,EAAA,CAAA,CAAM,CAAC,OAAO,UAAU,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"TemplateScoringPanel-BDB4RErx.js","names":[],"sources":["../../../src/cloud/utils/scoringStyles.ts","../../../src/cloud/components/TemplateScoringPanel.vue","../../../src/cloud/components/TemplateScoringPanel.vue"],"sourcesContent":["export type ScoringSeverity = \"high\" | \"medium\" | \"low\" | string;\n\nexport function scoreColor(score: number): string {\n if (score >= 80) return \"var(--tpl-success)\";\n if (score >= 60) return \"var(--tpl-warning)\";\n return \"var(--tpl-danger)\";\n}\n\nexport function scoreBgColor(score: number): string {\n if (score >= 80) return \"var(--tpl-success-light)\";\n if (score >= 60) return \"var(--tpl-warning-light)\";\n return \"var(--tpl-danger-light)\";\n}\n\nexport function severityColor(severity: ScoringSeverity): string {\n if (severity === \"high\") return \"var(--tpl-danger)\";\n if (severity === \"medium\") return \"var(--tpl-warning)\";\n return \"var(--tpl-text-muted)\";\n}\n\nexport function severityBgColor(severity: ScoringSeverity): string {\n if (severity === \"high\") return \"var(--tpl-danger-light)\";\n if (severity === \"medium\") return \"var(--tpl-warning-light)\";\n return \"var(--tpl-bg-hover)\";\n}\n","<script setup lang=\"ts\">\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport {\n EDITOR_KEY,\n SCORING_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n requireInject,\n} from \"../../keys\";\nimport {\n scoreColor,\n scoreBgColor,\n severityColor,\n severityBgColor,\n} from \"../utils/scoringStyles\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { ScoringCategory, ScoringFinding } from \"@templatical/types\";\nimport {\n CircleAlert,\n TriangleAlert,\n ChevronDown,\n Eye,\n Info,\n LoaderCircle,\n RefreshCw,\n ShieldCheck,\n ShieldX,\n Sparkles,\n X,\n Zap,\n} from \"@lucide/vue\";\nimport { inject, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"TemplateScoringPanel\");\nconst scoring = requireInject(SCORING_KEY, \"TemplateScoringPanel\");\nconst mergeTags = inject(MERGE_TAGS_KEY, NO_MERGE_TAGS);\n\nconst expandedCategories = ref<Record<string, boolean>>({\n spam: true,\n readability: true,\n accessibility: true,\n bestPractices: true,\n});\n\nfunction toggleCategory(category: string): void {\n expandedCategories.value[category] = !expandedCategories.value[category];\n}\n\nconst categoryOrder: ScoringCategory[] = [\n \"spam\",\n \"readability\",\n \"accessibility\",\n \"bestPractices\",\n];\n\nconst categoryIcons: Record<ScoringCategory, typeof ShieldCheck> = {\n spam: ShieldX,\n readability: Eye,\n accessibility: Sparkles,\n bestPractices: Zap,\n};\n\nfunction triggerScore(): void {\n scoring.score(editor.content.value, mergeTags.value);\n}\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible && !scoring.scoringResult.value && !scoring.isScoring.value) {\n triggerScore();\n }\n },\n);\n\nasync function handleFix(finding: ScoringFinding): Promise<void> {\n if (!finding.blockId) {\n return;\n }\n\n // Get the block content from the editor\n const block = editor.content.value.blocks.find(\n (b) => b.id === finding.blockId,\n );\n if (!block) {\n return;\n }\n\n // Extract HTML content from the block (text blocks have content property)\n const blockContent = (block as unknown as Record<string, unknown>).content as\n string | undefined;\n if (!blockContent) {\n return;\n }\n\n const fixedContent = await scoring.fixFinding(\n blockContent,\n finding,\n mergeTags.value,\n );\n\n if (fixedContent) {\n editor.updateBlock(finding.blockId, { content: fixedContent });\n scoring.removeFinding(finding.category, finding.id);\n }\n}\n\nfunction totalFindings(): number {\n if (!scoring.scoringResult.value) {\n return 0;\n }\n return categoryOrder.reduce((sum, cat) => {\n return (\n sum + (scoring.scoringResult.value?.categories[cat]?.findings.length ?? 0)\n );\n }, 0);\n}\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-scoring-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-scoring-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n role=\"complementary\"\n :aria-label=\"cloudT.scoring.title\"\n class=\"tpl-scoring-panel tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\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\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ShieldCheck :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.scoring.title }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <button\n v-if=\"scoring.scoringResult.value && !scoring.isScoring.value\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.rescore\"\n :title=\"cloudT.scoring.rescore\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"scoring.isScoring.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.scoring.scoring }}\n </p>\n <LoadingTrack class=\"tpl:w-3/4\" />\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"scoring.error.value && !scoring.scoringResult.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.error }}\n </p>\n <button\n class=\"tpl:mt-2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.scoring.rescore }}\n </button>\n </div>\n\n <!-- Results -->\n <div\n v-else-if=\"scoring.scoringResult.value\"\n class=\"tpl:flex tpl:flex-col tpl:gap-4\"\n >\n <!-- Overall score -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:p-5\"\n :style=\"{\n backgroundColor: scoreBgColor(scoring.scoringResult.value.score),\n }\"\n >\n <span\n class=\"tpl:text-4xl tpl:font-bold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(scoring.scoringResult.value.score),\n }\"\n >\n {{ scoring.scoringResult.value.score }}\n </span>\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:uppercase tpl:tracking-wider tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.overallScore }}\n </span>\n <span\n v-if=\"totalFindings() > 0\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ totalFindings() }}\n {{ cloudT.scoring.findings }}\n </span>\n </div>\n\n <!-- Fix error -->\n <div\n v-if=\"scoring.fixError.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.scoring.fixError }}</span>\n </div>\n\n <!-- Category cards -->\n <div\n v-for=\"category in categoryOrder\"\n :key=\"category\"\n class=\"tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)]\"\n >\n <!-- Category header -->\n <button\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-2.5 tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:bg-[var(--tpl-bg)]\"\n @click=\"toggleCategory(category)\"\n >\n <component\n :is=\"categoryIcons[category]\"\n :size=\"14\"\n :stroke-width=\"2\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n />\n <span\n class=\"tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.scoring.categories[category] }}\n </span>\n <span\n class=\"tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-semibold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n backgroundColor: scoreBgColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n >\n {{ scoring.scoringResult.value.categories[category].score }}\n </span>\n <span\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length > 0\n \"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n scoring.scoringResult.value.categories[category].findings\n .length\n }}\n </span>\n <ChevronDown\n :size=\"12\"\n :stroke-width=\"2\"\n class=\"tpl:transition-transform tpl:duration-200 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"\n expandedCategories[category]\n ? 'tpl:rotate-0'\n : 'tpl:-rotate-90'\n \"\n />\n </button>\n\n <!-- Findings list -->\n <div\n v-if=\"expandedCategories[category]\"\n class=\"tpl:border-t tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length === 0\n \"\n class=\"tpl:px-3 tpl:py-3 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.scoring.noFindings }}\n </div>\n <div\n v-for=\"finding in scoring.scoringResult.value.categories[\n category\n ].findings\"\n :key=\"finding.id\"\n class=\"tpl:border-t tpl:px-3 tpl:py-2.5 tpl:first:border-t-0 tpl:border-[var(--tpl-border-light)]\"\n >\n <div class=\"tpl:flex tpl:items-start tpl:gap-2\">\n <!-- Severity icon -->\n <component\n :is=\"\n finding.severity === 'high'\n ? CircleAlert\n : finding.severity === 'medium'\n ? TriangleAlert\n : Info\n \"\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n :style=\"{\n color: severityColor(finding.severity),\n }\"\n />\n <div class=\"tpl:flex-1 tpl:min-w-0\">\n <!-- Severity badge + message -->\n <div class=\"tpl:flex tpl:items-start tpl:gap-1.5\">\n <span\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:rounded tpl:px-1 tpl:py-px tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n :style=\"{\n color: severityColor(finding.severity),\n backgroundColor: severityBgColor(finding.severity),\n }\"\n >\n {{ cloudT.scoring.severity[finding.severity] }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ finding.message }}\n </span>\n </div>\n <!-- Suggestion -->\n <p\n v-if=\"finding.suggestion\"\n class=\"tpl:mt-1 tpl:text-[10px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ finding.suggestion }}\n </p>\n <!-- Fix button -->\n <div\n v-if=\"finding.blockId\"\n class=\"tpl:mt-2 tpl:flex tpl:justify-center\"\n >\n <button\n class=\"tpl-scoring-fix-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)]\"\n style=\"background-color: transparent\"\n :disabled=\"scoring.fixingFindingId.value !== null\"\n @click=\"handleFix(finding)\"\n >\n <LoaderCircle\n v-if=\"scoring.fixingFindingId.value === finding.id\"\n class=\"tpl-spinner\"\n :size=\"11\"\n :stroke-width=\"2\"\n />\n <Sparkles v-else :size=\"11\" :stroke-width=\"2\" />\n {{\n scoring.fixingFindingId.value === finding.id\n ? cloudT.scoring.fixing\n : cloudT.scoring.fix\n }}\n </button>\n <p\n v-if=\"scoring.fixError.value\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ scoring.fixError.value }}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state (no result, no loading, no error) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <ShieldCheck\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.emptyState }}\n </p>\n </div>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:px-4 tpl:pb-2 tpl:pt-2 tpl:text-center tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-scoring-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-fix-btn:not(:disabled):hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport {\n EDITOR_KEY,\n SCORING_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n requireInject,\n} from \"../../keys\";\nimport {\n scoreColor,\n scoreBgColor,\n severityColor,\n severityBgColor,\n} from \"../utils/scoringStyles\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { ScoringCategory, ScoringFinding } from \"@templatical/types\";\nimport {\n CircleAlert,\n TriangleAlert,\n ChevronDown,\n Eye,\n Info,\n LoaderCircle,\n RefreshCw,\n ShieldCheck,\n ShieldX,\n Sparkles,\n X,\n Zap,\n} from \"@lucide/vue\";\nimport { inject, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"TemplateScoringPanel\");\nconst scoring = requireInject(SCORING_KEY, \"TemplateScoringPanel\");\nconst mergeTags = inject(MERGE_TAGS_KEY, NO_MERGE_TAGS);\n\nconst expandedCategories = ref<Record<string, boolean>>({\n spam: true,\n readability: true,\n accessibility: true,\n bestPractices: true,\n});\n\nfunction toggleCategory(category: string): void {\n expandedCategories.value[category] = !expandedCategories.value[category];\n}\n\nconst categoryOrder: ScoringCategory[] = [\n \"spam\",\n \"readability\",\n \"accessibility\",\n \"bestPractices\",\n];\n\nconst categoryIcons: Record<ScoringCategory, typeof ShieldCheck> = {\n spam: ShieldX,\n readability: Eye,\n accessibility: Sparkles,\n bestPractices: Zap,\n};\n\nfunction triggerScore(): void {\n scoring.score(editor.content.value, mergeTags.value);\n}\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible && !scoring.scoringResult.value && !scoring.isScoring.value) {\n triggerScore();\n }\n },\n);\n\nasync function handleFix(finding: ScoringFinding): Promise<void> {\n if (!finding.blockId) {\n return;\n }\n\n // Get the block content from the editor\n const block = editor.content.value.blocks.find(\n (b) => b.id === finding.blockId,\n );\n if (!block) {\n return;\n }\n\n // Extract HTML content from the block (text blocks have content property)\n const blockContent = (block as unknown as Record<string, unknown>).content as\n string | undefined;\n if (!blockContent) {\n return;\n }\n\n const fixedContent = await scoring.fixFinding(\n blockContent,\n finding,\n mergeTags.value,\n );\n\n if (fixedContent) {\n editor.updateBlock(finding.blockId, { content: fixedContent });\n scoring.removeFinding(finding.category, finding.id);\n }\n}\n\nfunction totalFindings(): number {\n if (!scoring.scoringResult.value) {\n return 0;\n }\n return categoryOrder.reduce((sum, cat) => {\n return (\n sum + (scoring.scoringResult.value?.categories[cat]?.findings.length ?? 0)\n );\n }, 0);\n}\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-scoring-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-scoring-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n role=\"complementary\"\n :aria-label=\"cloudT.scoring.title\"\n class=\"tpl-scoring-panel tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\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\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ShieldCheck :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.scoring.title }}</span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-1\">\n <button\n v-if=\"scoring.scoringResult.value && !scoring.isScoring.value\"\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.rescore\"\n :title=\"cloudT.scoring.rescore\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.scoring.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"scoring.isScoring.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.scoring.scoring }}\n </p>\n <LoadingTrack class=\"tpl:w-3/4\" />\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"scoring.error.value && !scoring.scoringResult.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.error }}\n </p>\n <button\n class=\"tpl:mt-2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"triggerScore()\"\n >\n <RefreshCw :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.scoring.rescore }}\n </button>\n </div>\n\n <!-- Results -->\n <div\n v-else-if=\"scoring.scoringResult.value\"\n class=\"tpl:flex tpl:flex-col tpl:gap-4\"\n >\n <!-- Overall score -->\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:p-5\"\n :style=\"{\n backgroundColor: scoreBgColor(scoring.scoringResult.value.score),\n }\"\n >\n <span\n class=\"tpl:text-4xl tpl:font-bold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(scoring.scoringResult.value.score),\n }\"\n >\n {{ scoring.scoringResult.value.score }}\n </span>\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:uppercase tpl:tracking-wider tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.overallScore }}\n </span>\n <span\n v-if=\"totalFindings() > 0\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ totalFindings() }}\n {{ cloudT.scoring.findings }}\n </span>\n </div>\n\n <!-- Fix error -->\n <div\n v-if=\"scoring.fixError.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.scoring.fixError }}</span>\n </div>\n\n <!-- Category cards -->\n <div\n v-for=\"category in categoryOrder\"\n :key=\"category\"\n class=\"tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)]\"\n >\n <!-- Category header -->\n <button\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-2.5 tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:bg-[var(--tpl-bg)]\"\n @click=\"toggleCategory(category)\"\n >\n <component\n :is=\"categoryIcons[category]\"\n :size=\"14\"\n :stroke-width=\"2\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n />\n <span\n class=\"tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.scoring.categories[category] }}\n </span>\n <span\n class=\"tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-semibold tpl:tabular-nums\"\n :style=\"{\n color: scoreColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n backgroundColor: scoreBgColor(\n scoring.scoringResult.value.categories[category].score,\n ),\n }\"\n >\n {{ scoring.scoringResult.value.categories[category].score }}\n </span>\n <span\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length > 0\n \"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n scoring.scoringResult.value.categories[category].findings\n .length\n }}\n </span>\n <ChevronDown\n :size=\"12\"\n :stroke-width=\"2\"\n class=\"tpl:transition-transform tpl:duration-200 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"\n expandedCategories[category]\n ? 'tpl:rotate-0'\n : 'tpl:-rotate-90'\n \"\n />\n </button>\n\n <!-- Findings list -->\n <div\n v-if=\"expandedCategories[category]\"\n class=\"tpl:border-t tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"\n scoring.scoringResult.value.categories[category].findings\n .length === 0\n \"\n class=\"tpl:px-3 tpl:py-3 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.scoring.noFindings }}\n </div>\n <div\n v-for=\"finding in scoring.scoringResult.value.categories[\n category\n ].findings\"\n :key=\"finding.id\"\n class=\"tpl:border-t tpl:px-3 tpl:py-2.5 tpl:first:border-t-0 tpl:border-[var(--tpl-border-light)]\"\n >\n <div class=\"tpl:flex tpl:items-start tpl:gap-2\">\n <!-- Severity icon -->\n <component\n :is=\"\n finding.severity === 'high'\n ? CircleAlert\n : finding.severity === 'medium'\n ? TriangleAlert\n : Info\n \"\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n :style=\"{\n color: severityColor(finding.severity),\n }\"\n />\n <div class=\"tpl:flex-1 tpl:min-w-0\">\n <!-- Severity badge + message -->\n <div class=\"tpl:flex tpl:items-start tpl:gap-1.5\">\n <span\n class=\"tpl:mt-0.5 tpl:shrink-0 tpl:rounded tpl:px-1 tpl:py-px tpl:text-[10px] tpl:font-medium tpl:leading-tight\"\n :style=\"{\n color: severityColor(finding.severity),\n backgroundColor: severityBgColor(finding.severity),\n }\"\n >\n {{ cloudT.scoring.severity[finding.severity] }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\"\n >\n {{ finding.message }}\n </span>\n </div>\n <!-- Suggestion -->\n <p\n v-if=\"finding.suggestion\"\n class=\"tpl:mt-1 tpl:text-[10px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ finding.suggestion }}\n </p>\n <!-- Fix button -->\n <div\n v-if=\"finding.blockId\"\n class=\"tpl:mt-2 tpl:flex tpl:justify-center\"\n >\n <button\n class=\"tpl-scoring-fix-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded tpl:border tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)]\"\n style=\"background-color: transparent\"\n :disabled=\"scoring.fixingFindingId.value !== null\"\n @click=\"handleFix(finding)\"\n >\n <LoaderCircle\n v-if=\"scoring.fixingFindingId.value === finding.id\"\n class=\"tpl-spinner\"\n :size=\"11\"\n :stroke-width=\"2\"\n />\n <Sparkles v-else :size=\"11\" :stroke-width=\"2\" />\n {{\n scoring.fixingFindingId.value === finding.id\n ? cloudT.scoring.fixing\n : cloudT.scoring.fix\n }}\n </button>\n <p\n v-if=\"scoring.fixError.value\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ scoring.fixError.value }}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Empty state (no result, no loading, no error) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <ShieldCheck\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.scoring.emptyState }}\n </p>\n </div>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:px-4 tpl:pb-2 tpl:pt-2 tpl:text-center tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-scoring-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-scoring-fix-btn:not(:disabled):hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import { E as e, F as t, N as n, S as r, at as i, b as a, ct as o, f as s, g as c, h as l, lt as u, p as d, u as f, ut as p } from "./draggable-IB4x6w2O.js";
2
- import { G as ee } from "./src-DveVYoWS.js";
2
+ import { G as ee } from "./src-BR3I3gOW.js";
3
3
  import { $ as te, q as ne, v as re } from "./keys-DCWeHkSF.js";
4
4
  import { t as ie } from "./useI18n-CyO_Nmca.js";
5
5
  import { n as ae, t as m } from "./templateSettingsFields-YCFVBVYh.js";
6
- import { At as h, _t as g, dt as oe, w as _, wt as v } from "./icons-BKkJ-5xF.js";
6
+ import { T as h, Tt as g, ft as oe, jt as _, vt as v } from "./icons-Bad3WZ3G.js";
7
7
  import { g as y, h as se, l as ce, m as le, p as b, t as x } from "./styleConstants-C_YAPdQS.js";
8
- import { c as S } from "./LogicTagInsertButton-CHc1yZ9v.js";
9
- import { n as C, t as w } from "./ToggleSwitch-0dGCYnRY.js";
8
+ import { c as S } from "./LogicTagInsertButton-3pIbVE9p.js";
9
+ import { n as C, t as w } from "./ToggleSwitch-niSL2uog.js";
10
10
  //#region src/components/TemplateSettings.vue?vue&type=script&setup=true&lang.ts
11
11
  var T = {
12
12
  "data-testid": "template-settings",
@@ -81,7 +81,7 @@ var T = {
81
81
  class: o($),
82
82
  "data-testid": "template-settings-card-layout"
83
83
  }, [
84
- d("div", D, [a(i(_), {
84
+ d("div", D, [a(i(h), {
85
85
  class: "tpl:text-[var(--tpl-text-muted)]",
86
86
  size: 14,
87
87
  "stroke-width": 2
@@ -110,7 +110,7 @@ var T = {
110
110
  class: o($),
111
111
  "data-testid": "template-settings-card-appearance"
112
112
  }, [
113
- d("div", P, [a(i(h), {
113
+ d("div", P, [a(i(_), {
114
114
  class: "tpl:text-[var(--tpl-text-muted)]",
115
115
  size: 14,
116
116
  "stroke-width": 2
@@ -150,7 +150,7 @@ var T = {
150
150
  class: o($),
151
151
  "data-testid": "template-settings-card-language"
152
152
  }, [
153
- d("div", H, [a(i(g), {
153
+ d("div", H, [a(i(v), {
154
154
  class: "tpl:text-[var(--tpl-text-muted)]",
155
155
  size: 14,
156
156
  "stroke-width": 2
@@ -180,7 +180,7 @@ var T = {
180
180
  key: 3,
181
181
  class: o($),
182
182
  "data-testid": "template-settings-card-preheader"
183
- }, [d("div", me, [a(i(v), {
183
+ }, [d("div", me, [a(i(g), {
184
184
  class: "tpl:text-[var(--tpl-text-muted)]",
185
185
  size: 14,
186
186
  "stroke-width": 2
@@ -204,4 +204,4 @@ var T = {
204
204
  //#endregion
205
205
  export { W as default };
206
206
 
207
- //# sourceMappingURL=TemplateSettings-Dr5OJ0Gi.js.map
207
+ //# sourceMappingURL=TemplateSettings-DVN1t3yF.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateSettings-Dr5OJ0Gi.js","names":[],"sources":["../../../src/components/TemplateSettings.vue","../../../src/components/TemplateSettings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport ToggleSwitch from \"./ToggleSwitch.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n resolveContentDirection,\n type TemplateSettings,\n} from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n FONTS_MANAGER_KEY,\n TEMPLATE_SETTINGS_FIELDS_KEY,\n requireInject,\n} from \"../keys\";\nimport {\n ALL_TEMPLATE_SETTINGS_FIELDS,\n TEMPLATE_SETTINGS_FIELD_CARDS,\n type TemplateSettingsCard,\n type TemplateSettingsField,\n} from \"../utils/templateSettingsFields\";\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\n// Default to every field: the key is absent whenever a consumer configured no\n// `templateSettings`, and that config only ever narrows.\nconst allowedFields = inject(\n TEMPLATE_SETTINGS_FIELDS_KEY,\n new Set(ALL_TEMPLATE_SETTINGS_FIELDS),\n);\n\nfunction shows(field: TemplateSettingsField): boolean {\n return allowedFields.has(field);\n}\n\n// A card earns its heading only if it still has something to hold. Derived from\n// the field→card map rather than hand-listed per card, so a new setting joins\n// its card's gate the moment it joins the map.\nfunction showsCard(card: TemplateSettingsCard): boolean {\n return ALL_TEMPLATE_SETTINGS_FIELDS.some(\n (field) => TEMPLATE_SETTINGS_FIELD_CARDS[field] === card && shows(field),\n );\n}\n\n// Tips is advice about the settings, so it rides on there being at least one:\n// a Settings tab holding nothing but advice reads as a bug. Plain const, like\n// the two helpers above — the allowlist is init-time config, not reactive\n// state, and a `computed` here would imply it can change.\nconst showsAnyField = allowedFields.size > 0;\n\nconst isRtl = computed(() => resolveContentDirection(props.settings) === \"rtl\");\n\n// Cards lay their contents out with a flex gap rather than a bottom margin on\n// every child but the last. A margin leaves dead space under whichever field\n// became last once an excluded one is dropped, and which field that is depends\n// on the consumer's allowlist — so there is no \"last\" to special-case.\nconst cardStackClass = `${cardClass} tpl:flex tpl:flex-col tpl:gap-3.5`;\n</script>\n\n<template>\n <aside\n data-testid=\"template-settings\"\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\n v-if=\"showsCard('layout')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-layout\"\n >\n <div\n class=\"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 v-if=\"shows('width')\" data-testid=\"template-settings-width-preset\">\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 v-if=\"shows('width')\" data-testid=\"template-settings-width-custom\">\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div\n v-if=\"showsCard('appearance')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-appearance\"\n >\n <div\n class=\"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\n v-if=\"shows('backgroundColor')\"\n data-testid=\"template-settings-background\"\n >\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 v-if=\"shows('textColor')\"\n data-testid=\"template-settings-text-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.textColor }}</label>\n <ColorPicker\n :model-value=\"settings.textColor ?? ''\"\n seed-color=\"#1a1a1a\"\n @update:model-value=\"emit('update', { textColor: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('linkColor')\"\n data-testid=\"template-settings-link-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.linkColor }}</label>\n <ColorPicker\n :model-value=\"settings.linkColor ?? ''\"\n seed-color=\"#0066cc\"\n @update:model-value=\"\n emit('update', { linkColor: $event || undefined })\n \"\n />\n </div>\n\n <div\n v-if=\"shows('linkUnderline')\"\n data-testid=\"template-settings-link-underline\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"settings.linkUnderline\"\n :label=\"t.templateSettings.linkUnderline\"\n @update:model-value=\"emit('update', { linkUnderline: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('fontFamily')\"\n data-testid=\"template-settings-font-family\"\n >\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\n v-if=\"showsCard('language')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-language\"\n >\n <div\n class=\"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 v-if=\"shows('locale')\" data-testid=\"template-settings-locale\">\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\n <div\n v-if=\"shows('direction')\"\n data-testid=\"template-settings-direction\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"isRtl\"\n :label=\"t.templateSettings.contentDirection\"\n @update:model-value=\"\n emit('update', { direction: $event ? 'rtl' : 'ltr' })\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.contentDirectionHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div\n v-if=\"showsCard('preheader')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-preheader\"\n >\n <div\n class=\"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 v-if=\"shows('preheaderText')\"\n data-testid=\"template-settings-preheader\"\n >\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 v-if=\"showsAnyField\"\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n data-testid=\"template-settings-card-tips\"\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 ToggleSwitch from \"./ToggleSwitch.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n resolveContentDirection,\n type TemplateSettings,\n} from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n FONTS_MANAGER_KEY,\n TEMPLATE_SETTINGS_FIELDS_KEY,\n requireInject,\n} from \"../keys\";\nimport {\n ALL_TEMPLATE_SETTINGS_FIELDS,\n TEMPLATE_SETTINGS_FIELD_CARDS,\n type TemplateSettingsCard,\n type TemplateSettingsField,\n} from \"../utils/templateSettingsFields\";\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\n// Default to every field: the key is absent whenever a consumer configured no\n// `templateSettings`, and that config only ever narrows.\nconst allowedFields = inject(\n TEMPLATE_SETTINGS_FIELDS_KEY,\n new Set(ALL_TEMPLATE_SETTINGS_FIELDS),\n);\n\nfunction shows(field: TemplateSettingsField): boolean {\n return allowedFields.has(field);\n}\n\n// A card earns its heading only if it still has something to hold. Derived from\n// the field→card map rather than hand-listed per card, so a new setting joins\n// its card's gate the moment it joins the map.\nfunction showsCard(card: TemplateSettingsCard): boolean {\n return ALL_TEMPLATE_SETTINGS_FIELDS.some(\n (field) => TEMPLATE_SETTINGS_FIELD_CARDS[field] === card && shows(field),\n );\n}\n\n// Tips is advice about the settings, so it rides on there being at least one:\n// a Settings tab holding nothing but advice reads as a bug. Plain const, like\n// the two helpers above — the allowlist is init-time config, not reactive\n// state, and a `computed` here would imply it can change.\nconst showsAnyField = allowedFields.size > 0;\n\nconst isRtl = computed(() => resolveContentDirection(props.settings) === \"rtl\");\n\n// Cards lay their contents out with a flex gap rather than a bottom margin on\n// every child but the last. A margin leaves dead space under whichever field\n// became last once an excluded one is dropped, and which field that is depends\n// on the consumer's allowlist — so there is no \"last\" to special-case.\nconst cardStackClass = `${cardClass} tpl:flex tpl:flex-col tpl:gap-3.5`;\n</script>\n\n<template>\n <aside\n data-testid=\"template-settings\"\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\n v-if=\"showsCard('layout')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-layout\"\n >\n <div\n class=\"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 v-if=\"shows('width')\" data-testid=\"template-settings-width-preset\">\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 v-if=\"shows('width')\" data-testid=\"template-settings-width-custom\">\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div\n v-if=\"showsCard('appearance')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-appearance\"\n >\n <div\n class=\"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\n v-if=\"shows('backgroundColor')\"\n data-testid=\"template-settings-background\"\n >\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 v-if=\"shows('textColor')\"\n data-testid=\"template-settings-text-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.textColor }}</label>\n <ColorPicker\n :model-value=\"settings.textColor ?? ''\"\n seed-color=\"#1a1a1a\"\n @update:model-value=\"emit('update', { textColor: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('linkColor')\"\n data-testid=\"template-settings-link-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.linkColor }}</label>\n <ColorPicker\n :model-value=\"settings.linkColor ?? ''\"\n seed-color=\"#0066cc\"\n @update:model-value=\"\n emit('update', { linkColor: $event || undefined })\n \"\n />\n </div>\n\n <div\n v-if=\"shows('linkUnderline')\"\n data-testid=\"template-settings-link-underline\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"settings.linkUnderline\"\n :label=\"t.templateSettings.linkUnderline\"\n @update:model-value=\"emit('update', { linkUnderline: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('fontFamily')\"\n data-testid=\"template-settings-font-family\"\n >\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\n v-if=\"showsCard('language')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-language\"\n >\n <div\n class=\"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 v-if=\"shows('locale')\" data-testid=\"template-settings-locale\">\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\n <div\n v-if=\"shows('direction')\"\n data-testid=\"template-settings-direction\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"isRtl\"\n :label=\"t.templateSettings.contentDirection\"\n @update:model-value=\"\n emit('update', { direction: $event ? 'rtl' : 'ltr' })\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.contentDirectionHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div\n v-if=\"showsCard('preheader')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-preheader\"\n >\n <div\n class=\"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 v-if=\"shows('preheaderText')\"\n data-testid=\"template-settings-preheader\"\n >\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 v-if=\"showsAnyField\"\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n data-testid=\"template-settings-card-tips\"\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"],"mappings":"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"}
1
+ {"version":3,"file":"TemplateSettings-DVN1t3yF.js","names":[],"sources":["../../../src/components/TemplateSettings.vue","../../../src/components/TemplateSettings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport ToggleSwitch from \"./ToggleSwitch.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n resolveContentDirection,\n type TemplateSettings,\n} from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n FONTS_MANAGER_KEY,\n TEMPLATE_SETTINGS_FIELDS_KEY,\n requireInject,\n} from \"../keys\";\nimport {\n ALL_TEMPLATE_SETTINGS_FIELDS,\n TEMPLATE_SETTINGS_FIELD_CARDS,\n type TemplateSettingsCard,\n type TemplateSettingsField,\n} from \"../utils/templateSettingsFields\";\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\n// Default to every field: the key is absent whenever a consumer configured no\n// `templateSettings`, and that config only ever narrows.\nconst allowedFields = inject(\n TEMPLATE_SETTINGS_FIELDS_KEY,\n new Set(ALL_TEMPLATE_SETTINGS_FIELDS),\n);\n\nfunction shows(field: TemplateSettingsField): boolean {\n return allowedFields.has(field);\n}\n\n// A card earns its heading only if it still has something to hold. Derived from\n// the field→card map rather than hand-listed per card, so a new setting joins\n// its card's gate the moment it joins the map.\nfunction showsCard(card: TemplateSettingsCard): boolean {\n return ALL_TEMPLATE_SETTINGS_FIELDS.some(\n (field) => TEMPLATE_SETTINGS_FIELD_CARDS[field] === card && shows(field),\n );\n}\n\n// Tips is advice about the settings, so it rides on there being at least one:\n// a Settings tab holding nothing but advice reads as a bug. Plain const, like\n// the two helpers above — the allowlist is init-time config, not reactive\n// state, and a `computed` here would imply it can change.\nconst showsAnyField = allowedFields.size > 0;\n\nconst isRtl = computed(() => resolveContentDirection(props.settings) === \"rtl\");\n\n// Cards lay their contents out with a flex gap rather than a bottom margin on\n// every child but the last. A margin leaves dead space under whichever field\n// became last once an excluded one is dropped, and which field that is depends\n// on the consumer's allowlist — so there is no \"last\" to special-case.\nconst cardStackClass = `${cardClass} tpl:flex tpl:flex-col tpl:gap-3.5`;\n</script>\n\n<template>\n <aside\n data-testid=\"template-settings\"\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\n v-if=\"showsCard('layout')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-layout\"\n >\n <div\n class=\"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 v-if=\"shows('width')\" data-testid=\"template-settings-width-preset\">\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 v-if=\"shows('width')\" data-testid=\"template-settings-width-custom\">\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div\n v-if=\"showsCard('appearance')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-appearance\"\n >\n <div\n class=\"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\n v-if=\"shows('backgroundColor')\"\n data-testid=\"template-settings-background\"\n >\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 v-if=\"shows('textColor')\"\n data-testid=\"template-settings-text-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.textColor }}</label>\n <ColorPicker\n :model-value=\"settings.textColor ?? ''\"\n seed-color=\"#1a1a1a\"\n @update:model-value=\"emit('update', { textColor: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('linkColor')\"\n data-testid=\"template-settings-link-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.linkColor }}</label>\n <ColorPicker\n :model-value=\"settings.linkColor ?? ''\"\n seed-color=\"#0066cc\"\n @update:model-value=\"\n emit('update', { linkColor: $event || undefined })\n \"\n />\n </div>\n\n <div\n v-if=\"shows('linkUnderline')\"\n data-testid=\"template-settings-link-underline\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"settings.linkUnderline\"\n :label=\"t.templateSettings.linkUnderline\"\n @update:model-value=\"emit('update', { linkUnderline: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('fontFamily')\"\n data-testid=\"template-settings-font-family\"\n >\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\n v-if=\"showsCard('language')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-language\"\n >\n <div\n class=\"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 v-if=\"shows('locale')\" data-testid=\"template-settings-locale\">\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\n <div\n v-if=\"shows('direction')\"\n data-testid=\"template-settings-direction\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"isRtl\"\n :label=\"t.templateSettings.contentDirection\"\n @update:model-value=\"\n emit('update', { direction: $event ? 'rtl' : 'ltr' })\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.contentDirectionHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div\n v-if=\"showsCard('preheader')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-preheader\"\n >\n <div\n class=\"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 v-if=\"shows('preheaderText')\"\n data-testid=\"template-settings-preheader\"\n >\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 v-if=\"showsAnyField\"\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n data-testid=\"template-settings-card-tips\"\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 ToggleSwitch from \"./ToggleSwitch.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport {\n resolveContentDirection,\n type TemplateSettings,\n} from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n FONTS_MANAGER_KEY,\n TEMPLATE_SETTINGS_FIELDS_KEY,\n requireInject,\n} from \"../keys\";\nimport {\n ALL_TEMPLATE_SETTINGS_FIELDS,\n TEMPLATE_SETTINGS_FIELD_CARDS,\n type TemplateSettingsCard,\n type TemplateSettingsField,\n} from \"../utils/templateSettingsFields\";\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\n// Default to every field: the key is absent whenever a consumer configured no\n// `templateSettings`, and that config only ever narrows.\nconst allowedFields = inject(\n TEMPLATE_SETTINGS_FIELDS_KEY,\n new Set(ALL_TEMPLATE_SETTINGS_FIELDS),\n);\n\nfunction shows(field: TemplateSettingsField): boolean {\n return allowedFields.has(field);\n}\n\n// A card earns its heading only if it still has something to hold. Derived from\n// the field→card map rather than hand-listed per card, so a new setting joins\n// its card's gate the moment it joins the map.\nfunction showsCard(card: TemplateSettingsCard): boolean {\n return ALL_TEMPLATE_SETTINGS_FIELDS.some(\n (field) => TEMPLATE_SETTINGS_FIELD_CARDS[field] === card && shows(field),\n );\n}\n\n// Tips is advice about the settings, so it rides on there being at least one:\n// a Settings tab holding nothing but advice reads as a bug. Plain const, like\n// the two helpers above — the allowlist is init-time config, not reactive\n// state, and a `computed` here would imply it can change.\nconst showsAnyField = allowedFields.size > 0;\n\nconst isRtl = computed(() => resolveContentDirection(props.settings) === \"rtl\");\n\n// Cards lay their contents out with a flex gap rather than a bottom margin on\n// every child but the last. A margin leaves dead space under whichever field\n// became last once an excluded one is dropped, and which field that is depends\n// on the consumer's allowlist — so there is no \"last\" to special-case.\nconst cardStackClass = `${cardClass} tpl:flex tpl:flex-col tpl:gap-3.5`;\n</script>\n\n<template>\n <aside\n data-testid=\"template-settings\"\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\n v-if=\"showsCard('layout')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-layout\"\n >\n <div\n class=\"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 v-if=\"shows('width')\" data-testid=\"template-settings-width-preset\">\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 v-if=\"shows('width')\" data-testid=\"template-settings-width-custom\">\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div\n v-if=\"showsCard('appearance')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-appearance\"\n >\n <div\n class=\"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\n v-if=\"shows('backgroundColor')\"\n data-testid=\"template-settings-background\"\n >\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 v-if=\"shows('textColor')\"\n data-testid=\"template-settings-text-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.textColor }}</label>\n <ColorPicker\n :model-value=\"settings.textColor ?? ''\"\n seed-color=\"#1a1a1a\"\n @update:model-value=\"emit('update', { textColor: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('linkColor')\"\n data-testid=\"template-settings-link-color\"\n >\n <label :class=\"labelClass\">{{ t.templateSettings.linkColor }}</label>\n <ColorPicker\n :model-value=\"settings.linkColor ?? ''\"\n seed-color=\"#0066cc\"\n @update:model-value=\"\n emit('update', { linkColor: $event || undefined })\n \"\n />\n </div>\n\n <div\n v-if=\"shows('linkUnderline')\"\n data-testid=\"template-settings-link-underline\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"settings.linkUnderline\"\n :label=\"t.templateSettings.linkUnderline\"\n @update:model-value=\"emit('update', { linkUnderline: $event })\"\n />\n </div>\n\n <div\n v-if=\"shows('fontFamily')\"\n data-testid=\"template-settings-font-family\"\n >\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\n v-if=\"showsCard('language')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-language\"\n >\n <div\n class=\"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 v-if=\"shows('locale')\" data-testid=\"template-settings-locale\">\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\n <div\n v-if=\"shows('direction')\"\n data-testid=\"template-settings-direction\"\n >\n <ToggleSwitch\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n :model-value=\"isRtl\"\n :label=\"t.templateSettings.contentDirection\"\n @update:model-value=\"\n emit('update', { direction: $event ? 'rtl' : 'ltr' })\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.contentDirectionHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div\n v-if=\"showsCard('preheader')\"\n :class=\"cardStackClass\"\n data-testid=\"template-settings-card-preheader\"\n >\n <div\n class=\"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 v-if=\"shows('preheaderText')\"\n data-testid=\"template-settings-preheader\"\n >\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 v-if=\"showsAnyField\"\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n data-testid=\"template-settings-card-tips\"\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"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import { B as e, E as t, F as n, H as r, N as i, Q as a, S as o, U as s, a as ee, at as c, b as l, ct as u, f as d, g as f, h as p, m, o as te, p as h, u as g, ut as _, y as v } from "./draggable-IB4x6w2O.js";
2
- import { b as ne, u as re } from "./src-DveVYoWS.js";
3
- import { P as ie } from "./useEditorCore-DZmtKwJh.js";
2
+ import { b as ne, u as re } from "./src-BR3I3gOW.js";
3
+ import { P as ie } from "./useEditorCore-yd40TFKy.js";
4
4
  import { C as ae, F as oe, H as y, M as se, O as ce, g as le } from "./keys-DCWeHkSF.js";
5
5
  import { t as ue } from "./useI18n-CyO_Nmca.js";
6
- import { Lt as de, it as fe } from "./icons-BKkJ-5xF.js";
6
+ import { Rt as de, at as fe } from "./icons-Bad3WZ3G.js";
7
7
  import { v as pe } from "./styleConstants-C_YAPdQS.js";
8
- import { n as me, t as he } from "./MergeTagModeToggle-CdWIGo3Q.js";
9
- import { t as ge } from "./TplModal-D8DoPbFa.js";
10
- import { t as _e } from "./BlockPreviewCanvas-D31TNib3.js";
8
+ import { n as me, t as he } from "./MergeTagModeToggle-CYmueXS2.js";
9
+ import { t as ge } from "./TplModal-C_eIPykE.js";
10
+ import { t as _e } from "./BlockPreviewCanvas-BXpcAiiY.js";
11
11
  //#region src/utils/validateEmailShape.ts
12
12
  var b = /[,;<>()[\]\\"'`|/?#@!$%^&*+=~{}]/;
13
13
  function x(e) {
@@ -208,4 +208,4 @@ var S = ["aria-busy"], C = {
208
208
  //#endregion
209
209
  export { R as default };
210
210
 
211
- //# sourceMappingURL=TestEmailModal-CrjzVw8e.js.map
211
+ //# sourceMappingURL=TestEmailModal-DZBCmBtT.js.map