@templatical/editor 0.39.1 → 0.39.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/{AiChatSidebar-pOxCZWz9.js → AiChatSidebar-5qqrS6uy.js} +69 -63
  2. package/dist/{AiFeatureMenu-Bljk6Qjv.js → AiFeatureMenu-BBO_Ojlp.js} +1 -1
  3. package/dist/{BlockPreviewCanvas-DXGjUECG.js → BlockPreviewCanvas-BMC16_QU.js} +1 -1
  4. package/dist/{CloudHeaderExtras-DaocwNmy.js → CloudHeaderExtras-Cg6uF-yQ.js} +1 -1
  5. package/dist/{CloudPanels-CyZAnSJY.js → CloudPanels-D1Gg4qrG.js} +80 -66
  6. package/dist/{CollaboratorBar-C7Zca3g1.js → CollaboratorBar-Cmn0LccS.js} +3 -3
  7. package/dist/{CommentsPanel-CNOb7YCm.js → CommentsPanel-twNhYUqZ.js} +1 -1
  8. package/dist/{CommentsSidebar-VmwjU9va.js → CommentsSidebar-DDO0Qx8U.js} +122 -112
  9. package/dist/{CountdownToolbar-C2kfS6Oy.js → CountdownToolbar-nvHxu8Gu.js} +1 -1
  10. package/dist/DesignReferenceSidebar-C8ohNhOd.js +280 -0
  11. package/dist/{IssuesPanel-DL2Xr14_.js → IssuesPanel-Ck0EGoV5.js} +4 -4
  12. package/dist/{LoadingTrack-C2jKCU2d.js → LoadingTrack-C1Epulqq.js} +1 -1
  13. package/dist/MediaEditModal-D4GEq2md.js +3545 -0
  14. package/dist/{MediaPanels-Cc4UkfG1.js → MediaPanels-sapgKtxu.js} +1 -1
  15. package/dist/{ParagraphEditor-DkkPSexG.js → ParagraphEditor-D6FRxr8Y.js} +2 -2
  16. package/dist/{RestoreVersionDialog-BgueZnZr.js → RestoreVersionDialog--b0qfNaK.js} +1 -1
  17. package/dist/{SaveBlockDialog-BuTuVlLr.js → SaveBlockDialog-C0BdEGeL.js} +6 -6
  18. package/dist/{SavedBlocksBrowserModal-Srhix8BB.js → SavedBlocksBrowserModal-BzDgXtr4.js} +8 -8
  19. package/dist/{SavedBlocksPanels-B2iScCS-.js → SavedBlocksPanels-DN-bDZzy.js} +1 -1
  20. package/dist/SavedBlocksPickBar-pEi9Et8Q.js +53 -0
  21. package/dist/{TemplateScoringPanel-C2Rdlyn6.js → TemplateScoringPanel-B1wcfE0w.js} +62 -56
  22. package/dist/{TestEmailModal-DJ5q_u4N.js → TestEmailModal-maM8SLBV.js} +6 -6
  23. package/dist/{TestEmailPanel-CP24UGst.js → TestEmailPanel-8iAZYpCK.js} +1 -1
  24. package/dist/{TitleEditor-QBBOXtxx.js → TitleEditor-L26QXwna.js} +1 -1
  25. package/dist/{Toolbar-COD2TW20.js → Toolbar-B2kDLFhn.js} +314 -314
  26. package/dist/{TplModal-C8xGjC1l.js → TplModal-DVMS_nAn.js} +1 -1
  27. package/dist/{VersionHistoryPanels-BrK6na4A.js → VersionHistoryPanels-D8f8KvKX.js} +1 -1
  28. package/dist/bundle-stats.json +7 -7
  29. package/dist/{ca-Jx2CDwtn.js → ca-BTf1Zbqb.js} +9 -0
  30. package/dist/{ca-YlPDhElv.js → ca-Bqeygp1_.js} +1 -0
  31. package/dist/cdn/chunks/{AiChatSidebar-CjSEoK4x.js → AiChatSidebar-ctfIU4i0.js} +67 -61
  32. package/dist/cdn/chunks/AiChatSidebar-ctfIU4i0.js.map +1 -0
  33. package/dist/cdn/chunks/{AiFeatureMenu-l0K5TOSn.js → AiFeatureMenu-ByygdH_9.js} +2 -2
  34. package/dist/cdn/chunks/{AiFeatureMenu-l0K5TOSn.js.map → AiFeatureMenu-ByygdH_9.js.map} +1 -1
  35. package/dist/cdn/chunks/{BlockPreviewCanvas-lQrAdgG3.js → BlockPreviewCanvas-DeS7Sa--.js} +2 -2
  36. package/dist/cdn/chunks/{BlockPreviewCanvas-lQrAdgG3.js.map → BlockPreviewCanvas-DeS7Sa--.js.map} +1 -1
  37. package/dist/cdn/chunks/{CloudHeaderExtras-CXNe7Q-4.js → CloudHeaderExtras-BxsMpvVr.js} +2 -2
  38. package/dist/cdn/chunks/{CloudHeaderExtras-CXNe7Q-4.js.map → CloudHeaderExtras-BxsMpvVr.js.map} +1 -1
  39. package/dist/cdn/chunks/{CloudPanels-bHhh9AUA.js → CloudPanels-DmxmqAS_.js} +78 -64
  40. package/dist/cdn/chunks/CloudPanels-DmxmqAS_.js.map +1 -0
  41. package/dist/cdn/chunks/{CollaboratorBar-DgqcaGGI.js → CollaboratorBar-B8ttqAqX.js} +4 -4
  42. package/dist/cdn/chunks/CollaboratorBar-B8ttqAqX.js.map +1 -0
  43. package/dist/cdn/chunks/{CommentsPanel-Cy4TlTHC.js → CommentsPanel-C8ibhaiK.js} +2 -2
  44. package/dist/cdn/chunks/{CommentsPanel-Cy4TlTHC.js.map → CommentsPanel-C8ibhaiK.js.map} +1 -1
  45. package/dist/cdn/chunks/{CommentsSidebar-C7oAP3Mx.js → CommentsSidebar-CybWfZ3V.js} +118 -108
  46. package/dist/cdn/chunks/CommentsSidebar-CybWfZ3V.js.map +1 -0
  47. package/dist/cdn/chunks/{CountdownToolbar-CN9h4nlC.js → CountdownToolbar-DRESXiVD.js} +2 -2
  48. package/dist/cdn/chunks/{CountdownToolbar-CN9h4nlC.js.map → CountdownToolbar-DRESXiVD.js.map} +1 -1
  49. package/dist/cdn/chunks/DesignReferenceSidebar-CtaoY-hl.js +230 -0
  50. package/dist/cdn/chunks/DesignReferenceSidebar-CtaoY-hl.js.map +1 -0
  51. package/dist/cdn/chunks/{IssuesPanel-DdFk-Blf.js → IssuesPanel-C6RlIxoi.js} +5 -5
  52. package/dist/cdn/chunks/IssuesPanel-C6RlIxoi.js.map +1 -0
  53. package/dist/cdn/chunks/{LoadingTrack-Ba5xh5HD.js → LoadingTrack-_7DH_5aw.js} +2 -2
  54. package/dist/cdn/chunks/{LoadingTrack-Ba5xh5HD.js.map → LoadingTrack-_7DH_5aw.js.map} +1 -1
  55. package/dist/cdn/chunks/MediaEditModal-BBZRcYKe.js +3529 -0
  56. package/dist/cdn/chunks/MediaEditModal-BBZRcYKe.js.map +1 -0
  57. package/dist/cdn/chunks/{MediaPanels-kX9AZQ7K.js → MediaPanels-CSBCsFwT.js} +2 -2
  58. package/dist/cdn/chunks/{MediaPanels-kX9AZQ7K.js.map → MediaPanels-CSBCsFwT.js.map} +1 -1
  59. package/dist/cdn/chunks/{ParagraphEditor-DO4RzP8l.js → ParagraphEditor-R2ph_UOo.js} +3 -3
  60. package/dist/cdn/chunks/ParagraphEditor-R2ph_UOo.js.map +1 -0
  61. package/dist/cdn/chunks/{RestoreVersionDialog-CzZ_sFnz.js → RestoreVersionDialog-C-dwbb7T.js} +2 -2
  62. package/dist/cdn/chunks/{RestoreVersionDialog-CzZ_sFnz.js.map → RestoreVersionDialog-C-dwbb7T.js.map} +1 -1
  63. package/dist/cdn/chunks/{SaveBlockDialog-LgxYrXyx.js → SaveBlockDialog-C_BPjVv6.js} +7 -7
  64. package/dist/cdn/chunks/SaveBlockDialog-C_BPjVv6.js.map +1 -0
  65. package/dist/cdn/chunks/{SavedBlocksBrowserModal-CdeSsTpb.js → SavedBlocksBrowserModal-CrrGEfFb.js} +9 -9
  66. package/dist/cdn/chunks/SavedBlocksBrowserModal-CrrGEfFb.js.map +1 -0
  67. package/dist/cdn/chunks/{SavedBlocksPanels-B5hakNJj.js → SavedBlocksPanels-DfTaMPAr.js} +2 -2
  68. package/dist/cdn/chunks/{SavedBlocksPanels-B5hakNJj.js.map → SavedBlocksPanels-DfTaMPAr.js.map} +1 -1
  69. package/dist/cdn/chunks/SavedBlocksPickBar-DziwHYSu.js +55 -0
  70. package/dist/cdn/chunks/SavedBlocksPickBar-DziwHYSu.js.map +1 -0
  71. package/dist/cdn/chunks/{TemplateScoringPanel--JxR6CsQ.js → TemplateScoringPanel-CR6RG6dZ.js} +95 -89
  72. package/dist/cdn/chunks/TemplateScoringPanel-CR6RG6dZ.js.map +1 -0
  73. package/dist/cdn/chunks/{TestEmailModal-BAuDxJtD.js → TestEmailModal-DId9Og-N.js} +7 -7
  74. package/dist/cdn/chunks/TestEmailModal-DId9Og-N.js.map +1 -0
  75. package/dist/cdn/chunks/{TestEmailPanel-GUGZisLp.js → TestEmailPanel-B8LwXmeX.js} +2 -2
  76. package/dist/cdn/chunks/{TestEmailPanel-GUGZisLp.js.map → TestEmailPanel-B8LwXmeX.js.map} +1 -1
  77. package/dist/cdn/chunks/{TitleEditor-uKu33Nwr.js → TitleEditor-C7KMzX1e.js} +2 -2
  78. package/dist/cdn/chunks/{TitleEditor-uKu33Nwr.js.map → TitleEditor-C7KMzX1e.js.map} +1 -1
  79. package/dist/cdn/chunks/{Toolbar-dxDYwN5E.js → Toolbar-DqFhWxgW.js} +310 -310
  80. package/dist/cdn/chunks/Toolbar-DqFhWxgW.js.map +1 -0
  81. package/dist/cdn/chunks/{TplModal-DuwS2ifK.js → TplModal-BKqDFLq3.js} +2 -2
  82. package/dist/cdn/chunks/{TplModal-DuwS2ifK.js.map → TplModal-BKqDFLq3.js.map} +1 -1
  83. package/dist/cdn/chunks/{VersionHistoryPanels-GeR6MStB.js → VersionHistoryPanels-DcCFFDK6.js} +2 -2
  84. package/dist/cdn/chunks/{VersionHistoryPanels-GeR6MStB.js.map → VersionHistoryPanels-DcCFFDK6.js.map} +1 -1
  85. package/dist/cdn/chunks/{ca-Jx2CDwtn.js → ca-BTf1Zbqb.js} +10 -1
  86. package/dist/cdn/chunks/{ca-Jx2CDwtn.js.map → ca-BTf1Zbqb.js.map} +1 -1
  87. package/dist/cdn/chunks/{ca-YlPDhElv.js → ca-Bqeygp1_.js} +2 -1
  88. package/dist/cdn/chunks/{ca-YlPDhElv.js.map → ca-Bqeygp1_.js.map} +1 -1
  89. package/dist/cdn/chunks/{de-CONjnzrF.js → de-CY6SlqBx.js} +2 -1
  90. package/dist/cdn/chunks/{de-CONjnzrF.js.map → de-CY6SlqBx.js.map} +1 -1
  91. package/dist/{de-BytZwgn6.js → cdn/chunks/de-mMY5yQJu.js} +5 -0
  92. package/dist/cdn/chunks/de-mMY5yQJu.js.map +1 -0
  93. package/dist/cdn/chunks/{de-Ck0pBGHu.js → de-xzrh9PA3.js} +10 -1
  94. package/dist/cdn/chunks/{de-Ck0pBGHu.js.map → de-xzrh9PA3.js.map} +1 -1
  95. package/dist/cdn/chunks/editor-modal-C-yBlj2C.js +2700 -0
  96. package/dist/cdn/chunks/editor-modal-C-yBlj2C.js.map +1 -0
  97. package/dist/cdn/chunks/{en-BAMooiOF.js → en-8V-2kDcp.js} +2 -1
  98. package/dist/cdn/chunks/{en-BAMooiOF.js.map → en-8V-2kDcp.js.map} +1 -1
  99. package/dist/{en-DyOSV0lY.js → cdn/chunks/en-ChVeGQBY.js} +5 -0
  100. package/dist/cdn/chunks/en-ChVeGQBY.js.map +1 -0
  101. package/dist/cdn/chunks/en-DYtFUE2l.js +2 -0
  102. package/dist/cdn/chunks/{es-lagTvUfX.js → es-BT4LSpE2.js} +10 -1
  103. package/dist/cdn/chunks/{es-lagTvUfX.js.map → es-BT4LSpE2.js.map} +1 -1
  104. package/dist/cdn/chunks/{es-I47lHs2m.js → es-Cmn1rT3l.js} +2 -1
  105. package/dist/cdn/chunks/{es-I47lHs2m.js.map → es-Cmn1rT3l.js.map} +1 -1
  106. package/dist/cdn/chunks/{fr-B8MIKjT4.js → fr-2yDa4zXM.js} +2 -1
  107. package/dist/cdn/chunks/{fr-B8MIKjT4.js.map → fr-2yDa4zXM.js.map} +1 -1
  108. package/dist/cdn/chunks/{fr-C4hVl5Lk.js → fr-CI7OJoql.js} +10 -1
  109. package/dist/cdn/chunks/{fr-C4hVl5Lk.js.map → fr-CI7OJoql.js.map} +1 -1
  110. package/dist/cdn/chunks/{nl-CBLxcb-0.js → nl-Bzk2zRI8.js} +2 -1
  111. package/dist/cdn/chunks/{nl-CBLxcb-0.js.map → nl-Bzk2zRI8.js.map} +1 -1
  112. package/dist/cdn/chunks/{nl-CReD7Apd.js → nl-D8y5OWGB.js} +10 -1
  113. package/dist/cdn/chunks/{nl-CReD7Apd.js.map → nl-D8y5OWGB.js.map} +1 -1
  114. package/dist/cdn/chunks/{pt-BR-C1WLSGDh.js → pt-BR-CoCwRUi0.js} +4 -1
  115. package/dist/cdn/chunks/pt-BR-CoCwRUi0.js.map +1 -0
  116. package/dist/{pt-BR-BRA0-vGP.js → cdn/chunks/pt-BR-DMCbUBvJ.js} +11 -0
  117. package/dist/cdn/chunks/{pt-BR-BRA0-vGP.js.map → pt-BR-DMCbUBvJ.js.map} +1 -1
  118. package/dist/cdn/chunks/{pt-BR-CMSRfX7t.js → pt-BR-DqtSCTUi.js} +2 -1
  119. package/dist/cdn/chunks/{pt-BR-CMSRfX7t.js.map → pt-BR-DqtSCTUi.js.map} +1 -1
  120. package/dist/cdn/chunks/{src-CZ-W8YBa.js → src-kSeXM7B2.js} +2 -2
  121. package/dist/cdn/chunks/{src-CZ-W8YBa.js.map → src-kSeXM7B2.js.map} +1 -1
  122. package/dist/cdn/chunks/{useEditorCore-BHmIp1hW.js → useEditorCore-CXYI14ux.js} +14 -14
  123. package/dist/cdn/chunks/{useEditorCore-BHmIp1hW.js.map → useEditorCore-CXYI14ux.js.map} +1 -1
  124. package/dist/cdn/editor.css +1 -1
  125. package/dist/cdn/editor.js +15 -15
  126. package/dist/cdn/editor.js.map +1 -1
  127. package/dist/{de-CONjnzrF.js → de-CY6SlqBx.js} +1 -0
  128. package/dist/{cdn/chunks/de-BytZwgn6.js → de-mMY5yQJu.js} +3 -2
  129. package/dist/{de-Ck0pBGHu.js → de-xzrh9PA3.js} +9 -0
  130. package/dist/editor-modal-oD1qNz_P.js +2773 -0
  131. package/dist/{en-BAMooiOF.js → en-8V-2kDcp.js} +1 -0
  132. package/dist/en-CM5fTFSo.js +2 -0
  133. package/dist/{cdn/chunks/en-DyOSV0lY.js → en-ChVeGQBY.js} +3 -2
  134. package/dist/{es-lagTvUfX.js → es-BT4LSpE2.js} +9 -0
  135. package/dist/{es-I47lHs2m.js → es-Cmn1rT3l.js} +1 -0
  136. package/dist/{fr-B8MIKjT4.js → fr-2yDa4zXM.js} +1 -0
  137. package/dist/{fr-C4hVl5Lk.js → fr-CI7OJoql.js} +9 -0
  138. package/dist/{nl-CBLxcb-0.js → nl-Bzk2zRI8.js} +1 -0
  139. package/dist/{nl-CReD7Apd.js → nl-D8y5OWGB.js} +9 -0
  140. package/dist/{pt-BR-C1WLSGDh.js → pt-BR-CoCwRUi0.js} +3 -0
  141. package/dist/{cdn/chunks/pt-BR-BRA0-vGP.js → pt-BR-DMCbUBvJ.js} +9 -2
  142. package/dist/{pt-BR-CMSRfX7t.js → pt-BR-DqtSCTUi.js} +1 -0
  143. package/dist/style.css +1 -1
  144. package/dist/templatical-editor.js +14 -14
  145. package/dist/{useEditorCore-C5APBjjV.js → useEditorCore-BUVbQgwb.js} +13 -13
  146. package/package.json +7 -7
  147. package/dist/DesignReferenceSidebar-QC4gEUWp.js +0 -273
  148. package/dist/SavedBlocksPickBar-cI7bDQQi.js +0 -52
  149. package/dist/cdn/chunks/AiChatSidebar-CjSEoK4x.js.map +0 -1
  150. package/dist/cdn/chunks/CloudPanels-bHhh9AUA.js.map +0 -1
  151. package/dist/cdn/chunks/CollaboratorBar-DgqcaGGI.js.map +0 -1
  152. package/dist/cdn/chunks/CommentsSidebar-C7oAP3Mx.js.map +0 -1
  153. package/dist/cdn/chunks/DesignReferenceSidebar-Bjo0M-AY.js +0 -223
  154. package/dist/cdn/chunks/DesignReferenceSidebar-Bjo0M-AY.js.map +0 -1
  155. package/dist/cdn/chunks/IssuesPanel-DdFk-Blf.js.map +0 -1
  156. package/dist/cdn/chunks/ParagraphEditor-DO4RzP8l.js.map +0 -1
  157. package/dist/cdn/chunks/SaveBlockDialog-LgxYrXyx.js.map +0 -1
  158. package/dist/cdn/chunks/SavedBlocksBrowserModal-CdeSsTpb.js.map +0 -1
  159. package/dist/cdn/chunks/SavedBlocksPickBar-Cl6PLwwV.js +0 -54
  160. package/dist/cdn/chunks/SavedBlocksPickBar-Cl6PLwwV.js.map +0 -1
  161. package/dist/cdn/chunks/TemplateScoringPanel--JxR6CsQ.js.map +0 -1
  162. package/dist/cdn/chunks/TestEmailModal-BAuDxJtD.js.map +0 -1
  163. package/dist/cdn/chunks/Toolbar-dxDYwN5E.js.map +0 -1
  164. package/dist/cdn/chunks/de-BytZwgn6.js.map +0 -1
  165. package/dist/cdn/chunks/editor-modal-C_xYN_66.js +0 -5908
  166. package/dist/cdn/chunks/editor-modal-C_xYN_66.js.map +0 -1
  167. package/dist/cdn/chunks/en-BwfjKb4M.js +0 -2
  168. package/dist/cdn/chunks/en-DyOSV0lY.js.map +0 -1
  169. package/dist/cdn/chunks/pt-BR-C1WLSGDh.js.map +0 -1
  170. package/dist/editor-modal-OPhP71YO.js +0 -5998
  171. package/dist/en-CjNopehs.js +0 -2
@@ -1,34 +1,31 @@
1
- import { B as e, E as t, F as n, H as r, N as i, Q as ee, R as a, S as o, at as s, b as c, ct as te, g as l, h as u, lt as d, m as f, n as p, p as m, u as h, ut as g, y as _ } from "./draggable-IB4x6w2O.js";
2
- import { D as ne, P as re, U as ie, Z as v, g as ae } from "./keys-dJbD1QPs.js";
3
- import { E as y, It as oe, Mt as b, O as x, dt as S, it as C, k as w, n as se, t as ce, u as le, wt as ue, z as T } from "./icons-BKkJ-5xF.js";
4
- import { t as E } from "./LoadingTrack-Ba5xh5HD.js";
5
- import { t as D } from "./_plugin-vue_export-helper-B3ysoDQm.js";
6
- import { n as O } from "./useCloudI18n-C09JcFS0.js";
1
+ import { B as e, E as t, F as n, H as r, N as i, Q as ee, R as a, S as o, at as s, b as c, ct as te, g as l, h as u, lt as d, m as f, n as ne, p, u as m, ut as h, y as g } from "./draggable-IB4x6w2O.js";
2
+ import { D as _, P as v, U as re, Z as y, g as b } from "./keys-dJbD1QPs.js";
3
+ import { E as x, It as S, Mt as C, O as w, dt as ie, it as ae, k as T, n as oe, t as se, u as ce, wt as le, z as E } from "./icons-BKkJ-5xF.js";
4
+ import { t as D } from "./LoadingTrack-_7DH_5aw.js";
5
+ import { t as O } from "./_plugin-vue_export-helper-B3ysoDQm.js";
6
+ import { n as k } from "./useCloudI18n-C09JcFS0.js";
7
7
  //#region src/cloud/utils/scoringStyles.ts
8
- function k(e) {
8
+ function A(e) {
9
9
  return e >= 80 ? "var(--tpl-success)" : e >= 60 ? "var(--tpl-warning)" : "var(--tpl-danger)";
10
10
  }
11
- function A(e) {
11
+ function j(e) {
12
12
  return e >= 80 ? "var(--tpl-success-light)" : e >= 60 ? "var(--tpl-warning-light)" : "var(--tpl-danger-light)";
13
13
  }
14
- function j(e) {
14
+ function M(e) {
15
15
  return e === "high" ? "var(--tpl-danger)" : e === "medium" ? "var(--tpl-warning)" : "var(--tpl-text-muted)";
16
16
  }
17
- function M(e) {
17
+ function N(e) {
18
18
  return e === "high" ? "var(--tpl-danger-light)" : e === "medium" ? "var(--tpl-warning-light)" : "var(--tpl-bg-hover)";
19
19
  }
20
20
  //#endregion
21
21
  //#region src/cloud/components/TemplateScoringPanel.vue?vue&type=script&setup=true&lang.ts
22
- var N = {
23
- key: 0,
24
- 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)]"
25
- }, P = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3" }, F = { class: "tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]" }, I = { class: "tpl:flex tpl:items-center tpl:gap-1" }, L = ["title"], R = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:p-4" }, z = {
22
+ var P = ["aria-label"], F = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3" }, I = { class: "tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]" }, L = { class: "tpl:flex tpl:items-center tpl:gap-1" }, R = ["aria-label", "title"], z = ["aria-label"], B = { class: "tpl:flex-1 tpl:overflow-y-auto tpl:p-4" }, V = {
26
23
  key: 0,
27
24
  class: "tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center"
28
- }, B = { class: "tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, V = {
25
+ }, H = { class: "tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, U = {
29
26
  key: 1,
30
27
  class: "tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center"
31
- }, H = { class: "tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, U = {
28
+ }, W = { class: "tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, ue = {
32
29
  key: 2,
33
30
  class: "tpl:flex tpl:flex-col tpl:gap-4"
34
31
  }, de = { class: "tpl:text-xs tpl:font-medium tpl:uppercase tpl:tracking-wider tpl:text-[var(--tpl-text-muted)]" }, fe = {
@@ -37,39 +34,39 @@ var N = {
37
34
  }, pe = {
38
35
  key: 0,
39
36
  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)]"
40
- }, W = ["onClick"], me = { class: "tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]" }, he = {
37
+ }, me = ["onClick"], he = { class: "tpl:flex-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]" }, ge = {
41
38
  key: 0,
42
39
  class: "tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]"
43
- }, ge = {
40
+ }, _e = {
44
41
  key: 0,
45
42
  class: "tpl:border-t tpl:border-[var(--tpl-border)]"
46
- }, _e = {
43
+ }, ve = {
47
44
  key: 0,
48
45
  class: "tpl:px-3 tpl:py-3 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]"
49
- }, ve = { class: "tpl:flex tpl:items-start tpl:gap-2" }, ye = { class: "tpl:flex-1 tpl:min-w-0" }, be = { class: "tpl:flex tpl:items-start tpl:gap-1.5" }, xe = { class: "tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]" }, Se = {
46
+ }, ye = { class: "tpl:flex tpl:items-start tpl:gap-2" }, be = { class: "tpl:flex-1 tpl:min-w-0" }, xe = { class: "tpl:flex tpl:items-start tpl:gap-1.5" }, Se = { class: "tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]" }, Ce = {
50
47
  key: 0,
51
- class: "tpl:mt-1 tpl:text-[11px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]"
52
- }, Ce = {
48
+ class: "tpl:mt-1 tpl:text-[10px] tpl:leading-snug tpl:text-[var(--tpl-text-dim)]"
49
+ }, we = {
53
50
  key: 1,
54
51
  class: "tpl:mt-2 tpl:flex tpl:justify-center"
55
- }, we = ["disabled", "onClick"], Te = {
52
+ }, Te = ["disabled", "onClick"], Ee = {
56
53
  key: 0,
57
- class: "tpl:mt-1.5 tpl:text-[11px] tpl:text-[var(--tpl-danger)]"
58
- }, Ee = {
54
+ class: "tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]"
55
+ }, De = {
59
56
  key: 3,
60
57
  class: "tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center"
61
- }, De = { class: "tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Oe = { class: "tpl:m-0 tpl:px-4 tpl:pb-2 tpl:pt-2 tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]" }, G = /*#__PURE__*/ D(/* @__PURE__ */ o({
58
+ }, Oe = { class: "tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, ke = { 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)]" }, G = /*#__PURE__*/ O(/* @__PURE__ */ o({
62
59
  __name: "TemplateScoringPanel",
63
60
  props: { visible: { type: Boolean } },
64
61
  emits: ["close"],
65
- setup(o, { emit: D }) {
66
- let G = o, ke = D, { t: K } = O(), q = v(ae, "TemplateScoringPanel"), J = v(ie, "TemplateScoringPanel"), Y = t(ne, re), X = ee({
62
+ setup(o, { emit: O }) {
63
+ let G = o, Ae = O, { t: K } = k(), q = y(b, "TemplateScoringPanel"), J = y(re, "TemplateScoringPanel"), Y = t(_, v), X = ee({
67
64
  spam: !0,
68
65
  readability: !0,
69
66
  accessibility: !0,
70
67
  bestPractices: !0
71
68
  });
72
- function Ae(e) {
69
+ function je(e) {
73
70
  X.value[e] = !X.value[e];
74
71
  }
75
72
  let Z = [
@@ -77,11 +74,11 @@ var N = {
77
74
  "readability",
78
75
  "accessibility",
79
76
  "bestPractices"
80
- ], je = {
81
- spam: x,
82
- readability: ue,
83
- accessibility: y,
84
- bestPractices: ce
77
+ ], Me = {
78
+ spam: w,
79
+ readability: le,
80
+ accessibility: x,
81
+ bestPractices: se
85
82
  };
86
83
  function Q() {
87
84
  J.score(q.content.value, Y.value);
@@ -89,7 +86,7 @@ var N = {
89
86
  e(() => G.visible, (e) => {
90
87
  e && !J.scoringResult.value && !J.isScoring.value && Q();
91
88
  });
92
- async function Me(e) {
89
+ async function Ne(e) {
93
90
  if (!e.blockId) return;
94
91
  let t = q.content.value.blocks.find((t) => t.id === e.blockId);
95
92
  if (!t) return;
@@ -101,7 +98,7 @@ var N = {
101
98
  function $() {
102
99
  return J.scoringResult.value ? Z.reduce((e, t) => e + (J.scoringResult.value?.categories[t]?.findings.length ?? 0), 0) : 0;
103
100
  }
104
- return (e, t) => (i(), f(p, {
101
+ return (e, t) => (i(), f(ne, {
105
102
  "enter-active-class": "tpl-scoring-slide-enter-active",
106
103
  "enter-from-class": "tpl:translate-x-full",
107
104
  "enter-to-class": "tpl:translate-x-0",
@@ -109,123 +106,132 @@ var N = {
109
106
  "leave-from-class": "tpl:translate-x-0",
110
107
  "leave-to-class": "tpl:translate-x-full"
111
108
  }, {
112
- default: r(() => [o.visible ? (i(), l("div", N, [m("div", P, [m("div", F, [c(s(w), {
109
+ default: r(() => [o.visible ? (i(), l("div", {
110
+ key: 0,
111
+ role: "complementary",
112
+ "aria-label": s(K).scoring.title,
113
+ 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)]"
114
+ }, [p("div", F, [p("div", I, [c(s(T), {
113
115
  size: 13,
114
116
  "stroke-width": 2
115
- }), m("span", null, g(s(K).scoring.title), 1)]), m("div", I, [s(J).scoringResult.value && !s(J).isScoring.value ? (i(), l("button", {
117
+ }), p("span", null, h(s(K).scoring.title), 1)]), p("div", L, [s(J).scoringResult.value && !s(J).isScoring.value ? (i(), l("button", {
116
118
  key: 0,
119
+ type: "button",
117
120
  class: "tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]",
121
+ "aria-label": s(K).scoring.rescore,
118
122
  title: s(K).scoring.rescore,
119
123
  onClick: t[0] ||= (e) => Q()
120
- }, [c(s(T), {
124
+ }, [c(s(E), {
121
125
  size: 14,
122
126
  "stroke-width": 2
123
- })], 8, L)) : u("", !0), m("button", {
127
+ })], 8, R)) : u("", !0), p("button", {
128
+ type: "button",
124
129
  class: "tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]",
125
- onClick: t[1] ||= (e) => ke("close")
126
- }, [c(s(se), {
130
+ "aria-label": s(K).scoring.close,
131
+ onClick: t[1] ||= (e) => Ae("close")
132
+ }, [c(s(oe), {
127
133
  size: 14,
128
134
  "stroke-width": 2
129
- })])])]), m("div", R, [s(J).isScoring.value ? (i(), l("div", z, [m("p", B, g(s(K).scoring.scoring), 1), c(E, { class: "tpl:w-3/4" })])) : s(J).error.value && !s(J).scoringResult.value ? (i(), l("div", V, [
130
- c(s(b), {
135
+ })], 8, z)])]), p("div", B, [s(J).isScoring.value ? (i(), l("div", V, [p("p", H, h(s(K).scoring.scoring), 1), c(D, { class: "tpl:w-3/4" })])) : s(J).error.value && !s(J).scoringResult.value ? (i(), l("div", U, [
136
+ c(s(C), {
131
137
  size: 32,
132
138
  "stroke-width": 1.5,
133
139
  class: "tpl:text-[var(--tpl-danger)]"
134
140
  }),
135
- m("p", H, g(s(K).scoring.error), 1),
136
- m("button", {
141
+ p("p", W, h(s(K).scoring.error), 1),
142
+ p("button", {
137
143
  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)]",
138
144
  onClick: t[2] ||= (e) => Q()
139
- }, [c(s(T), {
145
+ }, [c(s(E), {
140
146
  size: 12,
141
147
  "stroke-width": 2
142
- }), _(" " + g(s(K).scoring.rescore), 1)])
143
- ])) : s(J).scoringResult.value ? (i(), l("div", U, [
144
- m("div", {
148
+ }), g(" " + h(s(K).scoring.rescore), 1)])
149
+ ])) : s(J).scoringResult.value ? (i(), l("div", ue, [
150
+ p("div", {
145
151
  class: "tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:p-5",
146
- style: d({ backgroundColor: s(A)(s(J).scoringResult.value.score) })
152
+ style: d({ backgroundColor: s(j)(s(J).scoringResult.value.score) })
147
153
  }, [
148
- m("span", {
154
+ p("span", {
149
155
  class: "tpl:text-4xl tpl:font-bold tpl:tabular-nums",
150
- style: d({ color: s(k)(s(J).scoringResult.value.score) })
151
- }, g(s(J).scoringResult.value.score), 5),
152
- m("span", de, g(s(K).scoring.overallScore), 1),
153
- $() > 0 ? (i(), l("span", fe, g($()) + " " + g(s(K).scoring.findings), 1)) : u("", !0)
156
+ style: d({ color: s(A)(s(J).scoringResult.value.score) })
157
+ }, h(s(J).scoringResult.value.score), 5),
158
+ p("span", de, h(s(K).scoring.overallScore), 1),
159
+ $() > 0 ? (i(), l("span", fe, h($()) + " " + h(s(K).scoring.findings), 1)) : u("", !0)
154
160
  ], 4),
155
- s(J).fixError.value ? (i(), l("div", pe, [c(s(b), {
161
+ s(J).fixError.value ? (i(), l("div", pe, [c(s(C), {
156
162
  size: 14,
157
163
  "stroke-width": 2,
158
164
  class: "tpl:mt-0.5 tpl:shrink-0"
159
- }), m("span", null, g(s(K).scoring.fixError), 1)])) : u("", !0),
160
- (i(), l(h, null, n(Z, (e) => m("div", {
165
+ }), p("span", null, h(s(K).scoring.fixError), 1)])) : u("", !0),
166
+ (i(), l(m, null, n(Z, (e) => p("div", {
161
167
  key: e,
162
168
  class: "tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)]"
163
- }, [m("button", {
169
+ }, [p("button", {
164
170
  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)]",
165
- onClick: (t) => Ae(e)
171
+ onClick: (t) => je(e)
166
172
  }, [
167
- (i(), f(a(je[e]), {
173
+ (i(), f(a(Me[e]), {
168
174
  size: 14,
169
175
  "stroke-width": 2,
170
- style: d({ color: s(k)(s(J).scoringResult.value.categories[e].score) })
176
+ style: d({ color: s(A)(s(J).scoringResult.value.categories[e].score) })
171
177
  }, null, 8, ["style"])),
172
- m("span", me, g(s(K).scoring.categories[e]), 1),
173
- m("span", {
178
+ p("span", he, h(s(K).scoring.categories[e]), 1),
179
+ p("span", {
174
180
  class: "tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-semibold tpl:tabular-nums",
175
181
  style: d({
176
- color: s(k)(s(J).scoringResult.value.categories[e].score),
177
- backgroundColor: s(A)(s(J).scoringResult.value.categories[e].score)
182
+ color: s(A)(s(J).scoringResult.value.categories[e].score),
183
+ backgroundColor: s(j)(s(J).scoringResult.value.categories[e].score)
178
184
  })
179
- }, g(s(J).scoringResult.value.categories[e].score), 5),
180
- s(J).scoringResult.value.categories[e].findings.length > 0 ? (i(), l("span", he, g(s(J).scoringResult.value.categories[e].findings.length), 1)) : u("", !0),
181
- c(s(oe), {
185
+ }, h(s(J).scoringResult.value.categories[e].score), 5),
186
+ s(J).scoringResult.value.categories[e].findings.length > 0 ? (i(), l("span", ge, h(s(J).scoringResult.value.categories[e].findings.length), 1)) : u("", !0),
187
+ c(s(S), {
182
188
  size: 12,
183
189
  "stroke-width": 2,
184
190
  class: te(["tpl:transition-transform tpl:duration-200 tpl:text-[var(--tpl-text-dim)]", X.value[e] ? "tpl:rotate-0" : "tpl:-rotate-90"])
185
191
  }, null, 8, ["class"])
186
- ], 8, W), X.value[e] ? (i(), l("div", ge, [s(J).scoringResult.value.categories[e].findings.length === 0 ? (i(), l("div", _e, g(s(K).scoring.noFindings), 1)) : u("", !0), (i(!0), l(h, null, n(s(J).scoringResult.value.categories[e].findings, (e) => (i(), l("div", {
192
+ ], 8, me), X.value[e] ? (i(), l("div", _e, [s(J).scoringResult.value.categories[e].findings.length === 0 ? (i(), l("div", ve, h(s(K).scoring.noFindings), 1)) : u("", !0), (i(!0), l(m, null, n(s(J).scoringResult.value.categories[e].findings, (e) => (i(), l("div", {
187
193
  key: e.id,
188
194
  class: "tpl:border-t tpl:px-3 tpl:py-2.5 tpl:first:border-t-0 tpl:border-[var(--tpl-border-light)]"
189
- }, [m("div", ve, [(i(), f(a(s(e.severity === "high" ? b : e.severity === "medium" ? le : S)), {
195
+ }, [p("div", ye, [(i(), f(a(s(e.severity === "high" ? C : e.severity === "medium" ? ce : ie)), {
190
196
  size: 13,
191
197
  "stroke-width": 2,
192
198
  class: "tpl:mt-0.5 tpl:shrink-0",
193
- style: d({ color: s(j)(e.severity) })
194
- }, null, 8, ["style"])), m("div", ye, [
195
- m("div", be, [m("span", {
199
+ style: d({ color: s(M)(e.severity) })
200
+ }, null, 8, ["style"])), p("div", be, [
201
+ p("div", xe, [p("span", {
196
202
  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",
197
203
  style: d({
198
- color: s(j)(e.severity),
199
- backgroundColor: s(M)(e.severity)
204
+ color: s(M)(e.severity),
205
+ backgroundColor: s(N)(e.severity)
200
206
  })
201
- }, g(s(K).scoring.severity[e.severity]), 5), m("span", xe, g(e.message), 1)]),
202
- e.suggestion ? (i(), l("p", Se, g(e.suggestion), 1)) : u("", !0),
203
- e.blockId ? (i(), l("div", Ce, [m("button", {
204
- 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-[11px] tpl:font-medium tpl:transition-all tpl:disabled:opacity-50 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)]",
207
+ }, h(s(K).scoring.severity[e.severity]), 5), p("span", Se, h(e.message), 1)]),
208
+ e.suggestion ? (i(), l("p", Ce, h(e.suggestion), 1)) : u("", !0),
209
+ e.blockId ? (i(), l("div", we, [p("button", {
210
+ 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)]",
205
211
  style: { "background-color": "transparent" },
206
212
  disabled: s(J).fixingFindingId.value !== null,
207
- onClick: (t) => Me(e)
208
- }, [s(J).fixingFindingId.value === e.id ? (i(), f(s(C), {
213
+ onClick: (t) => Ne(e)
214
+ }, [s(J).fixingFindingId.value === e.id ? (i(), f(s(ae), {
209
215
  key: 0,
210
216
  class: "tpl-spinner",
211
217
  size: 11,
212
218
  "stroke-width": 2
213
- })) : (i(), f(s(y), {
219
+ })) : (i(), f(s(x), {
214
220
  key: 1,
215
221
  size: 11,
216
222
  "stroke-width": 2
217
- })), _(" " + g(s(J).fixingFindingId.value === e.id ? s(K).scoring.fixing : s(K).scoring.fix), 1)], 8, we), s(J).fixError.value ? (i(), l("p", Te, g(s(J).fixError.value), 1)) : u("", !0)])) : u("", !0)
223
+ })), g(" " + h(s(J).fixingFindingId.value === e.id ? s(K).scoring.fixing : s(K).scoring.fix), 1)], 8, Te), s(J).fixError.value ? (i(), l("p", Ee, h(s(J).fixError.value), 1)) : u("", !0)])) : u("", !0)
218
224
  ])])]))), 128))])) : u("", !0)])), 64))
219
- ])) : (i(), l("div", Ee, [c(s(w), {
225
+ ])) : (i(), l("div", De, [c(s(T), {
220
226
  size: 32,
221
227
  "stroke-width": 1.5,
222
228
  class: "tpl:text-[var(--tpl-text-dim)]"
223
- }), m("p", De, g(s(K).scoring.emptyState), 1)])), m("p", Oe, g(s(K).aiMenu.disclaimer), 1)])])) : u("", !0)]),
229
+ }), p("p", Oe, h(s(K).scoring.emptyState), 1)])), p("p", ke, h(s(K).aiMenu.disclaimer), 1)])], 8, P)) : u("", !0)]),
224
230
  _: 1
225
231
  }));
226
232
  }
227
- }), [["__scopeId", "data-v-e9458def"]]);
233
+ }), [["__scopeId", "data-v-165e948a"]]);
228
234
  //#endregion
229
235
  export { G as default };
230
236
 
231
- //# sourceMappingURL=TemplateScoringPanel--JxR6CsQ.js.map
237
+ //# sourceMappingURL=TemplateScoringPanel-CR6RG6dZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateScoringPanel-CR6RG6dZ.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,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
2
  import { u as ne } from "./src-Dwgq7hf4.js";
3
- import { N as re } from "./useEditorCore-BHmIp1hW.js";
3
+ import { N as re } from "./useEditorCore-CXYI14ux.js";
4
4
  import { D as ie, P as y, V as ae, g as oe, j as se } from "./keys-dJbD1QPs.js";
5
5
  import { t as ce } from "./useI18n-DTZ9sIcv.js";
6
6
  import { Lt as le, it as ue } from "./icons-BKkJ-5xF.js";
7
7
  import { v as de } from "./styleConstants-C_YAPdQS.js";
8
8
  import { n as fe, t as pe } from "./MergeTagModeToggle-n3nvSNeQ.js";
9
- import { t as me } from "./TplModal-DuwS2ifK.js";
10
- import { t as he } from "./BlockPreviewCanvas-lQrAdgG3.js";
9
+ import { t as me } from "./TplModal-BKqDFLq3.js";
10
+ import { t as he } from "./BlockPreviewCanvas-DeS7Sa--.js";
11
11
  //#region src/utils/validateEmailShape.ts
12
12
  var b = /[,;<>()[\]\\"'`|/?#@!$%^&*+=~{}]/;
13
13
  function x(e) {
@@ -127,14 +127,14 @@ var S = ["aria-busy"], C = {
127
127
  value: H.value,
128
128
  disabled: "",
129
129
  "data-testid": "test-email-recipient",
130
- class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
130
+ class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
131
131
  }, null, 8, E)) : J.value ? s((i(), f("select", {
132
132
  key: 1,
133
133
  id: "tpl-test-email-recipient",
134
134
  "onUpdate:modelValue": t[0] ||= (e) => H.value = e,
135
135
  disabled: o.isSending || o.justSent,
136
136
  "data-testid": "test-email-recipient",
137
- class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
137
+ class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
138
138
  }, [(i(!0), f(g, null, n(o.allowedRecipients, (e) => (i(), f("option", {
139
139
  key: e,
140
140
  value: e
@@ -148,7 +148,7 @@ var S = ["aria-busy"], C = {
148
148
  placeholder: c(V).testEmail.recipientPlaceholder,
149
149
  disabled: o.isSending || o.justSent,
150
150
  "data-testid": "test-email-recipient",
151
- class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
151
+ class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]"
152
152
  }, null, 8, k)), [[te, H.value]]),
153
153
  Oe.value ? (i(), f("p", A, _(c(V).testEmail.invalidAddress), 1)) : p("", !0)
154
154
  ]),
@@ -203,4 +203,4 @@ var S = ["aria-busy"], C = {
203
203
  //#endregion
204
204
  export { z as default };
205
205
 
206
- //# sourceMappingURL=TestEmailModal-BAuDxJtD.js.map
206
+ //# sourceMappingURL=TestEmailModal-DId9Og-N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestEmailModal-DId9Og-N.js","names":[],"sources":["../../../src/utils/validateEmailShape.ts","../../../src/components/TestEmailModal.vue","../../../src/components/TestEmailModal.vue"],"sourcesContent":["/**\n * Characters that cannot appear in a domain but routinely arrive attached to one.\n *\n * A **denylist**, not an allowlist, so unicode domains keep working — an\n * allowlist of `[a-z0-9.-]` would be the stricter, more obviously \"correct\"\n * choice and would reject them. What this actually targets is the punctuation\n * that comes from pasting out of a list or a mail client: `user@example.com,`\n * and `<user@example.com>`.\n */\nconst IMPOSSIBLE_DOMAIN_CHARS = /[,;<>()[\\]\\\\\"'`|/?#@!$%^&*+=~{}]/;\n\n/**\n * Does this string look like an email address?\n *\n * Deliberately loose: non-empty, no whitespace, exactly one `@`, a non-empty\n * local part, and a domain containing a dot with something either side and no\n * impossible characters. That is enough to catch the mistakes a typo actually\n * produces — a missing `@`, a trailing comma, a half-typed domain — before the\n * user pays for a round-trip.\n *\n * **Do not \"improve\" this into an RFC-ish regex.** Those reliably reject valid\n * addresses (plus-addressing, long new gTLDs, unicode domains) and the resulting\n * bug reports cost far more than the malformed addresses they catch. Only the\n * receiving mail server can really tell, and the sending backend has to validate\n * anyway — this is a fast-feedback affordance, not a gate. `validateEmailShape`\n * has its own test file pinning the addresses that must keep passing.\n */\nexport function looksLikeEmail(value: string): boolean {\n const trimmed = value.trim();\n if (trimmed.length === 0) return false;\n if (/\\s/.test(trimmed)) return false;\n\n const parts = trimmed.split(\"@\");\n if (parts.length !== 2) return false;\n\n const [local, domain] = parts;\n if (local.length === 0) return false;\n if (IMPOSSIBLE_DOMAIN_CHARS.test(domain)) return false;\n\n // A dot with at least one character either side, so `a@b` and `a@b.` fail but\n // `a@b.c` and `a@sub.example.museum` pass.\n const dot = domain.indexOf(\".\");\n return dot > 0 && dot < domain.length - 1;\n}\n","<script setup lang=\"ts\">\nimport { primaryBtnClass } from \"../constants/styleConstants\";\n/**\n * Send-a-test dialog, shared by the OSS and Cloud editors.\n *\n * The recipient control has three shapes, driven entirely by whether the\n * provider configured an allowlist:\n *\n * - **no allowlist** — free text, validated for shape only. This branch exists\n * for BYO senders; Cloud never takes it, because its list comes from a signed\n * JWT.\n * - **exactly one** — a read-only field. Preserved verbatim from the Cloud-only\n * version; there is nothing to choose, so offering an editable field would\n * imply otherwise.\n * - **several** — a picker of exactly those addresses.\n *\n * An empty allowlist never reaches here: the feature reports itself unavailable,\n * so no trigger renders. (The Cloud-only version fell through to `v-else` and\n * rendered a select with no options and a permanently disabled Send.)\n */\nimport TplModal from \"./TplModal.vue\";\n// Statically imported: this modal is already lazy-loaded as a whole, so the\n// preview rides its chunk rather than costing a second round-trip and flashing\n// an empty frame. Still shared with the saved-blocks surfaces, which lazy-load\n// it themselves.\nimport BlockPreviewCanvas from \"./BlockPreviewCanvas.vue\";\n// The editor header's own control, reused verbatim so the two viewport switches\n// are the same component rather than two things that resemble each other.\nimport ViewportToggle from \"./ViewportToggle.vue\";\nimport MergeTagModeToggle from \"./MergeTagModeToggle.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { looksLikeEmail } from \"../utils/validateEmailShape\";\nimport { Check, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref, watch } from \"vue\";\nimport { hasMergeTagSamples, type ViewportSize } from \"@templatical/types\";\nimport {\n EDITOR_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n MERGE_TAG_SAMPLE_MODE_KEY,\n RESOLVE_PREVIEW_KEY,\n} from \"../keys\";\nimport { usePreviewResolution } from \"../composables/usePreviewResolution\";\nimport type { TestEmailError } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n visible: boolean;\n /** Omitted / undefined means unrestricted, so the field accepts free text. */\n allowedRecipients?: string[];\n defaultRecipient?: string;\n isSending: boolean;\n justSent: boolean;\n error: TestEmailError | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"send\", recipient: string): void;\n (e: \"close\"): void;\n}>();\n\nconst { t } = useI18n();\nconst editor = inject(EDITOR_KEY, null);\n\nconst recipient = ref(\"\");\n\nconst previewViewport = ref<ViewportSize>(\"desktop\");\n\n/**\n * Its own resolution instance rather than the editor's, because this dialog has\n * a recipient and the editor's preview does not — resolving for \"whoever is\n * selected here\" is a different question from resolving for the canvas.\n * `isActive` is the dialog's own visibility.\n */\nconst resolvePreviewHook = inject(RESOLVE_PREVIEW_KEY, undefined);\nconst previewResolution = usePreviewResolution({\n resolvePreview: resolvePreviewHook ?? undefined,\n getContent: () =>\n editor?.content.value ?? { blocks: [], settings: {} as never },\n isActive: () => props.visible,\n getRecipient: () => recipient.value || undefined,\n});\n\n/**\n * Shared with the editor's own preview, so switching here and switching there\n * are the same choice — it is a property of how the user wants to read a\n * preview, not of one dialog. Falls back to a local ref for headless mounts.\n */\nconst sharedSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\n// Seeded from availability, matching how `useEditorCore` seeds the shared ref.\n// A blanket `true` would make the dialog claim tags \"show example values\" for a\n// consumer who configured none — false, and with no toggle rendered to correct\n// it, since the toggle hides itself when nothing has a sample.\nconst localSampleMode = ref(\n hasMergeTagSamples(inject(MERGE_TAGS_KEY, NO_MERGE_TAGS).value),\n);\nconst sampleMode = computed({\n get: () =>\n // Off entirely while a resolver is configured — same rule as the canvas.\n previewResolution.isConfigured\n ? false\n : (sharedSampleMode?.value ?? localSampleMode.value),\n set: (on: boolean) => {\n if (sharedSampleMode) sharedSampleMode.value = on;\n else localSampleMode.value = on;\n },\n});\n\nconst previewBlocks = computed(\n () => previewResolution.content.value.blocks ?? [],\n);\n\nconst hasAllowlist = computed(\n () =>\n props.allowedRecipients !== undefined && props.allowedRecipients.length > 0,\n);\nconst isSingleRecipient = computed(() => props.allowedRecipients?.length === 1);\n\n// Mounted behind a `v-if` on the same state that drives `visible`, so it can\n// mount with `visible` already true — without `immediate` the field would never\n// be seeded.\nwatch(\n () => props.visible,\n (visible) => {\n if (!visible) return;\n recipient.value =\n props.defaultRecipient ?? props.allowedRecipients?.[0] ?? \"\";\n },\n { immediate: true },\n);\n\n/** Shape-check free text only; a picker can't produce a malformed value. */\nconst isRecipientValid = computed(() =>\n hasAllowlist.value\n ? recipient.value.length > 0\n : looksLikeEmail(recipient.value),\n);\n\nconst canSend = computed(\n () => isRecipientValid.value && !props.isSending && !props.justSent,\n);\n\n/** Free text that's been typed into but isn't yet a plausible address. */\nconst showShapeHint = computed(\n () =>\n !hasAllowlist.value &&\n recipient.value.trim().length > 0 &&\n !isRecipientValid.value,\n);\n\nconst errorText = computed(() => {\n if (!props.error) return null;\n return props.error.kind === \"provider\"\n ? props.error.message\n : t.testEmail.recipientNotAllowed;\n});\n\nfunction handleSend(): void {\n if (!canSend.value) return;\n emit(\"send\", recipient.value.trim());\n}\n\nfunction handleClose(): void {\n // Sending is the one state that blocks dismissal; the success state closes\n // immediately rather than making the user wait out the timer.\n if (props.isSending) return;\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSend();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Grows to `max-w-3xl` only while the preview is open. A 600px email has\n to render at true size AND leave slack on both sides for the band of\n body background the preview draws beside it: at `max-w-2xl` the\n preview region's content box is exactly 600px, so the band would have\n nowhere to go and `settings.backgroundColor` would look unset for any\n template whose sections span the full width. Collapsed, it stays the\n compact form it was. `w-full` keeps it shrinking on narrow screens.\n `max-h-[90%]`\n with a `min-h-0 overflow-y-auto` preview region is the pattern the\n saved-blocks dialogs use: the cap bounds the panel to TplModal's\n backdrop (never to `vh` — see the note there), and `min-h-0` is what\n lets a flex child shrink below its content and actually scroll. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSending\"\n aria-labelledby=\"tpl-test-email-title\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-3xl tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-test-email-title\"\n class=\"tpl:mb-4 tpl:shrink-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.testEmail.title }}\n </h3>\n\n <!-- Recipient -->\n <div class=\"tpl:mb-3 tpl:shrink-0\">\n <label\n for=\"tpl-test-email-recipient\"\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.recipientLabel }}\n </label>\n\n <!-- Exactly one allowed address: nothing to choose. -->\n <input\n v-if=\"isSingleRecipient\"\n id=\"tpl-test-email-recipient\"\n type=\"text\"\n :value=\"recipient\"\n disabled\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <!-- Several allowed addresses: pick one. -->\n <select\n v-else-if=\"hasAllowlist\"\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"email in allowedRecipients\"\n :key=\"email\"\n :value=\"email\"\n >\n {{ email }}\n </option>\n </select>\n\n <!-- No allowlist: free text. -->\n <input\n v-else\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n type=\"email\"\n autocomplete=\"email\"\n spellcheck=\"false\"\n :placeholder=\"t.testEmail.recipientPlaceholder\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <p\n v-if=\"showShapeHint\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.testEmail.invalidAddress }}\n </p>\n </div>\n\n <!-- Preview header: a label plus the viewport switch. Always shown — the\n preview is part of the dialog rather than something to opt into. -->\n <div class=\"tpl:mb-2 tpl:shrink-0\">\n <!-- Two segmented controls: `flex-wrap` so they drop to their own row\n rather than crushing the label on a narrow viewport. -->\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.preview }}\n </span>\n\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <MergeTagModeToggle\n v-if=\"!previewResolution.supersedesSamples.value\"\n :sample-mode=\"sampleMode\"\n @change=\"sampleMode = $event\"\n />\n\n <!-- Without this the preview would silently show only the desktop\n variant of any responsive block. -->\n <ViewportToggle\n :viewport=\"previewViewport\"\n @change=\"previewViewport = $event\"\n />\n </div>\n </div>\n\n <!-- The hint has to follow the mode: \"shown unresolved\" is simply false\n once every tag renders a realistic value. -->\n <p\n data-testid=\"test-email-preview-hint\"\n class=\"tpl:mt-1 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n previewResolution.supersedesSamples.value\n ? t.previewResolution.hint\n : sampleMode\n ? t.testEmail.previewHintSample\n : t.testEmail.previewHint\n }}\n </p>\n </div>\n\n <!-- The one region allowed to grow: `flex-1 min-h-0` so it absorbs the\n remaining height and scrolls, leaving the form and actions fixed. -->\n <div\n id=\"tpl-test-email-preview\"\n data-testid=\"test-email-preview\"\n class=\"tpl:mb-3 tpl:min-h-0 tpl:flex-1 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-sm)] tpl:p-3 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <!-- Skeleton while a *first* resolve is outstanding, so the dialog never\n flashes the unresolved template before the resolved one. Sized like\n the frame it replaces to avoid a layout jump. A re-resolve (recipient\n change) keeps the previous result instead — `isInitialResolve`. -->\n <div\n v-if=\"previewResolution.isInitialResolve.value\"\n role=\"status\"\n aria-busy=\"true\"\n data-testid=\"preview-resolution-loading\"\n class=\"tpl:flex tpl:flex-col tpl:gap-3\"\n >\n <span class=\"tpl:sr-only\">{{ t.previewResolution.resolving }}</span>\n <div\n v-for=\"n in 3\"\n :key=\"n\"\n aria-hidden=\"true\"\n class=\"tpl-pulse tpl:h-16 tpl:rounded-md tpl:bg-[var(--tpl-bg-hover)]\"\n ></div>\n </div>\n\n <!-- Resolution failed: the unresolved template renders below, so the\n dialog degrades rather than showing an empty frame. -->\n <p\n v-else-if=\"previewResolution.hasFailed.value\"\n role=\"status\"\n data-testid=\"preview-resolution-failed\"\n class=\"tpl:mb-2 tpl:rounded-md tpl:px-3 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg-hover)]\"\n >\n {{ t.previewResolution.failed }}\n </p>\n\n <BlockPreviewCanvas\n v-if=\"!previewResolution.isInitialResolve.value\"\n :blocks=\"previewBlocks\"\n :viewport=\"previewViewport\"\n :apply-condition-filter=\"!previewResolution.isConfigured\"\n />\n </div>\n\n <!-- Success. Announced politely; the dialog closes itself shortly after. -->\n <p\n v-if=\"justSent\"\n role=\"status\"\n data-testid=\"test-email-success\"\n class=\"tpl:mb-3 tpl:flex tpl:shrink-0 tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success,var(--tpl-primary))]\"\n >\n <Check :size=\"13\" :stroke-width=\"2.5\" />\n {{ t.testEmail.success }}\n </p>\n\n <!-- Failure. Stays open so the user can retry. -->\n <p\n v-else-if=\"errorText\"\n role=\"alert\"\n data-testid=\"test-email-error\"\n class=\"tpl:mb-3 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ errorText }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:shrink-0 tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"test-email-cancel\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSending\"\n :class=\"{ 'tpl:cursor-not-allowed tpl:opacity-50': isSending }\"\n @click=\"handleClose\"\n >\n {{ t.testEmail.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"test-email-send\"\n :class=\"primaryBtnClass\"\n :disabled=\"!canSend\"\n @click=\"handleSend\"\n >\n <span v-if=\"isSending\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.testEmail.sending }}\n </span>\n <span v-else>\n {{ t.testEmail.send }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport { primaryBtnClass } from \"../constants/styleConstants\";\n/**\n * Send-a-test dialog, shared by the OSS and Cloud editors.\n *\n * The recipient control has three shapes, driven entirely by whether the\n * provider configured an allowlist:\n *\n * - **no allowlist** — free text, validated for shape only. This branch exists\n * for BYO senders; Cloud never takes it, because its list comes from a signed\n * JWT.\n * - **exactly one** — a read-only field. Preserved verbatim from the Cloud-only\n * version; there is nothing to choose, so offering an editable field would\n * imply otherwise.\n * - **several** — a picker of exactly those addresses.\n *\n * An empty allowlist never reaches here: the feature reports itself unavailable,\n * so no trigger renders. (The Cloud-only version fell through to `v-else` and\n * rendered a select with no options and a permanently disabled Send.)\n */\nimport TplModal from \"./TplModal.vue\";\n// Statically imported: this modal is already lazy-loaded as a whole, so the\n// preview rides its chunk rather than costing a second round-trip and flashing\n// an empty frame. Still shared with the saved-blocks surfaces, which lazy-load\n// it themselves.\nimport BlockPreviewCanvas from \"./BlockPreviewCanvas.vue\";\n// The editor header's own control, reused verbatim so the two viewport switches\n// are the same component rather than two things that resemble each other.\nimport ViewportToggle from \"./ViewportToggle.vue\";\nimport MergeTagModeToggle from \"./MergeTagModeToggle.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { looksLikeEmail } from \"../utils/validateEmailShape\";\nimport { Check, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref, watch } from \"vue\";\nimport { hasMergeTagSamples, type ViewportSize } from \"@templatical/types\";\nimport {\n EDITOR_KEY,\n MERGE_TAGS_KEY,\n NO_MERGE_TAGS,\n MERGE_TAG_SAMPLE_MODE_KEY,\n RESOLVE_PREVIEW_KEY,\n} from \"../keys\";\nimport { usePreviewResolution } from \"../composables/usePreviewResolution\";\nimport type { TestEmailError } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n visible: boolean;\n /** Omitted / undefined means unrestricted, so the field accepts free text. */\n allowedRecipients?: string[];\n defaultRecipient?: string;\n isSending: boolean;\n justSent: boolean;\n error: TestEmailError | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"send\", recipient: string): void;\n (e: \"close\"): void;\n}>();\n\nconst { t } = useI18n();\nconst editor = inject(EDITOR_KEY, null);\n\nconst recipient = ref(\"\");\n\nconst previewViewport = ref<ViewportSize>(\"desktop\");\n\n/**\n * Its own resolution instance rather than the editor's, because this dialog has\n * a recipient and the editor's preview does not — resolving for \"whoever is\n * selected here\" is a different question from resolving for the canvas.\n * `isActive` is the dialog's own visibility.\n */\nconst resolvePreviewHook = inject(RESOLVE_PREVIEW_KEY, undefined);\nconst previewResolution = usePreviewResolution({\n resolvePreview: resolvePreviewHook ?? undefined,\n getContent: () =>\n editor?.content.value ?? { blocks: [], settings: {} as never },\n isActive: () => props.visible,\n getRecipient: () => recipient.value || undefined,\n});\n\n/**\n * Shared with the editor's own preview, so switching here and switching there\n * are the same choice — it is a property of how the user wants to read a\n * preview, not of one dialog. Falls back to a local ref for headless mounts.\n */\nconst sharedSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\n// Seeded from availability, matching how `useEditorCore` seeds the shared ref.\n// A blanket `true` would make the dialog claim tags \"show example values\" for a\n// consumer who configured none — false, and with no toggle rendered to correct\n// it, since the toggle hides itself when nothing has a sample.\nconst localSampleMode = ref(\n hasMergeTagSamples(inject(MERGE_TAGS_KEY, NO_MERGE_TAGS).value),\n);\nconst sampleMode = computed({\n get: () =>\n // Off entirely while a resolver is configured — same rule as the canvas.\n previewResolution.isConfigured\n ? false\n : (sharedSampleMode?.value ?? localSampleMode.value),\n set: (on: boolean) => {\n if (sharedSampleMode) sharedSampleMode.value = on;\n else localSampleMode.value = on;\n },\n});\n\nconst previewBlocks = computed(\n () => previewResolution.content.value.blocks ?? [],\n);\n\nconst hasAllowlist = computed(\n () =>\n props.allowedRecipients !== undefined && props.allowedRecipients.length > 0,\n);\nconst isSingleRecipient = computed(() => props.allowedRecipients?.length === 1);\n\n// Mounted behind a `v-if` on the same state that drives `visible`, so it can\n// mount with `visible` already true — without `immediate` the field would never\n// be seeded.\nwatch(\n () => props.visible,\n (visible) => {\n if (!visible) return;\n recipient.value =\n props.defaultRecipient ?? props.allowedRecipients?.[0] ?? \"\";\n },\n { immediate: true },\n);\n\n/** Shape-check free text only; a picker can't produce a malformed value. */\nconst isRecipientValid = computed(() =>\n hasAllowlist.value\n ? recipient.value.length > 0\n : looksLikeEmail(recipient.value),\n);\n\nconst canSend = computed(\n () => isRecipientValid.value && !props.isSending && !props.justSent,\n);\n\n/** Free text that's been typed into but isn't yet a plausible address. */\nconst showShapeHint = computed(\n () =>\n !hasAllowlist.value &&\n recipient.value.trim().length > 0 &&\n !isRecipientValid.value,\n);\n\nconst errorText = computed(() => {\n if (!props.error) return null;\n return props.error.kind === \"provider\"\n ? props.error.message\n : t.testEmail.recipientNotAllowed;\n});\n\nfunction handleSend(): void {\n if (!canSend.value) return;\n emit(\"send\", recipient.value.trim());\n}\n\nfunction handleClose(): void {\n // Sending is the one state that blocks dismissal; the success state closes\n // immediately rather than making the user wait out the timer.\n if (props.isSending) return;\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSend();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Grows to `max-w-3xl` only while the preview is open. A 600px email has\n to render at true size AND leave slack on both sides for the band of\n body background the preview draws beside it: at `max-w-2xl` the\n preview region's content box is exactly 600px, so the band would have\n nowhere to go and `settings.backgroundColor` would look unset for any\n template whose sections span the full width. Collapsed, it stays the\n compact form it was. `w-full` keeps it shrinking on narrow screens.\n `max-h-[90%]`\n with a `min-h-0 overflow-y-auto` preview region is the pattern the\n saved-blocks dialogs use: the cap bounds the panel to TplModal's\n backdrop (never to `vh` — see the note there), and `min-h-0` is what\n lets a flex child shrink below its content and actually scroll. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSending\"\n aria-labelledby=\"tpl-test-email-title\"\n class=\"tpl-scale-in tpl:flex tpl:max-h-[90%] tpl:w-full tpl:max-w-3xl tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-test-email-title\"\n class=\"tpl:mb-4 tpl:shrink-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.testEmail.title }}\n </h3>\n\n <!-- Recipient -->\n <div class=\"tpl:mb-3 tpl:shrink-0\">\n <label\n for=\"tpl-test-email-recipient\"\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.recipientLabel }}\n </label>\n\n <!-- Exactly one allowed address: nothing to choose. -->\n <input\n v-if=\"isSingleRecipient\"\n id=\"tpl-test-email-recipient\"\n type=\"text\"\n :value=\"recipient\"\n disabled\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <!-- Several allowed addresses: pick one. -->\n <select\n v-else-if=\"hasAllowlist\"\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"email in allowedRecipients\"\n :key=\"email\"\n :value=\"email\"\n >\n {{ email }}\n </option>\n </select>\n\n <!-- No allowlist: free text. -->\n <input\n v-else\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n type=\"email\"\n autocomplete=\"email\"\n spellcheck=\"false\"\n :placeholder=\"t.testEmail.recipientPlaceholder\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-[var(--tpl-shadow-sm)] tpl:outline-none tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)] tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <p\n v-if=\"showShapeHint\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.testEmail.invalidAddress }}\n </p>\n </div>\n\n <!-- Preview header: a label plus the viewport switch. Always shown — the\n preview is part of the dialog rather than something to opt into. -->\n <div class=\"tpl:mb-2 tpl:shrink-0\">\n <!-- Two segmented controls: `flex-wrap` so they drop to their own row\n rather than crushing the label on a narrow viewport. -->\n <div\n class=\"tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.preview }}\n </span>\n\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <MergeTagModeToggle\n v-if=\"!previewResolution.supersedesSamples.value\"\n :sample-mode=\"sampleMode\"\n @change=\"sampleMode = $event\"\n />\n\n <!-- Without this the preview would silently show only the desktop\n variant of any responsive block. -->\n <ViewportToggle\n :viewport=\"previewViewport\"\n @change=\"previewViewport = $event\"\n />\n </div>\n </div>\n\n <!-- The hint has to follow the mode: \"shown unresolved\" is simply false\n once every tag renders a realistic value. -->\n <p\n data-testid=\"test-email-preview-hint\"\n class=\"tpl:mt-1 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n previewResolution.supersedesSamples.value\n ? t.previewResolution.hint\n : sampleMode\n ? t.testEmail.previewHintSample\n : t.testEmail.previewHint\n }}\n </p>\n </div>\n\n <!-- The one region allowed to grow: `flex-1 min-h-0` so it absorbs the\n remaining height and scrolls, leaving the form and actions fixed. -->\n <div\n id=\"tpl-test-email-preview\"\n data-testid=\"test-email-preview\"\n class=\"tpl:mb-3 tpl:min-h-0 tpl:flex-1 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-sm)] tpl:p-3 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <!-- Skeleton while a *first* resolve is outstanding, so the dialog never\n flashes the unresolved template before the resolved one. Sized like\n the frame it replaces to avoid a layout jump. A re-resolve (recipient\n change) keeps the previous result instead — `isInitialResolve`. -->\n <div\n v-if=\"previewResolution.isInitialResolve.value\"\n role=\"status\"\n aria-busy=\"true\"\n data-testid=\"preview-resolution-loading\"\n class=\"tpl:flex tpl:flex-col tpl:gap-3\"\n >\n <span class=\"tpl:sr-only\">{{ t.previewResolution.resolving }}</span>\n <div\n v-for=\"n in 3\"\n :key=\"n\"\n aria-hidden=\"true\"\n class=\"tpl-pulse tpl:h-16 tpl:rounded-md tpl:bg-[var(--tpl-bg-hover)]\"\n ></div>\n </div>\n\n <!-- Resolution failed: the unresolved template renders below, so the\n dialog degrades rather than showing an empty frame. -->\n <p\n v-else-if=\"previewResolution.hasFailed.value\"\n role=\"status\"\n data-testid=\"preview-resolution-failed\"\n class=\"tpl:mb-2 tpl:rounded-md tpl:px-3 tpl:py-2 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg-hover)]\"\n >\n {{ t.previewResolution.failed }}\n </p>\n\n <BlockPreviewCanvas\n v-if=\"!previewResolution.isInitialResolve.value\"\n :blocks=\"previewBlocks\"\n :viewport=\"previewViewport\"\n :apply-condition-filter=\"!previewResolution.isConfigured\"\n />\n </div>\n\n <!-- Success. Announced politely; the dialog closes itself shortly after. -->\n <p\n v-if=\"justSent\"\n role=\"status\"\n data-testid=\"test-email-success\"\n class=\"tpl:mb-3 tpl:flex tpl:shrink-0 tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success,var(--tpl-primary))]\"\n >\n <Check :size=\"13\" :stroke-width=\"2.5\" />\n {{ t.testEmail.success }}\n </p>\n\n <!-- Failure. Stays open so the user can retry. -->\n <p\n v-else-if=\"errorText\"\n role=\"alert\"\n data-testid=\"test-email-error\"\n class=\"tpl:mb-3 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ errorText }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:shrink-0 tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"test-email-cancel\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSending\"\n :class=\"{ 'tpl:cursor-not-allowed tpl:opacity-50': isSending }\"\n @click=\"handleClose\"\n >\n {{ t.testEmail.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"test-email-send\"\n :class=\"primaryBtnClass\"\n :disabled=\"!canSend\"\n @click=\"handleSend\"\n >\n <span v-if=\"isSending\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.testEmail.sending }}\n </span>\n <span v-else>\n {{ t.testEmail.send }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ var o = /* @__PURE__ */ t({
5
5
  __name: "TestEmailPanel",
6
6
  props: { feature: {} },
7
7
  setup(t) {
8
- let o = t, s = a(() => import("./TestEmailModal-BAuDxJtD.js"));
8
+ let o = t, s = a(() => import("./TestEmailModal-DId9Og-N.js"));
9
9
  function c(e) {
10
10
  o.feature.send(e);
11
11
  }
@@ -32,4 +32,4 @@ var o = /* @__PURE__ */ t({
32
32
  //#endregion
33
33
  export { o as default };
34
34
 
35
- //# sourceMappingURL=TestEmailPanel-GUGZisLp.js.map
35
+ //# sourceMappingURL=TestEmailPanel-B8LwXmeX.js.map