@templatical/editor 0.42.0 → 0.42.1

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 (272) hide show
  1. package/dist/{AiChatSidebar-DqSoJAXx.js → AiChatSidebar-DOCvzGBe.js} +7 -7
  2. package/dist/{AiFeatureMenu-CoBvE72q.js → AiFeatureMenu-CfijAx4G.js} +4 -4
  3. package/dist/{BlockIssueBadge-D8KsGWY6.js → BlockIssueBadge-BJ3A6mqh.js} +2 -2
  4. package/dist/{BlockPreviewCanvas-W6-MRpWz.js → BlockPreviewCanvas-DggfBaoJ.js} +2 -2
  5. package/dist/{CloudHeaderExtras-AbsNuL9T.js → CloudHeaderExtras-dFbdqBr5.js} +2 -2
  6. package/dist/{CloudPanels-BNL6rSeB.js → CloudPanels-DmMxhzzZ.js} +4 -4
  7. package/dist/{CollaboratorBar-DlZ-wOL_.js → CollaboratorBar-XXk1tOC5.js} +2 -2
  8. package/dist/{CommentsPanel-9k1q-79y.js → CommentsPanel-CGEhbjx0.js} +1 -1
  9. package/dist/{CommentsSidebar-Bc7nORmD.js → CommentsSidebar-CUpst9ou.js} +10 -10
  10. package/dist/{CountdownToolbar-Bnq25sR-.js → CountdownToolbar-B1a_UIuB.js} +3 -3
  11. package/dist/{DesignReferenceSidebar-Cl5eE8Kn.js → DesignReferenceSidebar-DkAmsBgM.js} +5 -5
  12. package/dist/{IssuesPanel-Dnns3O9z.js → IssuesPanel-lTpsCV4a.js} +5 -5
  13. package/dist/{LogicTagInsertButton-SI6irEtn.js → LogicTagInsertButton-B5YymR4U.js} +3 -3
  14. package/dist/{MediaEditModal-C-InB-H2.js → MediaEditModal-B1X8qtJb.js} +1 -1
  15. package/dist/{MediaPanels-CTosUpeo.js → MediaPanels-DO30L1KS.js} +1 -1
  16. package/dist/{MergeTagInput-BMQGNh1k.js → MergeTagInput-B0M_-X0K.js} +1 -1
  17. package/dist/{MergeTagModeToggle-Cm9s-w6o.js → MergeTagModeToggle-B-8W1tK7.js} +3 -3
  18. package/dist/{ParagraphEditor-CnBPQCVo.js → ParagraphEditor-BhhpXgoO.js} +20 -20
  19. package/dist/{RestoreVersionDialog-B13whpxH.js → RestoreVersionDialog-DE8aqmdi.js} +3 -3
  20. package/dist/{RichTextEditorContent-DOvhgc-7.js → RichTextEditorContent-AaLAArA0.js} +4 -4
  21. package/dist/{SaveBlockDialog-b5yrQfoN.js → SaveBlockDialog-BJ-hEwLv.js} +8 -8
  22. package/dist/{SavedBlocksBrowserModal-DpqK4Zqp.js → SavedBlocksBrowserModal-nLI8aAQ5.js} +9 -9
  23. package/dist/{SavedBlocksPanels-C2wURIlT.js → SavedBlocksPanels-RnzUMUJI.js} +1 -1
  24. package/dist/{SavedBlocksPickBar-Bx9_SXVA.js → SavedBlocksPickBar-D3QA3Tgy.js} +2 -2
  25. package/dist/{TemplateScoringPanel-Cs0TGtRF.js → TemplateScoringPanel-C6ellAcs.js} +11 -11
  26. package/dist/{TemplateSettings-Gn9ps1R6.js → TemplateSettings-C_0qK-zs.js} +5 -5
  27. package/dist/{TestEmailModal-DaIaklcg.js → TestEmailModal-CnyxLhWJ.js} +6 -6
  28. package/dist/{TestEmailPanel-CKSvzakC.js → TestEmailPanel-BRisIL4G.js} +1 -1
  29. package/dist/{TitleEditor-C66Jk73n.js → TitleEditor-lPITPzw6.js} +9 -9
  30. package/dist/{ToggleSwitch-BqttX9lY.js → ToggleSwitch-CtZ6adlX.js} +1 -1
  31. package/dist/{Toolbar-DVsKYQaC.js → Toolbar-B6dG5_IP.js} +20 -20
  32. package/dist/{TplModal-DRQln7xH.js → TplModal-BiAf7tWe.js} +1 -1
  33. package/dist/{VersionHistoryMenu-b0A8mS2_.js → VersionHistoryMenu-DdspCRSA.js} +5 -5
  34. package/dist/{VersionHistoryPanels-Df5WjNah.js → VersionHistoryPanels-C589Jm_B.js} +1 -1
  35. package/dist/{VersionPreviewBanner-xB9r83r0.js → VersionPreviewBanner-XUuLe36h.js} +1 -1
  36. package/dist/{WrapperBlock-B0cUORGz.js → WrapperBlock-CVQ4t1tY.js} +1 -1
  37. package/dist/{blockTypeIcons-DfQNZnl7.js → blockTypeIcons-Sg8SY1NQ.js} +2 -2
  38. package/dist/bundle-stats.json +6 -6
  39. package/dist/cdn/chunks/{AiChatSidebar-DD17BwXn.js → AiChatSidebar-DiG-EQ-E.js} +4 -4
  40. package/dist/cdn/chunks/AiChatSidebar-DiG-EQ-E.js.map +1 -0
  41. package/dist/cdn/chunks/{AiFeatureMenu-Di381OsL.js → AiFeatureMenu-BXSPgOzA.js} +3 -3
  42. package/dist/cdn/chunks/AiFeatureMenu-BXSPgOzA.js.map +1 -0
  43. package/dist/cdn/chunks/{BlockIssueBadge-BR_N3nRh.js → BlockIssueBadge-ChQ0mPf5.js} +2 -2
  44. package/dist/cdn/chunks/BlockIssueBadge-ChQ0mPf5.js.map +1 -0
  45. package/dist/cdn/chunks/{BlockPreviewCanvas-BXpcAiiY.js → BlockPreviewCanvas-CMmv1FTA.js} +3 -3
  46. package/dist/cdn/chunks/BlockPreviewCanvas-CMmv1FTA.js.map +1 -0
  47. package/dist/cdn/chunks/{CloudHeaderExtras-BEM2eWfF.js → CloudHeaderExtras-yvFz2_e-.js} +3 -3
  48. package/dist/cdn/chunks/CloudHeaderExtras-yvFz2_e-.js.map +1 -0
  49. package/dist/cdn/chunks/{CloudPanels-D7nK1-bm.js → CloudPanels-DpKck9Na.js} +4 -4
  50. package/dist/cdn/chunks/CloudPanels-DpKck9Na.js.map +1 -0
  51. package/dist/cdn/chunks/{CollaboratorBar-D1SUwahw.js → CollaboratorBar-Clm1ZVXQ.js} +3 -3
  52. package/dist/cdn/chunks/CollaboratorBar-Clm1ZVXQ.js.map +1 -0
  53. package/dist/cdn/chunks/{CommentsPanel-qsLMAlJU.js → CommentsPanel-CjhxGMjL.js} +2 -2
  54. package/dist/cdn/chunks/CommentsPanel-CjhxGMjL.js.map +1 -0
  55. package/dist/cdn/chunks/{CommentsSidebar-CTwEE-s_.js → CommentsSidebar-D9bqMnlt.js} +2 -2
  56. package/dist/cdn/chunks/CommentsSidebar-D9bqMnlt.js.map +1 -0
  57. package/dist/cdn/chunks/CountdownBlock-sVDjKkRw.js.map +1 -1
  58. package/dist/cdn/chunks/{CountdownToolbar-BJTcnIQ-.js → CountdownToolbar-CIRx5w1U.js} +4 -4
  59. package/dist/cdn/chunks/CountdownToolbar-CIRx5w1U.js.map +1 -0
  60. package/dist/cdn/chunks/{DesignReferenceSidebar-Dl3eEGtS.js → DesignReferenceSidebar-C1-SbE_R.js} +2 -2
  61. package/dist/cdn/chunks/DesignReferenceSidebar-C1-SbE_R.js.map +1 -0
  62. package/dist/cdn/chunks/{IssuesPanel-L9Gq_T8n.js → IssuesPanel-qKY6UDd-.js} +2 -2
  63. package/dist/cdn/chunks/IssuesPanel-qKY6UDd-.js.map +1 -0
  64. package/dist/cdn/chunks/LoadingTrack-_7DH_5aw.js.map +1 -1
  65. package/dist/cdn/chunks/{LogicTagInsertButton-3pIbVE9p.js → LogicTagInsertButton-CItDS_U1.js} +2 -2
  66. package/dist/cdn/chunks/LogicTagInsertButton-CItDS_U1.js.map +1 -0
  67. package/dist/cdn/chunks/{MediaEditModal-rTjYl-fV.js → MediaEditModal-CzUtXkpk.js} +2 -2
  68. package/dist/cdn/chunks/MediaEditModal-CzUtXkpk.js.map +1 -0
  69. package/dist/cdn/chunks/{MediaPanels-DMXRQVqg.js → MediaPanels-DOLsh7WC.js} +2 -2
  70. package/dist/cdn/chunks/MediaPanels-DOLsh7WC.js.map +1 -0
  71. package/dist/cdn/chunks/{MergeTagInput-JcBzr77a.js → MergeTagInput-B33u3nxj.js} +2 -2
  72. package/dist/cdn/chunks/MergeTagInput-B33u3nxj.js.map +1 -0
  73. package/dist/cdn/chunks/{MergeTagModeToggle-CYmueXS2.js → MergeTagModeToggle-Bz1CH3NT.js} +2 -2
  74. package/dist/cdn/chunks/MergeTagModeToggle-Bz1CH3NT.js.map +1 -0
  75. package/dist/cdn/chunks/{ParagraphEditor-DbjFDnYm.js → ParagraphEditor-J74SIn-m.js} +24 -24
  76. package/dist/cdn/chunks/ParagraphEditor-J74SIn-m.js.map +1 -0
  77. package/dist/cdn/chunks/{RestoreVersionDialog-pANCWcce.js → RestoreVersionDialog-CFE7jqDB.js} +3 -3
  78. package/dist/cdn/chunks/RestoreVersionDialog-CFE7jqDB.js.map +1 -0
  79. package/dist/cdn/chunks/{RichTextEditorContent-BtM1or8A.js → RichTextEditorContent-dOg1xFqv.js} +4 -4
  80. package/dist/cdn/chunks/RichTextEditorContent-dOg1xFqv.js.map +1 -0
  81. package/dist/cdn/chunks/{SaveBlockDialog-CGM3mrGj.js → SaveBlockDialog-DZOmDCC8.js} +6 -6
  82. package/dist/cdn/chunks/SaveBlockDialog-DZOmDCC8.js.map +1 -0
  83. package/dist/cdn/chunks/{SavedBlocksBrowserModal-C6Z8AvtD.js → SavedBlocksBrowserModal-B43H5zKA.js} +6 -6
  84. package/dist/cdn/chunks/SavedBlocksBrowserModal-B43H5zKA.js.map +1 -0
  85. package/dist/cdn/chunks/{SavedBlocksPanels-DNwKmnhp.js → SavedBlocksPanels-uTSb2xO_.js} +2 -2
  86. package/dist/cdn/chunks/SavedBlocksPanels-uTSb2xO_.js.map +1 -0
  87. package/dist/cdn/chunks/{SavedBlocksPickBar-D2p1ufB4.js → SavedBlocksPickBar-BUvmGqHK.js} +3 -3
  88. package/dist/cdn/chunks/SavedBlocksPickBar-BUvmGqHK.js.map +1 -0
  89. package/dist/cdn/chunks/{TemplateScoringPanel-BDB4RErx.js → TemplateScoringPanel-ebsrVge3.js} +2 -2
  90. package/dist/cdn/chunks/TemplateScoringPanel-ebsrVge3.js.map +1 -0
  91. package/dist/cdn/chunks/{TemplateSettings-DVN1t3yF.js → TemplateSettings-BaEVdUTM.js} +4 -4
  92. package/dist/cdn/chunks/TemplateSettings-BaEVdUTM.js.map +1 -0
  93. package/dist/cdn/chunks/{TestEmailModal-DZBCmBtT.js → TestEmailModal-ide7UP_H.js} +6 -6
  94. package/dist/cdn/chunks/TestEmailModal-ide7UP_H.js.map +1 -0
  95. package/dist/cdn/chunks/{TestEmailPanel-1ibwYCUl.js → TestEmailPanel-Cqdsnufx.js} +2 -2
  96. package/dist/cdn/chunks/TestEmailPanel-Cqdsnufx.js.map +1 -0
  97. package/dist/cdn/chunks/{TitleEditor-BRl2qKbd.js → TitleEditor-CoFzxLPA.js} +8 -8
  98. package/dist/cdn/chunks/TitleEditor-CoFzxLPA.js.map +1 -0
  99. package/dist/cdn/chunks/{ToggleSwitch-niSL2uog.js → ToggleSwitch-D-8dCEbI.js} +2 -2
  100. package/dist/cdn/chunks/ToggleSwitch-D-8dCEbI.js.map +1 -0
  101. package/dist/cdn/chunks/{Toolbar-tTl0P-rr.js → Toolbar-BDzhrmGV.js} +32 -32
  102. package/dist/cdn/chunks/Toolbar-BDzhrmGV.js.map +1 -0
  103. package/dist/cdn/chunks/{TplModal-C_eIPykE.js → TplModal-mLMmolQp.js} +2 -2
  104. package/dist/cdn/chunks/TplModal-mLMmolQp.js.map +1 -0
  105. package/dist/cdn/chunks/{VersionHistoryMenu-Ww57XAzj.js → VersionHistoryMenu-C_bBKOYU.js} +2 -2
  106. package/dist/cdn/chunks/VersionHistoryMenu-C_bBKOYU.js.map +1 -0
  107. package/dist/cdn/chunks/{VersionHistoryPanels-w0EG_qVW.js → VersionHistoryPanels-B9LwOKxm.js} +2 -2
  108. package/dist/cdn/chunks/VersionHistoryPanels-B9LwOKxm.js.map +1 -0
  109. package/dist/cdn/chunks/{VersionPreviewBanner-Bbd4C-AN.js → VersionPreviewBanner-CKyrcgxJ.js} +2 -2
  110. package/dist/cdn/chunks/VersionPreviewBanner-CKyrcgxJ.js.map +1 -0
  111. package/dist/cdn/chunks/{WrapperBlock-Ch4KeMIb.js → WrapperBlock-DI8vSvTO.js} +2 -2
  112. package/dist/cdn/chunks/WrapperBlock-DI8vSvTO.js.map +1 -0
  113. package/dist/cdn/chunks/{blockTypeIcons-CyPXFh7L.js → blockTypeIcons-CdfCfqHE.js} +2 -2
  114. package/dist/cdn/chunks/blockTypeIcons-CdfCfqHE.js.map +1 -0
  115. package/dist/cdn/chunks/ca--zGWn9AU.js.map +1 -1
  116. package/dist/cdn/chunks/ca-BTf1Zbqb.js.map +1 -1
  117. package/dist/cdn/chunks/cloud-De-eh1u6.js.map +1 -1
  118. package/dist/cdn/chunks/createCloudRuntime-DsaUaWvr.js.map +1 -1
  119. package/dist/cdn/chunks/de-BRNLKcDl.js.map +1 -1
  120. package/dist/cdn/chunks/de-mMY5yQJu.js.map +1 -1
  121. package/dist/cdn/chunks/de-xzrh9PA3.js.map +1 -1
  122. package/dist/cdn/chunks/dist-BRZhN8qJ.js.map +1 -1
  123. package/dist/cdn/chunks/dist-CXB2_kBa.js.map +1 -1
  124. package/dist/cdn/chunks/draggable-IB4x6w2O.js.map +1 -1
  125. package/dist/cdn/chunks/{editor-modal-BdI0iA5r.js → editor-modal-CZQArGNj.js} +4 -4
  126. package/dist/cdn/chunks/editor-modal-CZQArGNj.js.map +1 -0
  127. package/dist/cdn/chunks/emojiData-6m0DBh7O.js.map +1 -1
  128. package/dist/cdn/chunks/en-9unxBjV8.js +2 -0
  129. package/dist/cdn/chunks/en-ChVeGQBY.js.map +1 -1
  130. package/dist/cdn/chunks/en-JJjqHMjy.js.map +1 -1
  131. package/dist/cdn/chunks/es-BIh8BxV9.js.map +1 -1
  132. package/dist/cdn/chunks/es-BT4LSpE2.js.map +1 -1
  133. package/dist/cdn/chunks/{extensions-DK_9-6Bd.js → extensions-Dy251lPd.js} +3 -3
  134. package/dist/cdn/chunks/extensions-Dy251lPd.js.map +1 -0
  135. package/dist/cdn/chunks/formatAbsoluteDateTime-DMmop3C-.js.map +1 -1
  136. package/dist/cdn/chunks/formatRelativeTime-CtUU-QZ8.js.map +1 -1
  137. package/dist/cdn/chunks/fr-C5VUZogP.js.map +1 -1
  138. package/dist/cdn/chunks/fr-CI7OJoql.js.map +1 -1
  139. package/dist/cdn/chunks/htmlparser-ClUZ6waR.js.map +1 -1
  140. package/dist/cdn/chunks/{icons-Bad3WZ3G.js → icons-CQnjugjy.js} +38 -38
  141. package/dist/cdn/chunks/icons-CQnjugjy.js.map +1 -0
  142. package/dist/cdn/chunks/ja-DhlWUfke.js.map +1 -1
  143. package/dist/cdn/chunks/ja-x5JYxaCW.js.map +1 -1
  144. package/dist/cdn/chunks/keys-DCWeHkSF.js.map +1 -1
  145. package/dist/cdn/chunks/liquid.browser-BypTT2oc.js.map +1 -1
  146. package/dist/cdn/chunks/logger-ByvdoAwq.js.map +1 -1
  147. package/dist/cdn/chunks/nl-D8y5OWGB.js.map +1 -1
  148. package/dist/cdn/chunks/nl-UXxFyXV6.js.map +1 -1
  149. package/dist/cdn/chunks/preRenderCustomBlocks-h4ZFBKbD.js.map +1 -1
  150. package/dist/cdn/chunks/pt-BR-CoCwRUi0.js.map +1 -1
  151. package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +1 -1
  152. package/dist/cdn/chunks/pt-BR-DMCbUBvJ.js.map +1 -1
  153. package/dist/cdn/chunks/pusher-BbIHJKf3.js.map +1 -1
  154. package/dist/cdn/chunks/readableTextColor-f8Kykfnh.js.map +1 -1
  155. package/dist/cdn/chunks/socialIcons-ZOCwCCyn.js.map +1 -1
  156. package/dist/cdn/chunks/src-BR3I3gOW.js.map +1 -1
  157. package/dist/cdn/chunks/src-fZSLjhjy.js.map +1 -1
  158. package/dist/cdn/chunks/{src-BxoLKnkl.js → src-nhIF5-mf.js} +3 -3
  159. package/dist/cdn/chunks/src-nhIF5-mf.js.map +1 -0
  160. package/dist/cdn/chunks/styleConstants-C_YAPdQS.js.map +1 -1
  161. package/dist/cdn/chunks/templateSettingsFields-YCFVBVYh.js.map +1 -1
  162. package/dist/cdn/chunks/timeouts-DHPZQ0G3.js.map +1 -1
  163. package/dist/cdn/chunks/{tiptap-DhLuKyDc.js → tiptap-D1u8JIUW.js} +1916 -1917
  164. package/dist/cdn/chunks/tiptap-D1u8JIUW.js.map +1 -0
  165. package/dist/cdn/chunks/useAliveFlag-DAlQLbM2.js.map +1 -1
  166. package/dist/cdn/chunks/useCloudI18n-CrjWbpbu.js.map +1 -1
  167. package/dist/cdn/chunks/{useEditorCore-yd40TFKy.js → useEditorCore-D4kzTMtQ.js} +5 -5
  168. package/dist/cdn/chunks/useEditorCore-D4kzTMtQ.js.map +1 -0
  169. package/dist/cdn/chunks/useEditorRoot-CshefZLP.js.map +1 -1
  170. package/dist/cdn/chunks/useI18n-CyO_Nmca.js.map +1 -1
  171. package/dist/cdn/chunks/useLogicTag-J_i_hvZg.js.map +1 -1
  172. package/dist/cdn/chunks/useMergeTag-C5MyHYxJ.js.map +1 -1
  173. package/dist/cdn/chunks/usePopoverPosition-DGXjXKjM.js.map +1 -1
  174. package/dist/cdn/chunks/useScrollToBlock-DgHZJvID.js.map +1 -1
  175. package/dist/cdn/editor.css +1 -1
  176. package/dist/cdn/editor.js +495 -466
  177. package/dist/cdn/editor.js.map +1 -1
  178. package/dist/{check-eqb2JhqU.js → check-BMx_TLAS.js} +1 -1
  179. package/dist/{chevron-down-Dkr-7Ywl.js → chevron-down-KZf5mh9_.js} +1 -1
  180. package/dist/{chevron-right-Bge4XF_B.js → chevron-right-BLZ60Vxw.js} +1 -1
  181. package/dist/{chevron-up-CQAyl0kC.js → chevron-up-B9ynB8eU.js} +1 -1
  182. package/dist/{circle-alert-CYgTHKWr.js → circle-alert-pjvFFDrF.js} +1 -1
  183. package/dist/{clock-BgSJZJP5.js → clock-CiiTBfF3.js} +1 -1
  184. package/dist/{copy-mw6PVnOJ.js → copy-BF0_pQjH.js} +1 -1
  185. package/dist/{createLucideIcon-BxY5yz5u.js → createLucideIcon-C2YOVVWx.js} +8 -8
  186. package/dist/{dist-CQSVro7b.js → dist-B0dkvy7z.js} +1 -1
  187. package/dist/{dist-BJM3eFyT.js → dist-BPWuzECC.js} +1 -1
  188. package/dist/{dist-EDkj3Ub1.js → dist-BUHovM4k.js} +1 -1
  189. package/dist/{dist-BmsLcnrO.js → dist-BwoEME8p.js} +1 -1
  190. package/dist/{dist-DerFNpDY.js → dist-C2Ge44fD.js} +760 -761
  191. package/dist/{dist-BdHF7nyq.js → dist-CIxmOOSG.js} +5 -5
  192. package/dist/{dist-wyXxj3R1.js → dist-CJ7RZARo.js} +1 -1
  193. package/dist/{dist-Bl4rxpX9.js → dist-CxmLZQso.js} +1 -1
  194. package/dist/{dist-DyrEOKEJ.js → dist-DRmafAUz.js} +1 -1
  195. package/dist/{dist-Djmzp6wi.js → dist-DdOAotfz.js} +1 -1
  196. package/dist/{dist-BxVnMgXZ.js → dist-W_ERo6c_.js} +1 -1
  197. package/dist/{dist-DQbOebGW.js → dist-p-LkiWIQ.js} +1 -1
  198. package/dist/{editor-modal-qzmEVcIk.js → editor-modal-99FB40dl.js} +16 -16
  199. package/dist/en-BvOjRLiw.js +2 -0
  200. package/dist/{extensions-DoT2-_pz.js → extensions-DqQXxrvP.js} +3 -3
  201. package/dist/{eye-DphkRtAD.js → eye-RdXYgrQZ.js} +1 -1
  202. package/dist/{image-up-Qi2BXD7T.js → image-up-BwdoReQt.js} +1 -1
  203. package/dist/{info-VSyAs02q.js → info-zFMSGrL1.js} +1 -1
  204. package/dist/{link-CNuozGqD.js → link-D7orqFBT.js} +1 -1
  205. package/dist/{list-BA0I5Wmu.js → list-BGt4Pgxc.js} +1 -1
  206. package/dist/{loader-circle-z5eeqasv.js → loader-circle-BVBgkRGz.js} +1 -1
  207. package/dist/{message-circle-BsQ9hEup.js → message-circle-D8YLsarv.js} +1 -1
  208. package/dist/{package-OkXfuRoO.js → package-CuFvk0L5.js} +1 -1
  209. package/dist/{pencil-DFVFA7Dj.js → pencil-DRYbmZW7.js} +1 -1
  210. package/dist/{plus-D8bkVjZ7.js → plus-Dv0b3kh0.js} +1 -1
  211. package/dist/{refresh-cw-DPB7YFuz.js → refresh-cw-gYTIS5bJ.js} +1 -1
  212. package/dist/{search-C-dCgqIa.js → search-D84_Ki_a.js} +1 -1
  213. package/dist/{send-Cm2fy2zm.js → send-Cn-8_H_U.js} +1 -1
  214. package/dist/{shield-check-COgBYFDF.js → shield-check-BOoVWJtD.js} +1 -1
  215. package/dist/{smartphone-v0u_MOEV.js → smartphone-4Ej3PBw5.js} +1 -1
  216. package/dist/{socialIcons-CACVa6m_.js → socialIcons-CzRJskEk.js} +1 -1
  217. package/dist/{sparkles-Ds0RF8Iu.js → sparkles-D3vnNe7O.js} +1 -1
  218. package/dist/style.css +1 -1
  219. package/dist/templatical-editor.js +404 -375
  220. package/dist/{text-align-start-DiavaIm6.js → text-align-start-Ci2h_Xly.js} +1 -1
  221. package/dist/{trash-DnOkfu2V.js → trash-wmi4UKvv.js} +1 -1
  222. package/dist/{triangle-alert-DGLAyXnh.js → triangle-alert-DJ0r2wmQ.js} +1 -1
  223. package/dist/{upload-BBDme2cy.js → upload-XMde-TXf.js} +1 -1
  224. package/dist/{useEditorCore-LyNoGs0m.js → useEditorCore-Dhit8Ygg.js} +11 -11
  225. package/dist/{useScrollToBlock-Dj4v3rxr.js → useScrollToBlock-B4F7KCrf.js} +1 -1
  226. package/dist/{x-3BHV3Q6K.js → x-BWrtpVl-.js} +1 -1
  227. package/package.json +10 -10
  228. package/dist/cdn/chunks/AiChatSidebar-DD17BwXn.js.map +0 -1
  229. package/dist/cdn/chunks/AiFeatureMenu-Di381OsL.js.map +0 -1
  230. package/dist/cdn/chunks/BlockIssueBadge-BR_N3nRh.js.map +0 -1
  231. package/dist/cdn/chunks/BlockPreviewCanvas-BXpcAiiY.js.map +0 -1
  232. package/dist/cdn/chunks/CloudHeaderExtras-BEM2eWfF.js.map +0 -1
  233. package/dist/cdn/chunks/CloudPanels-D7nK1-bm.js.map +0 -1
  234. package/dist/cdn/chunks/CollaboratorBar-D1SUwahw.js.map +0 -1
  235. package/dist/cdn/chunks/CommentsPanel-qsLMAlJU.js.map +0 -1
  236. package/dist/cdn/chunks/CommentsSidebar-CTwEE-s_.js.map +0 -1
  237. package/dist/cdn/chunks/CountdownToolbar-BJTcnIQ-.js.map +0 -1
  238. package/dist/cdn/chunks/DesignReferenceSidebar-Dl3eEGtS.js.map +0 -1
  239. package/dist/cdn/chunks/IssuesPanel-L9Gq_T8n.js.map +0 -1
  240. package/dist/cdn/chunks/LogicTagInsertButton-3pIbVE9p.js.map +0 -1
  241. package/dist/cdn/chunks/MediaEditModal-rTjYl-fV.js.map +0 -1
  242. package/dist/cdn/chunks/MediaPanels-DMXRQVqg.js.map +0 -1
  243. package/dist/cdn/chunks/MergeTagInput-JcBzr77a.js.map +0 -1
  244. package/dist/cdn/chunks/MergeTagModeToggle-CYmueXS2.js.map +0 -1
  245. package/dist/cdn/chunks/ParagraphEditor-DbjFDnYm.js.map +0 -1
  246. package/dist/cdn/chunks/RestoreVersionDialog-pANCWcce.js.map +0 -1
  247. package/dist/cdn/chunks/RichTextEditorContent-BtM1or8A.js.map +0 -1
  248. package/dist/cdn/chunks/SaveBlockDialog-CGM3mrGj.js.map +0 -1
  249. package/dist/cdn/chunks/SavedBlocksBrowserModal-C6Z8AvtD.js.map +0 -1
  250. package/dist/cdn/chunks/SavedBlocksPanels-DNwKmnhp.js.map +0 -1
  251. package/dist/cdn/chunks/SavedBlocksPickBar-D2p1ufB4.js.map +0 -1
  252. package/dist/cdn/chunks/TemplateScoringPanel-BDB4RErx.js.map +0 -1
  253. package/dist/cdn/chunks/TemplateSettings-DVN1t3yF.js.map +0 -1
  254. package/dist/cdn/chunks/TestEmailModal-DZBCmBtT.js.map +0 -1
  255. package/dist/cdn/chunks/TestEmailPanel-1ibwYCUl.js.map +0 -1
  256. package/dist/cdn/chunks/TitleEditor-BRl2qKbd.js.map +0 -1
  257. package/dist/cdn/chunks/ToggleSwitch-niSL2uog.js.map +0 -1
  258. package/dist/cdn/chunks/Toolbar-tTl0P-rr.js.map +0 -1
  259. package/dist/cdn/chunks/TplModal-C_eIPykE.js.map +0 -1
  260. package/dist/cdn/chunks/VersionHistoryMenu-Ww57XAzj.js.map +0 -1
  261. package/dist/cdn/chunks/VersionHistoryPanels-w0EG_qVW.js.map +0 -1
  262. package/dist/cdn/chunks/VersionPreviewBanner-Bbd4C-AN.js.map +0 -1
  263. package/dist/cdn/chunks/WrapperBlock-Ch4KeMIb.js.map +0 -1
  264. package/dist/cdn/chunks/blockTypeIcons-CyPXFh7L.js.map +0 -1
  265. package/dist/cdn/chunks/editor-modal-BdI0iA5r.js.map +0 -1
  266. package/dist/cdn/chunks/en-Cs-nw16K.js +0 -2
  267. package/dist/cdn/chunks/extensions-DK_9-6Bd.js.map +0 -1
  268. package/dist/cdn/chunks/icons-Bad3WZ3G.js.map +0 -1
  269. package/dist/cdn/chunks/src-BxoLKnkl.js.map +0 -1
  270. package/dist/cdn/chunks/tiptap-DhLuKyDc.js.map +0 -1
  271. package/dist/cdn/chunks/useEditorCore-yd40TFKy.js.map +0 -1
  272. package/dist/en-1tgLzB8q.js +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudPanels-DpKck9Na.js","names":["_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_toDisplayString","_normalizeClass","_createCommentVNode","_sfc_main","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createElementBlock","_unref","_createStaticVNode","_createElementVNode","_Fragment","_renderList","_createCommentVNode","_sfc_main","_hoisted_1","_openBlock","_createBlock","TplModal","_withCtx","_createElementVNode","_createVNode","_unref","AlertTriangle","_toDisplayString","_createElementBlock","_Fragment","_renderList","_normalizeClass","_sfc_main","_openBlock","_createElementBlock","_toDisplayString","_unref","_createCommentVNode","_sfc_main","_openBlock","_createElementBlock","_Fragment","_createVNode","_Transition","_withCtx","CloudLoadingOverlay","CloudErrorOverlay","CloudSaveGateModal","CollabUndoToast","_unref","_sfc_main","renderList","withCtx","openBlock","defineComponent","unref","createVNode","normalizeClass","createElementBlock","createCommentVNode","createBlock","Transition","createBaseVNode","Fragment","toDisplayString","createStaticVNode","defineAsyncComponent","useI18n","CircleAlert","TriangleAlert","useCloudI18nStrict","dangerBtnClass","primaryBtnClass","secondaryBtnClass","TplModal_default","_hoisted_1$3","_hoisted_2$2","_hoisted_3$2","_hoisted_4$1","_hoisted_5$1","CloudErrorOverlay_default","__emit","emit","cloudT","getErrorMessage","error","isNotFoundError","__props","_cache","_hoisted_1$2","_hoisted_2$1","_hoisted_3$1","CloudLoadingOverlay_default","n","_hoisted_1$1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","CloudSaveGateModal_default","issue","_hoisted_1","\"color\"","\"border\"","CollabUndoToast_default","CloudPanels_default","AiChatSidebar","DesignReferenceSidebar","TemplateScoringPanel","props","applyContent","content"],"sources":["../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CloudPanels.vue","../../../src/cloud/components/CloudPanels.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { primaryBtnClass } from \"../../constants/styleConstants\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-[100] tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n type=\"button\"\n :class=\"primaryBtnClass\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { primaryBtnClass } from \"../../constants/styleConstants\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-[100] tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n type=\"button\"\n :class=\"primaryBtnClass\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n :aria-label=\"cloudT.loading.initializing\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-[100] tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n aria-busy=\"true\"\n :aria-label=\"cloudT.loading.initializing\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-[100] tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport {\n dangerBtnClass,\n secondaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <TplModal :visible=\"open\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-save-gate-title\"\n aria-describedby=\"tpl-save-gate-body\"\n data-testid=\"cloud-save-gate-dialog\"\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-lg)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-lg)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n id=\"tpl-save-gate-title\"\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p\n id=\"tpl-save-gate-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"cloud-save-gate-cancel\"\n :class=\"secondaryBtnClass\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"cloud-save-gate-confirm\"\n :class=\"dangerBtnClass\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport {\n dangerBtnClass,\n secondaryBtnClass,\n} from \"../../constants/styleConstants\";\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <TplModal :visible=\"open\" @close=\"emit('cancel')\">\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-save-gate-title\"\n aria-describedby=\"tpl-save-gate-body\"\n data-testid=\"cloud-save-gate-dialog\"\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-lg)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-lg)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n id=\"tpl-save-gate-title\"\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p\n id=\"tpl-save-gate-body\"\n class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"cloud-save-gate-cancel\"\n :class=\"secondaryBtnClass\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"cloud-save-gate-confirm\"\n :class=\"dangerBtnClass\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-[60] tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-[60] tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n *\n * The media library is not here either: it is shared, as `MediaPanels` in\n * `Editor.vue` over a `MediaProvider`. Cloud supplies that provider; it does\n * not mount a second modal.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n *\n * The media library is not here either: it is shared, as `MediaPanels` in\n * `Editor.vue` over a `MediaProvider`. Cloud supplies that provider; it does\n * not mount a second modal.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n</template>\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { F as e, N as t, S as n, at as r, f as i, g as a, h as o, lt as s, m as c, p as l, u, ut as d } from "./draggable-IB4x6w2O.js";
2
- import "./useEditorCore-yd40TFKy.js";
3
- import { a as f, i as p } from "./icons-Bad3WZ3G.js";
2
+ import "./useEditorCore-D4kzTMtQ.js";
3
+ import { a as f, i as p } from "./icons-CQnjugjy.js";
4
4
  import { n as m } from "./useCloudI18n-CrjWbpbu.js";
5
5
  import { t as h } from "./readableTextColor-f8Kykfnh.js";
6
6
  //#region src/cloud/components/CollaboratorBar.vue?vue&type=script&setup=true&lang.ts
@@ -49,4 +49,4 @@ var g = { class: "tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2" }, _
49
49
  //#endregion
50
50
  export { S as default };
51
51
 
52
- //# sourceMappingURL=CollaboratorBar-D1SUwahw.js.map
52
+ //# sourceMappingURL=CollaboratorBar-Clm1ZVXQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollaboratorBar-Clm1ZVXQ.js","names":["_openBlock","_createElementBlock","_createElementVNode","_normalizeStyle","_unref","_createBlock","_Fragment","_renderList","_toDisplayString","_createCommentVNode","_sfc_main","renderList","openBlock","defineComponent","unref","computed","createElementBlock","createCommentVNode","normalizeStyle","createBlock","createBaseVNode","toDisplayString","WifiOff","Wifi","useCloudI18nStrict","readableTextColor","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","maxVisible","CollaboratorBar_default","props","__props","cloudT","visibleCollaborators","overflowCollaborators","overflowCount","overflowNames","c","getInitials","parts","name","Fragment","collaborator"],"sources":["../../../src/cloud/components/CollaboratorBar.vue","../../../src/cloud/components/CollaboratorBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-xs\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-xs\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;iCAcM+B,IAAa;;;;;;;EAPnB,IAAME,IAAQC,GAKR,EAAE,GAAGC,MAAWX,EAAmB,GAInCY,IAAuBrB,QAC3BkB,EAAM,cAAc,MAAM,GAAGF,CAAU,CACzC,GAEMM,IAAwBtB,QAC5BkB,EAAM,cAAc,MAAMF,CAAU,CACtC,GAEMO,IAAgBvB,QAAesB,EAAsB,MAAM,MAAM,GAEjEE,IAAgBxB,QACpBsB,EAAsB,MAAM,KAAK,MAAMG,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAC1D;EAEA,SAASC,EAAY,GAAsB;GACzC,IAAMC,IAAQC,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK;GAMrC,OALID,EAAM,UAAU,KAEhBA,EAAM,EAAE,CAAC,OAAO,CAAC,IAAIA,EAAMA,EAAM,SAAS,EAAE,CAAC,OAAO,CAAC,EAAA,CACrD,YAAY,IAETC,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY;EACpC;oBAIE3C,EAAA,GAAAC,EA0CM,OA1CNyB,GA0CM,CAxCJxB,EAaM,OAAA;GAZJ,OAAM;GACL,OAAKC,EAAA,EAAmB,OAAA+B,EAAA,cAAW,uBAAA,wBAAA,CAAA;GAGnC,OAAgBA,EAAA,cAAwB9B,EAAA+B,CAAA,CAAM,CAAC,cAAc,YAAsB/B,EAAA+B,CAAA,CAAM,CAAC,cAAc;EAM7F,GAAA,CAAAD,EAAA,eAAZlC,EAAA,GAAAK,EAAwDD,EAAAmB,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EACnDvB,CAAAA,MAAAA,EAAA,GAAAK,EAA+CD,EAAAkB,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EAKpC,CAAA,EAAA,GAAA,IAAAK,CAAA,GAAAO,EAAA,cAAc,SAAM,KAD5BlC,EAAA,GAAAC,EAuBM,OAvBN2B,GAuBM,EAnBJ5B,EAAA,EAAA,GAAAC,EAWMK,GAAA,MAAAC,EAVmB6B,EAAA,QAAhB,OADTpC,EAAA,GAAAC,EAWM,OAAA;GATH,KAAK4C,EAAa;GACnB,OAAM;GACL,OAAK1C,EAAA;IAA+B,iBAAA0C,EAAa;IAAwB,OAAAzC,EAAAqB,CAAA,CAAiB,CAACoB,EAAa,KAAK;;GAI7G,OAAOA,EAAa;EAElB,GAAArC,EAAAiC,EAAYI,EAAa,IAAI,CAAA,GAAA,IAAAhB,CAAA,EAG1B,GAAA,GAAA,IAAAS,EAAA,QAAa,KADrBtC,EAAA,GAAAC,EAMM,OAAA;;GAJJ,OAAM;GACL,OAAOsC,EAAA;EACT,GAAA,OACE/B,EAAG8B,EAAA,KAAa,GAAA,GAAAR,CAAA,KAAAb,EAAA,IAAA,EAAA,CAAA,CAAA,KAAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -5,7 +5,7 @@ var o = /* @__PURE__ */ t({
5
5
  __name: "CommentsPanel",
6
6
  props: { feature: {} },
7
7
  setup(t) {
8
- let o = a(() => import("./CommentsSidebar-CTwEE-s_.js"));
8
+ let o = a(() => import("./CommentsSidebar-D9bqMnlt.js"));
9
9
  return (a, s) => t.feature.isOpen.value ? (e(), i(n(o), {
10
10
  key: 0,
11
11
  visible: t.feature.isOpen.value,
@@ -17,4 +17,4 @@ var o = /* @__PURE__ */ t({
17
17
  //#endregion
18
18
  export { o as default };
19
19
 
20
- //# sourceMappingURL=CommentsPanel-qsLMAlJU.js.map
20
+ //# sourceMappingURL=CommentsPanel-CjhxGMjL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentsPanel-CjhxGMjL.js","names":["_openBlock","_createBlock","_unref","_createCommentVNode","_sfc_main","openBlock","defineComponent","unref","createCommentVNode","createBlock","defineAsyncComponent","CommentsPanel_default","CommentsSidebar","__props","_cache"],"sources":["../../../src/components/CommentsPanel.vue","../../../src/components/CommentsPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Mount point for the comments sidebar, shared by both entry points.\n *\n * The sidebar is `defineAsyncComponent` behind a `v-if` on its open state, so its\n * chunk is fetched only when the user actually opens the panel. `Editor.vue` in\n * turn lazy-loads *this* wrapper and renders it only when a `CommentsProvider` and\n * a `user` are configured, so a consumer with neither downloads none of it —\n * the `TestEmailPanel` / `SavedBlocksPanels` shape.\n *\n * Never static-import `CommentsSidebar` into `Editor.vue`: that collapses the\n * whole panel into the main entry for every consumer. Guarded by\n * `tests/cdn-chunk-granularity.test.ts`.\n */\nimport { defineAsyncComponent } from \"vue\";\nimport type { UseCommentsFeatureReturn } from \"../composables/useCommentsFeature\";\n\ndefineProps<{\n feature: UseCommentsFeatureReturn;\n}>();\n\nconst CommentsSidebar = defineAsyncComponent(\n () => import(\"./CommentsSidebar.vue\"),\n);\n</script>\n\n<template>\n <CommentsSidebar\n v-if=\"feature.isOpen.value\"\n :visible=\"feature.isOpen.value\"\n :feature=\"feature\"\n @close=\"feature.close()\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * Mount point for the comments sidebar, shared by both entry points.\n *\n * The sidebar is `defineAsyncComponent` behind a `v-if` on its open state, so its\n * chunk is fetched only when the user actually opens the panel. `Editor.vue` in\n * turn lazy-loads *this* wrapper and renders it only when a `CommentsProvider` and\n * a `user` are configured, so a consumer with neither downloads none of it —\n * the `TestEmailPanel` / `SavedBlocksPanels` shape.\n *\n * Never static-import `CommentsSidebar` into `Editor.vue`: that collapses the\n * whole panel into the main entry for every consumer. Guarded by\n * `tests/cdn-chunk-granularity.test.ts`.\n */\nimport { defineAsyncComponent } from \"vue\";\nimport type { UseCommentsFeatureReturn } from \"../composables/useCommentsFeature\";\n\ndefineProps<{\n feature: UseCommentsFeatureReturn;\n}>();\n\nconst CommentsSidebar = defineAsyncComponent(\n () => import(\"./CommentsSidebar.vue\"),\n);\n</script>\n\n<template>\n <CommentsSidebar\n v-if=\"feature.isOpen.value\"\n :visible=\"feature.isOpen.value\"\n :feature=\"feature\"\n @close=\"feature.close()\"\n />\n</template>\n"],"mappings":";;;;;;;EAqBA,IAAMY,IAAkBF,QAChB,OAAO,gCACf;mBAKUG,EAAA,QAAQ,OAAO,SADvBb,EAAA,GAAAC,EAKEC,EAAAU,CAAA,GAAA;;GAHC,SAASC,EAAA,QAAQ,OAAO;GACxB,SAASA,EAAA;GACT,SAAK,AAAAC,EAAA,QAAA,MAAED,EAAA,QAAQ,MAAK"}
@@ -1,7 +1,7 @@
1
1
  import { B as e, F as t, H as n, N as r, Q as i, S as a, U as o, at as s, b as c, ct as l, f as u, g as d, h as f, k as ee, lt as te, m as p, n as m, o as h, p as g, u as _, ut as v, y as ne } from "./draggable-IB4x6w2O.js";
2
2
  import { $ as re, g as ie } from "./keys-DCWeHkSF.js";
3
3
  import { t as ae } from "./useI18n-CyO_Nmca.js";
4
- import { Lt as oe, Mt as se, N as y, Pt as ce, R as le, Rt as ue, at as b, f as x, n as S, nt as C, q as w } from "./icons-Bad3WZ3G.js";
4
+ import { Lt as oe, Mt as se, N as y, Pt as ce, R as le, Rt as ue, at as b, f as x, n as S, nt as C, q as w } from "./icons-CQnjugjy.js";
5
5
  import { t as T } from "./_plugin-vue_export-helper-B3ysoDQm.js";
6
6
  import { t as de } from "./formatRelativeTime-CtUU-QZ8.js";
7
7
  //#region src/components/CommentsSidebar.vue?vue&type=script&setup=true&lang.ts
@@ -467,4 +467,4 @@ var fe = ["aria-label"], pe = { class: "tpl:flex tpl:items-center tpl:justify-be
467
467
  //#endregion
468
468
  export { E as default };
469
469
 
470
- //# sourceMappingURL=CommentsSidebar-CTwEE-s_.js.map
470
+ //# sourceMappingURL=CommentsSidebar-D9bqMnlt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentsSidebar-D9bqMnlt.js","names":["_openBlock","_createBlock","_Transition","_withCtx","_createElementBlock","_unref","_createElementVNode","_createVNode","_toDisplayString","_createCommentVNode","_normalizeClass","_Fragment","_renderList","_normalizeStyle","Trash2","_withDirectives","_vModelText","_createTextVNode","_sfc_main","watch","renderList","withCtx","openBlock","ref","defineComponent","withDirectives","unref","createVNode","normalizeClass","computed","createElementBlock","createCommentVNode","nextTick","normalizeStyle","createBlock","Transition","vModelText","createBaseVNode","Fragment","toDisplayString","createTextVNode","requireInject","EDITOR_KEY","useI18n","ChevronDown","CircleCheck","Send","ChevronUp","Reply","Check","LoaderCircle","Trash","X","MessageCircle","Pencil","_plugin_vue_export_helper_default","formatRelativeTime","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_30","_hoisted_31","_hoisted_32","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","_hoisted_40","_hoisted_41","_hoisted_42","_hoisted_43","_hoisted_44","_hoisted_45","_hoisted_46","_hoisted_47","_hoisted_48","_hoisted_49","_hoisted_50","_hoisted_51","_hoisted_52","_hoisted_53","_hoisted_54","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","_hoisted_60","_hoisted_61","_hoisted_62","_hoisted_63","_hoisted_64","CommentsSidebar_default","__expose","__emit","props","__props","emit","editor","comments","filterMode","newCommentBody","replyingTo","replyBody","editingId","editBody","expandedThreads","deletingId","newCommentInput","filterBlockId","blockId","isInitialLoad","allBlockIds","ids","block","column","child","isBlockMissing","isBlockFilterUnsaved","filteredComments","items","c","newBlockId","setFilter","mode","filterByBlock","toggleThread","threadId","startReply","cancelReply","startEdit","comment","cancelEdit","confirmDelete","commentId","cancelDelete","handleAddComment","body","handleReply","parent","parentId","handleEdit","handleDelete","handleResolve","canEdit","canDelete","formatTime","dateString","t","handleNewCommentKeydown","event","handleReplyKeydown","handleEditKeydown","focusNewComment","_cache","thread","format","$event","reply","index"],"sources":["../../../src/components/CommentsSidebar.vue","../../../src/components/CommentsSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { formatRelativeTime } from \"../utils/formatRelativeTime\";\nimport { EDITOR_KEY, requireInject } from \"../keys\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { UseCommentsFeatureReturn } from \"../composables/useCommentsFeature\";\nimport type { Comment } from \"@templatical/types\";\nimport {\n Check,\n CircleCheck,\n ChevronDown,\n ChevronUp,\n LoaderCircle,\n MessageCircle,\n Pencil,\n Reply,\n Send,\n Trash2,\n X,\n} from \"@lucide/vue\";\nimport { computed, nextTick, ref, watch } from \"vue\";\n\n/**\n * The review panel, **shared by both entry points**.\n *\n * It takes the shared feature as a prop — the `TestEmailPanel` /\n * `SavedBlocksPanels` shape — so the same panel drives a consumer's own store and\n * Cloud's, and its strings live in the OSS chunk.\n *\n * Every write action is gated on the provider having supplied that mutation, and\n * **hidden** rather than disabled when it hasn't: a provider that withholds all\n * four yields a readable review with no way to change anything.\n */\nconst props = defineProps<{\n visible: boolean;\n feature: UseCommentsFeatureReturn;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t, format } = useI18n();\nconst editor = requireInject(EDITOR_KEY, \"CommentsSidebar\");\n\nconst comments = computed(() => props.feature.headless);\n\ntype FilterMode = \"all\" | \"unresolved\" | \"block\";\nconst filterMode = ref<FilterMode>(\"unresolved\");\n\nconst newCommentBody = ref(\"\");\nconst replyingTo = ref<string | null>(null);\nconst replyBody = ref(\"\");\nconst editingId = ref<string | null>(null);\nconst editBody = ref(\"\");\nconst expandedThreads = ref<Set<string>>(new Set());\nconst deletingId = ref<string | null>(null);\nconst newCommentInput = ref<HTMLTextAreaElement | null>(null);\n\n/**\n * The block filter lives on the feature, not here: a canvas comment indicator sets\n * it *before* this panel exists (it is lazy), so the target has to survive the gap.\n */\nconst filterBlockId = computed(() => props.feature.filterBlockId.value);\n\n// An indicator click lands a block id on the feature; switching to that filter is\n// this panel's half of the handshake. `immediate` covers the case that opened the\n// panel in the first place, where the id is already set at mount.\nwatch(\n filterBlockId,\n (blockId) => {\n if (blockId) filterMode.value = \"block\";\n },\n { immediate: true },\n);\n\n// `isLoading` alone would flash the skeleton over a perfectly good list every\n// re-open; the pair distinguishes \"fetching, nothing yet\" from \"refreshing\".\nconst isInitialLoad = computed(\n () =>\n comments.value.isLoading.value &&\n comments.value.comments.value.length === 0,\n);\n\nconst allBlockIds = computed<Set<string>>(() => {\n const ids = new Set<string>();\n for (const block of editor.content.value.blocks) {\n ids.add(block.id);\n if (block.type === \"section\") {\n for (const column of block.children) {\n for (const child of column) {\n ids.add(child.id);\n }\n }\n }\n }\n return ids;\n});\n\nfunction isBlockMissing(blockId: string | null | undefined): boolean {\n if (!blockId) {\n return false;\n }\n return !allBlockIds.value.has(blockId);\n}\n\nconst isBlockFilterUnsaved = computed(() => {\n if (filterMode.value !== \"block\" || !filterBlockId.value) {\n return false;\n }\n // Asked through the capability rather than off the editor: a comment anchors to\n // a block in the *saved* template, and only the provider's side knows what that\n // contains. A store that accepts any anchor answers `true` and this never fires.\n return props.feature.capability.isBlockSaved(filterBlockId.value) === false;\n});\n\nconst filteredComments = computed(() => {\n let items = comments.value.comments.value;\n\n if (filterMode.value === \"unresolved\") {\n items = items.filter((c) => !c.resolvedAt);\n } else if (filterMode.value === \"block\" && filterBlockId.value) {\n items = items.filter((c) => c.blockId === filterBlockId.value);\n }\n\n return items;\n});\n\nwatch(\n () => editor.state.selectedBlockId,\n (newBlockId) => {\n if (filterMode.value === \"block\" && newBlockId) {\n props.feature.filterBlockId.value = newBlockId;\n }\n },\n);\n\nfunction setFilter(mode: FilterMode, blockId?: string): void {\n filterMode.value = mode;\n props.feature.filterBlockId.value =\n mode === \"block\" ? (blockId ?? null) : null;\n}\n\nfunction filterByBlock(blockId: string): void {\n setFilter(\"block\", blockId);\n}\n\nfunction toggleThread(threadId: string): void {\n if (expandedThreads.value.has(threadId)) {\n expandedThreads.value.delete(threadId);\n } else {\n expandedThreads.value.add(threadId);\n }\n}\n\nfunction startReply(threadId: string): void {\n replyingTo.value = threadId;\n replyBody.value = \"\";\n editingId.value = null;\n\n if (!expandedThreads.value.has(threadId)) {\n expandedThreads.value.add(threadId);\n }\n}\n\nfunction cancelReply(): void {\n replyingTo.value = null;\n replyBody.value = \"\";\n}\n\nfunction startEdit(comment: Comment): void {\n editingId.value = comment.id;\n editBody.value = comment.body;\n replyingTo.value = null;\n}\n\nfunction cancelEdit(): void {\n editingId.value = null;\n editBody.value = \"\";\n}\n\nfunction confirmDelete(commentId: string): void {\n deletingId.value = commentId;\n}\n\nfunction cancelDelete(): void {\n deletingId.value = null;\n}\n\n/**\n * Every write swallows its rejection. The feature already routed it to `onError`\n * and left the list untouched, and an unhandled rejection out of a DOM event\n * binding helps nobody — same shape as `VersionHistoryPanels`.\n */\nasync function handleAddComment(): Promise<void> {\n const body = newCommentBody.value.trim();\n if (!body || !comments.value.canCreate.value) {\n return;\n }\n\n const blockId =\n filterMode.value === \"block\" ? filterBlockId.value : undefined;\n try {\n await comments.value.create({ body, blockId: blockId ?? undefined });\n newCommentBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleReply(parentId: string): Promise<void> {\n const body = replyBody.value.trim();\n if (!body || !comments.value.canCreate.value) {\n return;\n }\n\n const parent = comments.value.find(parentId);\n try {\n await comments.value.create({\n body,\n blockId: parent?.blockId ?? undefined,\n parentId,\n });\n replyingTo.value = null;\n replyBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleEdit(commentId: string): Promise<void> {\n const body = editBody.value.trim();\n if (!body) {\n return;\n }\n\n try {\n await comments.value.update(commentId, { body });\n editingId.value = null;\n editBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleDelete(commentId: string): Promise<void> {\n try {\n await comments.value.remove(commentId);\n } catch {\n /* reported through onError */\n }\n deletingId.value = null;\n}\n\nasync function handleResolve(comment: Comment): Promise<void> {\n try {\n // The target state, not a toggle: two clicks in flight can't invert.\n await comments.value.setResolved(comment.id, !comment.resolvedAt);\n } catch {\n /* reported through onError */\n }\n}\n\n/** Editing and deleting are the author's own, and only when the store allows it. */\nfunction canEdit(comment: Comment): boolean {\n return comments.value.canUpdate.value && comments.value.isOwn(comment);\n}\n\nfunction canDelete(comment: Comment): boolean {\n return comments.value.canDelete.value && comments.value.isOwn(comment);\n}\n\nfunction formatTime(dateString: string): string {\n return formatRelativeTime(dateString, t.time) ?? dateString;\n}\n\nfunction handleNewCommentKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleAddComment();\n }\n}\n\nfunction handleReplyKeydown(event: KeyboardEvent, parentId: string): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleReply(parentId);\n }\n}\n\nfunction handleEditKeydown(event: KeyboardEvent, commentId: string): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleEdit(commentId);\n }\n if (event.key === \"Escape\") {\n cancelEdit();\n }\n}\n\nfunction focusNewComment(): void {\n nextTick(() => {\n newCommentInput.value?.focus();\n });\n}\n\ndefineExpose({ filterByBlock, focusNewComment });\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-comments-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-comments-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=\"t.comments.title\"\n data-testid=\"comments-sidebar\"\n class=\"tpl-comments-sidebar 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-text)]\"\n >\n <MessageCircle :size=\"13\" :stroke-width=\"2\" />\n <span>{{ t.comments.title }}</span>\n <span\n v-if=\"feature.unresolvedCount.value > 0\"\n class=\"tpl:ml-1 tpl:inline-flex tpl:size-5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n >\n {{ feature.unresolvedCount.value }}\n </span>\n </div>\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=\"t.comments.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Filter bar -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-2\"\n >\n <button\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n filterMode === 'unresolved' ? 'tpl-comment-filter--active' : ''\n \"\n :aria-pressed=\"filterMode === 'unresolved'\"\n data-testid=\"comments-filter-unresolved\"\n @click=\"setFilter('unresolved')\"\n >\n {{ t.comments.filterUnresolved }}\n </button>\n <button\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"filterMode === 'all' ? 'tpl-comment-filter--active' : ''\"\n :aria-pressed=\"filterMode === 'all'\"\n data-testid=\"comments-filter-all\"\n @click=\"setFilter('all')\"\n >\n {{ t.comments.filterAll }}\n </button>\n <button\n v-if=\"editor.state.selectedBlockId\"\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"filterMode === 'block' ? 'tpl-comment-filter--active' : ''\"\n :aria-pressed=\"filterMode === 'block'\"\n @click=\"setFilter('block', editor.state.selectedBlockId ?? undefined)\"\n >\n {{ t.comments.filterBlock }}\n </button>\n </div>\n\n <!-- Thread list -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto\">\n <!-- Loading state -->\n <div\n v-if=\"isInitialLoad\"\n class=\"tpl:flex tpl:h-full tpl:items-center tpl:justify-center\"\n >\n <LoaderCircle\n class=\"tpl-spinner tpl:text-[var(--tpl-text-muted)]\"\n :size=\"24\"\n :stroke-width=\"2\"\n />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"filteredComments.length === 0\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:px-6 tpl:text-center\"\n >\n <MessageCircle\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 {{\n filterMode === \"all\"\n ? t.comments.noCommentsHint\n : t.comments.noComments\n }}\n </p>\n </div>\n\n <!-- Comment threads -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-3 tpl:p-3\">\n <div\n v-for=\"thread in filteredComments\"\n :key=\"thread.id\"\n class=\"tpl-comment-thread\"\n data-testid=\"comment-thread\"\n >\n <!-- Root comment card -->\n <div\n class=\"tpl-comment-card tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-3\"\n >\n <!-- Comment header -->\n <div\n class=\"tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <span\n class=\"tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n comments.isOwn(thread)\n ? t.comments.ownedByYou\n : thread.author.name\n }}\n </span>\n <span class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\">\n {{ formatTime(thread.createdAt) }}\n </span>\n <span\n v-if=\"thread.updatedAt\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ t.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <!-- Resolve toggle -->\n <button\n v-if=\"comments.canResolve.value\"\n data-testid=\"comment-resolve\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"\n thread.resolvedAt\n ? t.comments.unresolve\n : t.comments.resolve\n \"\n @click=\"handleResolve(thread)\"\n >\n <CircleCheck\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl-resolve-icon\"\n :style=\"{\n color: thread.resolvedAt\n ? 'var(--tpl-primary)'\n : undefined,\n }\"\n />\n </button>\n <!-- Edit (own only) -->\n <button\n v-if=\"canEdit(thread)\"\n data-testid=\"comment-edit\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.edit\"\n @click=\"startEdit(thread)\"\n >\n <Pencil :size=\"12\" :stroke-width=\"2\" />\n </button>\n <!-- Delete (own only) -->\n <button\n v-if=\"canDelete(thread)\"\n data-testid=\"comment-delete\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.delete\"\n @click=\"confirmDelete(thread.id)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Resolved badge -->\n <Transition name=\"tpl-resolve\">\n <div\n v-if=\"thread.resolvedAt\"\n class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1 tpl:text-[10px] tpl:text-[var(--tpl-primary)]\"\n >\n <Check :size=\"10\" :stroke-width=\"2.5\" />\n <span>\n {{\n format(t.comments.resolvedBy, {\n name: thread.resolvedBy?.name ?? \"\",\n })\n }}\n </span>\n </div>\n </Transition>\n\n <!-- Block anchor indicator -->\n <span\n v-if=\"thread.blockId && isBlockMissing(thread.blockId)\"\n class=\"tpl:mt-1 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)]\"\n >\n {{ t.comments.missingBlock }}\n </span>\n <button\n v-else-if=\"thread.blockId\"\n class=\"tpl:mt-1 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"editor.selectBlock(thread.blockId ?? '')\"\n >\n {{ t.comments.jumpToBlock }}\n </button>\n\n <!-- Comment body (or edit mode) -->\n <div v-if=\"editingId === thread.id\" class=\"tpl:mt-2\">\n <textarea\n v-model=\"editBody\"\n class=\"tpl:w-full tpl:resize-none tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:font-sans tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\n rows=\"3\"\n @keydown=\"handleEditKeydown($event, thread.id)\"\n />\n <div class=\"tpl:mt-1.5 tpl:flex tpl:gap-1.5\">\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n :disabled=\"!editBody.trim() || comments.isSubmitting.value\"\n @click=\"handleEdit(thread.id)\"\n >\n {{ t.comments.save }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n <p\n v-else\n class=\"tpl:mt-1.5 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]\"\n >\n {{ thread.body }}\n </p>\n\n <!-- Delete confirmation -->\n <div\n v-if=\"deletingId === thread.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <span class=\"tpl:flex-1\">\n {{ t.comments.deleteConfirm }}\n </span>\n <button\n class=\"tpl:rounded tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-danger)] tpl:text-[var(--tpl-bg)]\"\n data-testid=\"comment-delete-confirm\"\n @click=\"handleDelete(thread.id)\"\n >\n {{ t.comments.delete }}\n </button>\n <button\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelDelete()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n\n <!-- Thread actions -->\n <div\n v-if=\"editingId !== thread.id && deletingId !== thread.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2\"\n >\n <button\n v-if=\"comments.canCreate.value\"\n data-testid=\"comment-reply\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.reply\"\n @click=\"startReply(thread.id)\"\n >\n <Reply\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-primary)]\"\n />\n </button>\n <button\n v-if=\"(thread.replies?.length ?? 0) > 0\"\n class=\"tpl:flex tpl:items-center tpl:gap-0.5 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n @click=\"toggleThread(thread.id)\"\n >\n <template v-if=\"expandedThreads.has(thread.id)\">\n <ChevronUp :size=\"11\" :stroke-width=\"2\" />\n </template>\n <template v-else>\n <ChevronDown :size=\"11\" :stroke-width=\"2\" />\n </template>\n {{\n (thread.replies?.length ?? 0) === 1\n ? format(t.comments.replyOne, {\n count: String(thread.replies?.length ?? 0),\n })\n : format(t.comments.replyMany, {\n count: String(thread.replies?.length ?? 0),\n })\n }}\n </button>\n </div>\n </div>\n\n <!-- Replies -->\n <Transition name=\"tpl-replies\">\n <div\n v-if=\"\n expandedThreads.has(thread.id) &&\n (thread.replies?.length ?? 0) > 0\n \"\n class=\"tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2\"\n >\n <div\n v-for=\"(reply, index) in thread.replies\"\n :key=\"reply.id\"\n class=\"tpl-comment-reply-card tpl:relative tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-2.5\"\n :class=\"\n index < (thread.replies?.length ?? 0) - 1 ? 'tpl:mb-2' : ''\n \"\n >\n <div\n class=\"tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <span\n class=\"tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n comments.isOwn(reply)\n ? t.comments.ownedByYou\n : reply.author.name\n }}\n </span>\n <span\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ formatTime(reply.createdAt) }}\n </span>\n <span\n v-if=\"reply.updatedAt\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ t.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <button\n v-if=\"canEdit(reply)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.edit\"\n @click=\"startEdit(reply)\"\n >\n <Pencil :size=\"11\" :stroke-width=\"2\" />\n </button>\n <button\n v-if=\"canDelete(reply)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.delete\"\n @click=\"confirmDelete(reply.id)\"\n >\n <Trash2 :size=\"11\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Reply body (or edit mode) -->\n <div v-if=\"editingId === reply.id\" class=\"tpl:mt-1.5\">\n <textarea\n v-model=\"editBody\"\n class=\"tpl:w-full tpl:resize-none tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:font-sans tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\n rows=\"2\"\n @keydown=\"handleEditKeydown($event, reply.id)\"\n />\n <div class=\"tpl:mt-1.5 tpl:flex tpl:gap-1.5\">\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n :disabled=\"\n !editBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleEdit(reply.id)\"\n >\n {{ t.comments.save }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n <p\n v-else\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]\"\n >\n {{ reply.body }}\n </p>\n\n <!-- Delete confirmation for reply -->\n <div\n v-if=\"deletingId === reply.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <span class=\"tpl:flex-1\">\n {{ t.comments.deleteConfirm }}\n </span>\n <button\n class=\"tpl:rounded tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-danger)] tpl:text-[var(--tpl-bg)]\"\n data-testid=\"comment-delete-confirm\"\n @click=\"handleDelete(reply.id)\"\n >\n {{ t.comments.delete }}\n </button>\n <button\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelDelete()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n\n <!-- Reply input -->\n <Transition name=\"tpl-replies\">\n <div\n v-if=\"replyingTo === thread.id\"\n class=\"tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2\"\n >\n <!-- Same shape as the new-comment composer below: the border and\n the focus ring belong to the wrapper, the field is bare\n inside it, and the actions sit within the bounds rather than\n floating beside them. The two composers do the same job, so a\n reader should not have to notice they are different\n components. Only the height differs — a reply is nested and\n indented, so it starts at two rows without the composer's\n `min-h`. -->\n <div\n class=\"tpl-comments-input-wrapper tpl-focus-ring-host tpl:flex tpl:items-end tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <textarea\n v-model=\"replyBody\"\n class=\"tpl:max-h-24 tpl:flex-1 tpl:resize-none tpl:border-none tpl:bg-transparent tpl:font-sans tpl:text-xs tpl:outline-none tpl:text-[var(--tpl-text)]\"\n :placeholder=\"t.comments.replyPlaceholder\"\n rows=\"2\"\n @keydown=\"handleReplyKeydown($event, thread.id)\"\n />\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n :disabled=\"!replyBody.trim() || comments.isSubmitting.value\"\n @click=\"handleReply(thread.id)\"\n >\n <Send :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelReply()\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <!-- New comment input. Absent entirely when the store withheld `create` —\n hidden rather than disabled, so a read-only review reads as one rather\n than as a broken composer. -->\n <div\n v-if=\"comments.canCreate.value\"\n class=\"tpl:border-t tpl:p-3 tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"isBlockFilterUnsaved\"\n class=\"tpl:flex tpl:min-h-[68px] tpl:items-center tpl:rounded-md tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)]\"\n >\n {{ t.comments.saveTemplateFirst }}\n </div>\n <div\n v-else\n class=\"tpl-comments-input-wrapper tpl-focus-ring-host tpl:flex tpl:min-h-[68px] tpl:items-end tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <textarea\n ref=\"newCommentInput\"\n v-model=\"newCommentBody\"\n class=\"tpl:max-h-24 tpl:min-h-[48px] tpl:flex-1 tpl:resize-none tpl:border-none tpl:bg-transparent tpl:font-sans tpl:text-xs tpl:outline-none tpl:text-[var(--tpl-text)]\"\n data-testid=\"comments-input\"\n :placeholder=\"t.comments.placeholder\"\n :disabled=\"comments.isSubmitting.value\"\n rows=\"2\"\n @keydown=\"handleNewCommentKeydown\"\n />\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n data-testid=\"comments-send\"\n :disabled=\"!newCommentBody.trim() || comments.isSubmitting.value\"\n @click=\"handleAddComment\"\n >\n <LoaderCircle\n v-if=\"comments.isSubmitting.value\"\n class=\"tpl-spinner\"\n :size=\"16\"\n :stroke-width=\"2\"\n />\n <Send v-else :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-comments-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-comments-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-comments-input-wrapper:focus-within {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-comments-send-btn:not(:disabled):hover {\n transform: scale(1.05);\n}\n\n.tpl-comment-filter {\n color: var(--tpl-text-muted);\n background-color: transparent;\n}\n\n.tpl-comment-filter:hover {\n background-color: var(--tpl-bg-hover);\n color: var(--tpl-text);\n}\n\n.tpl-comment-filter--active {\n background-color: var(--tpl-primary-light);\n color: var(--tpl-primary);\n}\n\n.tpl-comment-action {\n color: var(--tpl-text-dim);\n background-color: transparent;\n}\n\n.tpl-comment-action:hover {\n background-color: var(--tpl-bg-hover);\n color: var(--tpl-text);\n}\n\n.tpl-comment-delete:hover {\n background-color: var(--tpl-danger-light);\n color: var(--tpl-danger);\n}\n\n.tpl-comment-card {\n border-color: var(--tpl-border);\n background-color: var(--tpl-bg);\n}\n\n.tpl-comment-reply-card {\n border-color: var(--tpl-border);\n background-color: var(--tpl-bg);\n}\n\n.tpl-resolve-enter-active,\n.tpl-resolve-leave-active {\n transition:\n opacity 0.3s ease,\n transform 0.3s ease;\n}\n\n.tpl-resolve-enter-from,\n.tpl-resolve-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n.tpl-resolve-icon {\n transition:\n color 0.3s ease,\n transform 0.2s ease;\n}\n\n.tpl-resolve-icon:active {\n transform: scale(1.3);\n}\n\n.tpl-replies-enter-active,\n.tpl-replies-leave-active {\n transition:\n opacity 0.25s ease,\n transform 0.25s ease;\n}\n\n.tpl-replies-enter-from,\n.tpl-replies-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { formatRelativeTime } from \"../utils/formatRelativeTime\";\nimport { EDITOR_KEY, requireInject } from \"../keys\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { UseCommentsFeatureReturn } from \"../composables/useCommentsFeature\";\nimport type { Comment } from \"@templatical/types\";\nimport {\n Check,\n CircleCheck,\n ChevronDown,\n ChevronUp,\n LoaderCircle,\n MessageCircle,\n Pencil,\n Reply,\n Send,\n Trash2,\n X,\n} from \"@lucide/vue\";\nimport { computed, nextTick, ref, watch } from \"vue\";\n\n/**\n * The review panel, **shared by both entry points**.\n *\n * It takes the shared feature as a prop — the `TestEmailPanel` /\n * `SavedBlocksPanels` shape — so the same panel drives a consumer's own store and\n * Cloud's, and its strings live in the OSS chunk.\n *\n * Every write action is gated on the provider having supplied that mutation, and\n * **hidden** rather than disabled when it hasn't: a provider that withholds all\n * four yields a readable review with no way to change anything.\n */\nconst props = defineProps<{\n visible: boolean;\n feature: UseCommentsFeatureReturn;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n}>();\n\nconst { t, format } = useI18n();\nconst editor = requireInject(EDITOR_KEY, \"CommentsSidebar\");\n\nconst comments = computed(() => props.feature.headless);\n\ntype FilterMode = \"all\" | \"unresolved\" | \"block\";\nconst filterMode = ref<FilterMode>(\"unresolved\");\n\nconst newCommentBody = ref(\"\");\nconst replyingTo = ref<string | null>(null);\nconst replyBody = ref(\"\");\nconst editingId = ref<string | null>(null);\nconst editBody = ref(\"\");\nconst expandedThreads = ref<Set<string>>(new Set());\nconst deletingId = ref<string | null>(null);\nconst newCommentInput = ref<HTMLTextAreaElement | null>(null);\n\n/**\n * The block filter lives on the feature, not here: a canvas comment indicator sets\n * it *before* this panel exists (it is lazy), so the target has to survive the gap.\n */\nconst filterBlockId = computed(() => props.feature.filterBlockId.value);\n\n// An indicator click lands a block id on the feature; switching to that filter is\n// this panel's half of the handshake. `immediate` covers the case that opened the\n// panel in the first place, where the id is already set at mount.\nwatch(\n filterBlockId,\n (blockId) => {\n if (blockId) filterMode.value = \"block\";\n },\n { immediate: true },\n);\n\n// `isLoading` alone would flash the skeleton over a perfectly good list every\n// re-open; the pair distinguishes \"fetching, nothing yet\" from \"refreshing\".\nconst isInitialLoad = computed(\n () =>\n comments.value.isLoading.value &&\n comments.value.comments.value.length === 0,\n);\n\nconst allBlockIds = computed<Set<string>>(() => {\n const ids = new Set<string>();\n for (const block of editor.content.value.blocks) {\n ids.add(block.id);\n if (block.type === \"section\") {\n for (const column of block.children) {\n for (const child of column) {\n ids.add(child.id);\n }\n }\n }\n }\n return ids;\n});\n\nfunction isBlockMissing(blockId: string | null | undefined): boolean {\n if (!blockId) {\n return false;\n }\n return !allBlockIds.value.has(blockId);\n}\n\nconst isBlockFilterUnsaved = computed(() => {\n if (filterMode.value !== \"block\" || !filterBlockId.value) {\n return false;\n }\n // Asked through the capability rather than off the editor: a comment anchors to\n // a block in the *saved* template, and only the provider's side knows what that\n // contains. A store that accepts any anchor answers `true` and this never fires.\n return props.feature.capability.isBlockSaved(filterBlockId.value) === false;\n});\n\nconst filteredComments = computed(() => {\n let items = comments.value.comments.value;\n\n if (filterMode.value === \"unresolved\") {\n items = items.filter((c) => !c.resolvedAt);\n } else if (filterMode.value === \"block\" && filterBlockId.value) {\n items = items.filter((c) => c.blockId === filterBlockId.value);\n }\n\n return items;\n});\n\nwatch(\n () => editor.state.selectedBlockId,\n (newBlockId) => {\n if (filterMode.value === \"block\" && newBlockId) {\n props.feature.filterBlockId.value = newBlockId;\n }\n },\n);\n\nfunction setFilter(mode: FilterMode, blockId?: string): void {\n filterMode.value = mode;\n props.feature.filterBlockId.value =\n mode === \"block\" ? (blockId ?? null) : null;\n}\n\nfunction filterByBlock(blockId: string): void {\n setFilter(\"block\", blockId);\n}\n\nfunction toggleThread(threadId: string): void {\n if (expandedThreads.value.has(threadId)) {\n expandedThreads.value.delete(threadId);\n } else {\n expandedThreads.value.add(threadId);\n }\n}\n\nfunction startReply(threadId: string): void {\n replyingTo.value = threadId;\n replyBody.value = \"\";\n editingId.value = null;\n\n if (!expandedThreads.value.has(threadId)) {\n expandedThreads.value.add(threadId);\n }\n}\n\nfunction cancelReply(): void {\n replyingTo.value = null;\n replyBody.value = \"\";\n}\n\nfunction startEdit(comment: Comment): void {\n editingId.value = comment.id;\n editBody.value = comment.body;\n replyingTo.value = null;\n}\n\nfunction cancelEdit(): void {\n editingId.value = null;\n editBody.value = \"\";\n}\n\nfunction confirmDelete(commentId: string): void {\n deletingId.value = commentId;\n}\n\nfunction cancelDelete(): void {\n deletingId.value = null;\n}\n\n/**\n * Every write swallows its rejection. The feature already routed it to `onError`\n * and left the list untouched, and an unhandled rejection out of a DOM event\n * binding helps nobody — same shape as `VersionHistoryPanels`.\n */\nasync function handleAddComment(): Promise<void> {\n const body = newCommentBody.value.trim();\n if (!body || !comments.value.canCreate.value) {\n return;\n }\n\n const blockId =\n filterMode.value === \"block\" ? filterBlockId.value : undefined;\n try {\n await comments.value.create({ body, blockId: blockId ?? undefined });\n newCommentBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleReply(parentId: string): Promise<void> {\n const body = replyBody.value.trim();\n if (!body || !comments.value.canCreate.value) {\n return;\n }\n\n const parent = comments.value.find(parentId);\n try {\n await comments.value.create({\n body,\n blockId: parent?.blockId ?? undefined,\n parentId,\n });\n replyingTo.value = null;\n replyBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleEdit(commentId: string): Promise<void> {\n const body = editBody.value.trim();\n if (!body) {\n return;\n }\n\n try {\n await comments.value.update(commentId, { body });\n editingId.value = null;\n editBody.value = \"\";\n } catch {\n /* reported through onError */\n }\n}\n\nasync function handleDelete(commentId: string): Promise<void> {\n try {\n await comments.value.remove(commentId);\n } catch {\n /* reported through onError */\n }\n deletingId.value = null;\n}\n\nasync function handleResolve(comment: Comment): Promise<void> {\n try {\n // The target state, not a toggle: two clicks in flight can't invert.\n await comments.value.setResolved(comment.id, !comment.resolvedAt);\n } catch {\n /* reported through onError */\n }\n}\n\n/** Editing and deleting are the author's own, and only when the store allows it. */\nfunction canEdit(comment: Comment): boolean {\n return comments.value.canUpdate.value && comments.value.isOwn(comment);\n}\n\nfunction canDelete(comment: Comment): boolean {\n return comments.value.canDelete.value && comments.value.isOwn(comment);\n}\n\nfunction formatTime(dateString: string): string {\n return formatRelativeTime(dateString, t.time) ?? dateString;\n}\n\nfunction handleNewCommentKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleAddComment();\n }\n}\n\nfunction handleReplyKeydown(event: KeyboardEvent, parentId: string): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleReply(parentId);\n }\n}\n\nfunction handleEditKeydown(event: KeyboardEvent, commentId: string): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleEdit(commentId);\n }\n if (event.key === \"Escape\") {\n cancelEdit();\n }\n}\n\nfunction focusNewComment(): void {\n nextTick(() => {\n newCommentInput.value?.focus();\n });\n}\n\ndefineExpose({ filterByBlock, focusNewComment });\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-comments-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-comments-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=\"t.comments.title\"\n data-testid=\"comments-sidebar\"\n class=\"tpl-comments-sidebar 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-text)]\"\n >\n <MessageCircle :size=\"13\" :stroke-width=\"2\" />\n <span>{{ t.comments.title }}</span>\n <span\n v-if=\"feature.unresolvedCount.value > 0\"\n class=\"tpl:ml-1 tpl:inline-flex tpl:size-5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n >\n {{ feature.unresolvedCount.value }}\n </span>\n </div>\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=\"t.comments.close\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Filter bar -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-2\"\n >\n <button\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"\n filterMode === 'unresolved' ? 'tpl-comment-filter--active' : ''\n \"\n :aria-pressed=\"filterMode === 'unresolved'\"\n data-testid=\"comments-filter-unresolved\"\n @click=\"setFilter('unresolved')\"\n >\n {{ t.comments.filterUnresolved }}\n </button>\n <button\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"filterMode === 'all' ? 'tpl-comment-filter--active' : ''\"\n :aria-pressed=\"filterMode === 'all'\"\n data-testid=\"comments-filter-all\"\n @click=\"setFilter('all')\"\n >\n {{ t.comments.filterAll }}\n </button>\n <button\n v-if=\"editor.state.selectedBlockId\"\n type=\"button\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors\"\n :class=\"filterMode === 'block' ? 'tpl-comment-filter--active' : ''\"\n :aria-pressed=\"filterMode === 'block'\"\n @click=\"setFilter('block', editor.state.selectedBlockId ?? undefined)\"\n >\n {{ t.comments.filterBlock }}\n </button>\n </div>\n\n <!-- Thread list -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto\">\n <!-- Loading state -->\n <div\n v-if=\"isInitialLoad\"\n class=\"tpl:flex tpl:h-full tpl:items-center tpl:justify-center\"\n >\n <LoaderCircle\n class=\"tpl-spinner tpl:text-[var(--tpl-text-muted)]\"\n :size=\"24\"\n :stroke-width=\"2\"\n />\n </div>\n\n <!-- Empty state -->\n <div\n v-else-if=\"filteredComments.length === 0\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:px-6 tpl:text-center\"\n >\n <MessageCircle\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 {{\n filterMode === \"all\"\n ? t.comments.noCommentsHint\n : t.comments.noComments\n }}\n </p>\n </div>\n\n <!-- Comment threads -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-3 tpl:p-3\">\n <div\n v-for=\"thread in filteredComments\"\n :key=\"thread.id\"\n class=\"tpl-comment-thread\"\n data-testid=\"comment-thread\"\n >\n <!-- Root comment card -->\n <div\n class=\"tpl-comment-card tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-3\"\n >\n <!-- Comment header -->\n <div\n class=\"tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <span\n class=\"tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n comments.isOwn(thread)\n ? t.comments.ownedByYou\n : thread.author.name\n }}\n </span>\n <span class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\">\n {{ formatTime(thread.createdAt) }}\n </span>\n <span\n v-if=\"thread.updatedAt\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ t.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <!-- Resolve toggle -->\n <button\n v-if=\"comments.canResolve.value\"\n data-testid=\"comment-resolve\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"\n thread.resolvedAt\n ? t.comments.unresolve\n : t.comments.resolve\n \"\n @click=\"handleResolve(thread)\"\n >\n <CircleCheck\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl-resolve-icon\"\n :style=\"{\n color: thread.resolvedAt\n ? 'var(--tpl-primary)'\n : undefined,\n }\"\n />\n </button>\n <!-- Edit (own only) -->\n <button\n v-if=\"canEdit(thread)\"\n data-testid=\"comment-edit\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.edit\"\n @click=\"startEdit(thread)\"\n >\n <Pencil :size=\"12\" :stroke-width=\"2\" />\n </button>\n <!-- Delete (own only) -->\n <button\n v-if=\"canDelete(thread)\"\n data-testid=\"comment-delete\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.delete\"\n @click=\"confirmDelete(thread.id)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Resolved badge -->\n <Transition name=\"tpl-resolve\">\n <div\n v-if=\"thread.resolvedAt\"\n class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1 tpl:text-[10px] tpl:text-[var(--tpl-primary)]\"\n >\n <Check :size=\"10\" :stroke-width=\"2.5\" />\n <span>\n {{\n format(t.comments.resolvedBy, {\n name: thread.resolvedBy?.name ?? \"\",\n })\n }}\n </span>\n </div>\n </Transition>\n\n <!-- Block anchor indicator -->\n <span\n v-if=\"thread.blockId && isBlockMissing(thread.blockId)\"\n class=\"tpl:mt-1 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)]\"\n >\n {{ t.comments.missingBlock }}\n </span>\n <button\n v-else-if=\"thread.blockId\"\n class=\"tpl:mt-1 tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"editor.selectBlock(thread.blockId ?? '')\"\n >\n {{ t.comments.jumpToBlock }}\n </button>\n\n <!-- Comment body (or edit mode) -->\n <div v-if=\"editingId === thread.id\" class=\"tpl:mt-2\">\n <textarea\n v-model=\"editBody\"\n class=\"tpl:w-full tpl:resize-none tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:font-sans tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\n rows=\"3\"\n @keydown=\"handleEditKeydown($event, thread.id)\"\n />\n <div class=\"tpl:mt-1.5 tpl:flex tpl:gap-1.5\">\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n :disabled=\"!editBody.trim() || comments.isSubmitting.value\"\n @click=\"handleEdit(thread.id)\"\n >\n {{ t.comments.save }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n <p\n v-else\n class=\"tpl:mt-1.5 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]\"\n >\n {{ thread.body }}\n </p>\n\n <!-- Delete confirmation -->\n <div\n v-if=\"deletingId === thread.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <span class=\"tpl:flex-1\">\n {{ t.comments.deleteConfirm }}\n </span>\n <button\n class=\"tpl:rounded tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-danger)] tpl:text-[var(--tpl-bg)]\"\n data-testid=\"comment-delete-confirm\"\n @click=\"handleDelete(thread.id)\"\n >\n {{ t.comments.delete }}\n </button>\n <button\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelDelete()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n\n <!-- Thread actions -->\n <div\n v-if=\"editingId !== thread.id && deletingId !== thread.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2\"\n >\n <button\n v-if=\"comments.canCreate.value\"\n data-testid=\"comment-reply\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.reply\"\n @click=\"startReply(thread.id)\"\n >\n <Reply\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-primary)]\"\n />\n </button>\n <button\n v-if=\"(thread.replies?.length ?? 0) > 0\"\n class=\"tpl:flex tpl:items-center tpl:gap-0.5 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n @click=\"toggleThread(thread.id)\"\n >\n <template v-if=\"expandedThreads.has(thread.id)\">\n <ChevronUp :size=\"11\" :stroke-width=\"2\" />\n </template>\n <template v-else>\n <ChevronDown :size=\"11\" :stroke-width=\"2\" />\n </template>\n {{\n (thread.replies?.length ?? 0) === 1\n ? format(t.comments.replyOne, {\n count: String(thread.replies?.length ?? 0),\n })\n : format(t.comments.replyMany, {\n count: String(thread.replies?.length ?? 0),\n })\n }}\n </button>\n </div>\n </div>\n\n <!-- Replies -->\n <Transition name=\"tpl-replies\">\n <div\n v-if=\"\n expandedThreads.has(thread.id) &&\n (thread.replies?.length ?? 0) > 0\n \"\n class=\"tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2\"\n >\n <div\n v-for=\"(reply, index) in thread.replies\"\n :key=\"reply.id\"\n class=\"tpl-comment-reply-card tpl:relative tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-2.5\"\n :class=\"\n index < (thread.replies?.length ?? 0) - 1 ? 'tpl:mb-2' : ''\n \"\n >\n <div\n class=\"tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <span\n class=\"tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{\n comments.isOwn(reply)\n ? t.comments.ownedByYou\n : reply.author.name\n }}\n </span>\n <span\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ formatTime(reply.createdAt) }}\n </span>\n <span\n v-if=\"reply.updatedAt\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ t.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <button\n v-if=\"canEdit(reply)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.edit\"\n @click=\"startEdit(reply)\"\n >\n <Pencil :size=\"11\" :stroke-width=\"2\" />\n </button>\n <button\n v-if=\"canDelete(reply)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors\"\n :title=\"t.comments.delete\"\n @click=\"confirmDelete(reply.id)\"\n >\n <Trash2 :size=\"11\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Reply body (or edit mode) -->\n <div v-if=\"editingId === reply.id\" class=\"tpl:mt-1.5\">\n <textarea\n v-model=\"editBody\"\n class=\"tpl:w-full tpl:resize-none tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:font-sans tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\n rows=\"2\"\n @keydown=\"handleEditKeydown($event, reply.id)\"\n />\n <div class=\"tpl:mt-1.5 tpl:flex tpl:gap-1.5\">\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n :disabled=\"\n !editBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleEdit(reply.id)\"\n >\n {{ t.comments.save }}\n </button>\n <button\n class=\"tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n <p\n v-else\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]\"\n >\n {{ reply.body }}\n </p>\n\n <!-- Delete confirmation for reply -->\n <div\n v-if=\"deletingId === reply.id\"\n class=\"tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <span class=\"tpl:flex-1\">\n {{ t.comments.deleteConfirm }}\n </span>\n <button\n class=\"tpl:rounded tpl:px-2 tpl:py-0.5 tpl:text-xs tpl:font-medium tpl:bg-[var(--tpl-danger)] tpl:text-[var(--tpl-bg)]\"\n data-testid=\"comment-delete-confirm\"\n @click=\"handleDelete(reply.id)\"\n >\n {{ t.comments.delete }}\n </button>\n <button\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelDelete()\"\n >\n {{ t.comments.cancel }}\n </button>\n </div>\n </div>\n </div>\n </Transition>\n\n <!-- Reply input -->\n <Transition name=\"tpl-replies\">\n <div\n v-if=\"replyingTo === thread.id\"\n class=\"tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2\"\n >\n <!-- Same shape as the new-comment composer below: the border and\n the focus ring belong to the wrapper, the field is bare\n inside it, and the actions sit within the bounds rather than\n floating beside them. The two composers do the same job, so a\n reader should not have to notice they are different\n components. Only the height differs — a reply is nested and\n indented, so it starts at two rows without the composer's\n `min-h`. -->\n <div\n class=\"tpl-comments-input-wrapper tpl-focus-ring-host tpl:flex tpl:items-end tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <textarea\n v-model=\"replyBody\"\n class=\"tpl:max-h-24 tpl:flex-1 tpl:resize-none tpl:border-none tpl:bg-transparent tpl:font-sans tpl:text-xs tpl:outline-none tpl:text-[var(--tpl-text)]\"\n :placeholder=\"t.comments.replyPlaceholder\"\n rows=\"2\"\n @keydown=\"handleReplyKeydown($event, thread.id)\"\n />\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n :disabled=\"!replyBody.trim() || comments.isSubmitting.value\"\n @click=\"handleReply(thread.id)\"\n >\n <Send :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelReply()\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <!-- New comment input. Absent entirely when the store withheld `create` —\n hidden rather than disabled, so a read-only review reads as one rather\n than as a broken composer. -->\n <div\n v-if=\"comments.canCreate.value\"\n class=\"tpl:border-t tpl:p-3 tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"isBlockFilterUnsaved\"\n class=\"tpl:flex tpl:min-h-[68px] tpl:items-center tpl:rounded-md tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-warning-light)] tpl:text-[var(--tpl-warning)]\"\n >\n {{ t.comments.saveTemplateFirst }}\n </div>\n <div\n v-else\n class=\"tpl-comments-input-wrapper tpl-focus-ring-host tpl:flex tpl:min-h-[68px] tpl:items-end tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border tpl:px-3 tpl:py-2 tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <textarea\n ref=\"newCommentInput\"\n v-model=\"newCommentBody\"\n class=\"tpl:max-h-24 tpl:min-h-[48px] tpl:flex-1 tpl:resize-none tpl:border-none tpl:bg-transparent tpl:font-sans tpl:text-xs tpl:outline-none tpl:text-[var(--tpl-text)]\"\n data-testid=\"comments-input\"\n :placeholder=\"t.comments.placeholder\"\n :disabled=\"comments.isSubmitting.value\"\n rows=\"2\"\n @keydown=\"handleNewCommentKeydown\"\n />\n <button\n class=\"tpl-comments-send-btn tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-all tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n data-testid=\"comments-send\"\n :disabled=\"!newCommentBody.trim() || comments.isSubmitting.value\"\n @click=\"handleAddComment\"\n >\n <LoaderCircle\n v-if=\"comments.isSubmitting.value\"\n class=\"tpl-spinner\"\n :size=\"16\"\n :stroke-width=\"2\"\n />\n <Send v-else :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-comments-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-comments-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-comments-input-wrapper:focus-within {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-comments-send-btn:not(:disabled):hover {\n transform: scale(1.05);\n}\n\n.tpl-comment-filter {\n color: var(--tpl-text-muted);\n background-color: transparent;\n}\n\n.tpl-comment-filter:hover {\n background-color: var(--tpl-bg-hover);\n color: var(--tpl-text);\n}\n\n.tpl-comment-filter--active {\n background-color: var(--tpl-primary-light);\n color: var(--tpl-primary);\n}\n\n.tpl-comment-action {\n color: var(--tpl-text-dim);\n background-color: transparent;\n}\n\n.tpl-comment-action:hover {\n background-color: var(--tpl-bg-hover);\n color: var(--tpl-text);\n}\n\n.tpl-comment-delete:hover {\n background-color: var(--tpl-danger-light);\n color: var(--tpl-danger);\n}\n\n.tpl-comment-card {\n border-color: var(--tpl-border);\n background-color: var(--tpl-bg);\n}\n\n.tpl-comment-reply-card {\n border-color: var(--tpl-border);\n background-color: var(--tpl-bg);\n}\n\n.tpl-resolve-enter-active,\n.tpl-resolve-leave-active {\n transition:\n opacity 0.3s ease,\n transform 0.3s ease;\n}\n\n.tpl-resolve-enter-from,\n.tpl-resolve-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n\n.tpl-resolve-icon {\n transition:\n color 0.3s ease,\n transform 0.2s ease;\n}\n\n.tpl-resolve-icon:active {\n transform: scale(1.3);\n}\n\n.tpl-replies-enter-active,\n.tpl-replies-leave-active {\n transition:\n opacity 0.25s ease,\n transform 0.25s ease;\n}\n\n.tpl-replies-enter-from,\n.tpl-replies-leave-to {\n opacity: 0;\n transform: translateY(-4px);\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CountdownBlock-sVDjKkRw.js","names":[],"sources":["../../../src/components/blocks/CountdownBlock.vue","../../../src/components/blocks/CountdownBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n CountdownBlock as CountdownBlockType,\n ViewportSize,\n} from \"@templatical/types\";\nimport { useIntervalFn } from \"@vueuse/core\";\nimport { computed, ref } from \"vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\nconst props = defineProps<{\n block: CountdownBlockType;\n viewport: ViewportSize;\n}>();\n\nconst { t } = useI18n();\n\nconst now = ref(Date.now());\nuseIntervalFn(() => {\n now.value = Date.now();\n}, 1000);\n\nconst targetTime = computed(() => {\n if (!props.block.targetDate) return null;\n const date = new Date(props.block.targetDate);\n return isNaN(date.getTime()) ? null : date.getTime();\n});\n\nconst isExpired = computed(() => {\n if (!targetTime.value) return false;\n return now.value >= targetTime.value;\n});\n\nconst remaining = computed(() => {\n if (!targetTime.value || isExpired.value) {\n return { days: 0, hours: 0, minutes: 0, seconds: 0 };\n }\n const diff = targetTime.value - now.value;\n const totalSeconds = Math.floor(diff / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n});\n\nconst segments = computed(() => {\n const result: Array<{ value: string; label: string }> = [];\n if (props.block.showDays) {\n result.push({\n value: String(remaining.value.days).padStart(2, \"0\"),\n label: props.block.labelDays,\n });\n }\n if (props.block.showHours) {\n result.push({\n value: String(remaining.value.hours).padStart(2, \"0\"),\n label: props.block.labelHours,\n });\n }\n if (props.block.showMinutes) {\n result.push({\n value: String(remaining.value.minutes).padStart(2, \"0\"),\n label: props.block.labelMinutes,\n });\n }\n if (props.block.showSeconds) {\n result.push({\n value: String(remaining.value.seconds).padStart(2, \"0\"),\n label: props.block.labelSeconds,\n });\n }\n return result;\n});\n\nconst containerStyle = computed(() => ({\n backgroundColor: props.block.backgroundColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n\nconst digitStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n fontWeight: \"bold\" as const,\n color: props.block.digitColor,\n lineHeight: \"1.2\",\n}));\n\nconst labelStyle = computed(() => ({\n fontSize: `${props.block.labelFontSize}px`,\n color: props.block.labelColor,\n marginTop: \"4px\",\n}));\n\nconst separatorStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n color: props.block.digitColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n</script>\n\n<template>\n <!-- Empty state: no target date -->\n <div\n v-if=\"!block.targetDate\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-8 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.countdown.setDate }}\n </div>\n\n <!-- Expired + hidden -->\n <div\n v-else-if=\"isExpired && block.hideOnExpiry\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-4 tpl:text-xs tpl:text-[var(--tpl-text-dim)] tpl:opacity-50\"\n >\n {{ t.countdown.hidden }}\n </div>\n\n <!-- Expired with message -->\n <div\n v-else-if=\"isExpired\"\n :style=\"containerStyle\"\n class=\"tpl:py-4 tpl:text-center\"\n >\n <span\n :style=\"{\n fontSize: `${block.digitFontSize}px`,\n color: block.digitColor,\n }\"\n >\n {{ block.expiredMessage }}\n </span>\n </div>\n\n <!-- Active countdown -->\n <div\n v-else\n :style=\"containerStyle\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-2 tpl:py-4\"\n >\n <template v-for=\"(segment, index) in segments\" :key=\"segment.label\">\n <span v-if=\"index > 0\" :style=\"separatorStyle\" class=\"tpl:self-start\">\n {{ block.separator }}\n </span>\n <div class=\"tpl:text-center\">\n <div :style=\"digitStyle\">{{ segment.value }}</div>\n <div :style=\"labelStyle\">{{ segment.label }}</div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type {\n CountdownBlock as CountdownBlockType,\n ViewportSize,\n} from \"@templatical/types\";\nimport { useIntervalFn } from \"@vueuse/core\";\nimport { computed, ref } from \"vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\nconst props = defineProps<{\n block: CountdownBlockType;\n viewport: ViewportSize;\n}>();\n\nconst { t } = useI18n();\n\nconst now = ref(Date.now());\nuseIntervalFn(() => {\n now.value = Date.now();\n}, 1000);\n\nconst targetTime = computed(() => {\n if (!props.block.targetDate) return null;\n const date = new Date(props.block.targetDate);\n return isNaN(date.getTime()) ? null : date.getTime();\n});\n\nconst isExpired = computed(() => {\n if (!targetTime.value) return false;\n return now.value >= targetTime.value;\n});\n\nconst remaining = computed(() => {\n if (!targetTime.value || isExpired.value) {\n return { days: 0, hours: 0, minutes: 0, seconds: 0 };\n }\n const diff = targetTime.value - now.value;\n const totalSeconds = Math.floor(diff / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n});\n\nconst segments = computed(() => {\n const result: Array<{ value: string; label: string }> = [];\n if (props.block.showDays) {\n result.push({\n value: String(remaining.value.days).padStart(2, \"0\"),\n label: props.block.labelDays,\n });\n }\n if (props.block.showHours) {\n result.push({\n value: String(remaining.value.hours).padStart(2, \"0\"),\n label: props.block.labelHours,\n });\n }\n if (props.block.showMinutes) {\n result.push({\n value: String(remaining.value.minutes).padStart(2, \"0\"),\n label: props.block.labelMinutes,\n });\n }\n if (props.block.showSeconds) {\n result.push({\n value: String(remaining.value.seconds).padStart(2, \"0\"),\n label: props.block.labelSeconds,\n });\n }\n return result;\n});\n\nconst containerStyle = computed(() => ({\n backgroundColor: props.block.backgroundColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n\nconst digitStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n fontWeight: \"bold\" as const,\n color: props.block.digitColor,\n lineHeight: \"1.2\",\n}));\n\nconst labelStyle = computed(() => ({\n fontSize: `${props.block.labelFontSize}px`,\n color: props.block.labelColor,\n marginTop: \"4px\",\n}));\n\nconst separatorStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n color: props.block.digitColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n</script>\n\n<template>\n <!-- Empty state: no target date -->\n <div\n v-if=\"!block.targetDate\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-8 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.countdown.setDate }}\n </div>\n\n <!-- Expired + hidden -->\n <div\n v-else-if=\"isExpired && block.hideOnExpiry\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-4 tpl:text-xs tpl:text-[var(--tpl-text-dim)] tpl:opacity-50\"\n >\n {{ t.countdown.hidden }}\n </div>\n\n <!-- Expired with message -->\n <div\n v-else-if=\"isExpired\"\n :style=\"containerStyle\"\n class=\"tpl:py-4 tpl:text-center\"\n >\n <span\n :style=\"{\n fontSize: `${block.digitFontSize}px`,\n color: block.digitColor,\n }\"\n >\n {{ block.expiredMessage }}\n </span>\n </div>\n\n <!-- Active countdown -->\n <div\n v-else\n :style=\"containerStyle\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-2 tpl:py-4\"\n >\n <template v-for=\"(segment, index) in segments\" :key=\"segment.label\">\n <span v-if=\"index > 0\" :style=\"separatorStyle\" class=\"tpl:self-start\">\n {{ block.separator }}\n </span>\n <div class=\"tpl:text-center\">\n <div :style=\"digitStyle\">{{ segment.value }}</div>\n <div :style=\"labelStyle\">{{ segment.label }}</div>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"CountdownBlock-sVDjKkRw.js","names":["_openBlock","_createElementBlock","_toDisplayString","_unref","_normalizeStyle","_createElementVNode","_Fragment","_renderList","_createCommentVNode","_sfc_main","renderList","openBlock","ref","defineComponent","unref","computed","createElementBlock","createCommentVNode","normalizeStyle","createBaseVNode","toDisplayString","useIntervalFn","useI18n","_hoisted_1","_hoisted_2","_hoisted_3","CountdownBlock_default","props","__props","now","targetTime","date","isExpired","remaining","diff","totalSeconds","segments","result","containerStyle","digitStyle","labelStyle","separatorStyle","t","Fragment","segment","index"],"sources":["../../../src/components/blocks/CountdownBlock.vue","../../../src/components/blocks/CountdownBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type {\n CountdownBlock as CountdownBlockType,\n ViewportSize,\n} from \"@templatical/types\";\nimport { useIntervalFn } from \"@vueuse/core\";\nimport { computed, ref } from \"vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\nconst props = defineProps<{\n block: CountdownBlockType;\n viewport: ViewportSize;\n}>();\n\nconst { t } = useI18n();\n\nconst now = ref(Date.now());\nuseIntervalFn(() => {\n now.value = Date.now();\n}, 1000);\n\nconst targetTime = computed(() => {\n if (!props.block.targetDate) return null;\n const date = new Date(props.block.targetDate);\n return isNaN(date.getTime()) ? null : date.getTime();\n});\n\nconst isExpired = computed(() => {\n if (!targetTime.value) return false;\n return now.value >= targetTime.value;\n});\n\nconst remaining = computed(() => {\n if (!targetTime.value || isExpired.value) {\n return { days: 0, hours: 0, minutes: 0, seconds: 0 };\n }\n const diff = targetTime.value - now.value;\n const totalSeconds = Math.floor(diff / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n});\n\nconst segments = computed(() => {\n const result: Array<{ value: string; label: string }> = [];\n if (props.block.showDays) {\n result.push({\n value: String(remaining.value.days).padStart(2, \"0\"),\n label: props.block.labelDays,\n });\n }\n if (props.block.showHours) {\n result.push({\n value: String(remaining.value.hours).padStart(2, \"0\"),\n label: props.block.labelHours,\n });\n }\n if (props.block.showMinutes) {\n result.push({\n value: String(remaining.value.minutes).padStart(2, \"0\"),\n label: props.block.labelMinutes,\n });\n }\n if (props.block.showSeconds) {\n result.push({\n value: String(remaining.value.seconds).padStart(2, \"0\"),\n label: props.block.labelSeconds,\n });\n }\n return result;\n});\n\nconst containerStyle = computed(() => ({\n backgroundColor: props.block.backgroundColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n\nconst digitStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n fontWeight: \"bold\" as const,\n color: props.block.digitColor,\n lineHeight: \"1.2\",\n}));\n\nconst labelStyle = computed(() => ({\n fontSize: `${props.block.labelFontSize}px`,\n color: props.block.labelColor,\n marginTop: \"4px\",\n}));\n\nconst separatorStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n color: props.block.digitColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n</script>\n\n<template>\n <!-- Empty state: no target date -->\n <div\n v-if=\"!block.targetDate\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-8 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.countdown.setDate }}\n </div>\n\n <!-- Expired + hidden -->\n <div\n v-else-if=\"isExpired && block.hideOnExpiry\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-4 tpl:text-xs tpl:text-[var(--tpl-text-dim)] tpl:opacity-50\"\n >\n {{ t.countdown.hidden }}\n </div>\n\n <!-- Expired with message -->\n <div\n v-else-if=\"isExpired\"\n :style=\"containerStyle\"\n class=\"tpl:py-4 tpl:text-center\"\n >\n <span\n :style=\"{\n fontSize: `${block.digitFontSize}px`,\n color: block.digitColor,\n }\"\n >\n {{ block.expiredMessage }}\n </span>\n </div>\n\n <!-- Active countdown -->\n <div\n v-else\n :style=\"containerStyle\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-2 tpl:py-4\"\n >\n <template v-for=\"(segment, index) in segments\" :key=\"segment.label\">\n <span v-if=\"index > 0\" :style=\"separatorStyle\" class=\"tpl:self-start\">\n {{ block.separator }}\n </span>\n <div class=\"tpl:text-center\">\n <div :style=\"digitStyle\">{{ segment.value }}</div>\n <div :style=\"labelStyle\">{{ segment.label }}</div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type {\n CountdownBlock as CountdownBlockType,\n ViewportSize,\n} from \"@templatical/types\";\nimport { useIntervalFn } from \"@vueuse/core\";\nimport { computed, ref } from \"vue\";\nimport { useI18n } from \"../../composables/useI18n\";\n\nconst props = defineProps<{\n block: CountdownBlockType;\n viewport: ViewportSize;\n}>();\n\nconst { t } = useI18n();\n\nconst now = ref(Date.now());\nuseIntervalFn(() => {\n now.value = Date.now();\n}, 1000);\n\nconst targetTime = computed(() => {\n if (!props.block.targetDate) return null;\n const date = new Date(props.block.targetDate);\n return isNaN(date.getTime()) ? null : date.getTime();\n});\n\nconst isExpired = computed(() => {\n if (!targetTime.value) return false;\n return now.value >= targetTime.value;\n});\n\nconst remaining = computed(() => {\n if (!targetTime.value || isExpired.value) {\n return { days: 0, hours: 0, minutes: 0, seconds: 0 };\n }\n const diff = targetTime.value - now.value;\n const totalSeconds = Math.floor(diff / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n});\n\nconst segments = computed(() => {\n const result: Array<{ value: string; label: string }> = [];\n if (props.block.showDays) {\n result.push({\n value: String(remaining.value.days).padStart(2, \"0\"),\n label: props.block.labelDays,\n });\n }\n if (props.block.showHours) {\n result.push({\n value: String(remaining.value.hours).padStart(2, \"0\"),\n label: props.block.labelHours,\n });\n }\n if (props.block.showMinutes) {\n result.push({\n value: String(remaining.value.minutes).padStart(2, \"0\"),\n label: props.block.labelMinutes,\n });\n }\n if (props.block.showSeconds) {\n result.push({\n value: String(remaining.value.seconds).padStart(2, \"0\"),\n label: props.block.labelSeconds,\n });\n }\n return result;\n});\n\nconst containerStyle = computed(() => ({\n backgroundColor: props.block.backgroundColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n\nconst digitStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n fontWeight: \"bold\" as const,\n color: props.block.digitColor,\n lineHeight: \"1.2\",\n}));\n\nconst labelStyle = computed(() => ({\n fontSize: `${props.block.labelFontSize}px`,\n color: props.block.labelColor,\n marginTop: \"4px\",\n}));\n\nconst separatorStyle = computed(() => ({\n fontSize: `${props.block.digitFontSize}px`,\n color: props.block.digitColor,\n fontFamily: props.block.fontFamily || \"inherit\",\n}));\n</script>\n\n<template>\n <!-- Empty state: no target date -->\n <div\n v-if=\"!block.targetDate\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-8 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.countdown.setDate }}\n </div>\n\n <!-- Expired + hidden -->\n <div\n v-else-if=\"isExpired && block.hideOnExpiry\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:py-4 tpl:text-xs tpl:text-[var(--tpl-text-dim)] tpl:opacity-50\"\n >\n {{ t.countdown.hidden }}\n </div>\n\n <!-- Expired with message -->\n <div\n v-else-if=\"isExpired\"\n :style=\"containerStyle\"\n class=\"tpl:py-4 tpl:text-center\"\n >\n <span\n :style=\"{\n fontSize: `${block.digitFontSize}px`,\n color: block.digitColor,\n }\"\n >\n {{ block.expiredMessage }}\n </span>\n </div>\n\n <!-- Active countdown -->\n <div\n v-else\n :style=\"containerStyle\"\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:gap-2 tpl:py-4\"\n >\n <template v-for=\"(segment, index) in segments\" :key=\"segment.label\">\n <span v-if=\"index > 0\" :style=\"separatorStyle\" class=\"tpl:self-start\">\n {{ block.separator }}\n </span>\n <div class=\"tpl:text-center\">\n <div :style=\"digitStyle\">{{ segment.value }}</div>\n <div :style=\"labelStyle\">{{ segment.label }}</div>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { F as e, H as t, N as n, S as r, at as i, b as a, ct as o, f as s, g as c, m as l, p as u, u as d, ut as f } from "./draggable-IB4x6w2O.js";
2
2
  import { t as p } from "./useI18n-CyO_Nmca.js";
3
3
  import { p as m } from "./styleConstants-C_YAPdQS.js";
4
- import { c as h } from "./LogicTagInsertButton-3pIbVE9p.js";
5
- import { t as g } from "./ToggleSwitch-niSL2uog.js";
6
- import { i as _, n as v, r as y } from "./Toolbar-tTl0P-rr.js";
4
+ import { c as h } from "./LogicTagInsertButton-CItDS_U1.js";
5
+ import { t as g } from "./ToggleSwitch-D-8dCEbI.js";
6
+ import { i as _, n as v, r as y } from "./Toolbar-BDzhrmGV.js";
7
7
  //#region src/components/toolbar/CountdownToolbar.vue?vue&type=script&setup=true&lang.ts
8
8
  var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-2" }, w = ["value"], T = { value: "" }, E = ["value"], D = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-3" }, O = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-3" }, k = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-2" }, A = [
9
9
  "value",
@@ -196,4 +196,4 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
196
196
  //#endregion
197
197
  export { N as default };
198
198
 
199
- //# sourceMappingURL=CountdownToolbar-BJTcnIQ-.js.map
199
+ //# sourceMappingURL=CountdownToolbar-CIRx5w1U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CountdownToolbar-CIRx5w1U.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","FieldRow","_unref","_withCtx","_createElementVNode","_normalizeClass","_renderList","_toDisplayString","_createBlock","ToggleSwitch","SlidingPillSelect","NumberWithSuffix","ColorPicker","_sfc_main","renderList","withCtx","openBlock","defineComponent","unref","createVNode","normalizeClass","computed","createElementBlock","createBlock","createBaseVNode","Fragment","toDisplayString","useI18n","inputClass","ColorPicker_default","ToggleSwitch_default","SlidingPillSelect_default","FieldRow_default","NumberWithSuffix_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","CountdownToolbar_default","__emit","emit","TIMEZONES","SEPARATORS","UNITS","unitItems","unit","updateField","field","value","t","__props","_cache","$event","tz","item","font"],"sources":["../../../src/components/toolbar/CountdownToolbar.vue","../../../src/components/toolbar/CountdownToolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { A as e, B as t, H as n, N as r, Q as i, S as a, U as ee, at as o, b as s, ct as c, f as te, g as l, h as u, lt as d, m as ne, n as re, o as ie, p as f, ut as p, y as m } from "./draggable-IB4x6w2O.js";
2
2
  import { $ as h, g as ae, i as oe } from "./keys-DCWeHkSF.js";
3
3
  import "./timeouts-DHPZQ0G3.js";
4
- import { Ct as se, Nt as ce, St as g, mt as _, n as v, s as le } from "./icons-Bad3WZ3G.js";
4
+ import { Ct as se, Nt as ce, St as g, mt as _, n as v, s as le } from "./icons-CQnjugjy.js";
5
5
  import { t as ue } from "./LoadingTrack-_7DH_5aw.js";
6
6
  import { t as y } from "./_plugin-vue_export-helper-B3ysoDQm.js";
7
7
  import { n as de } from "./useCloudI18n-CrjWbpbu.js";
@@ -227,4 +227,4 @@ var pe = ["aria-label"], me = { class: "tpl:flex tpl:items-center tpl:justify-be
227
227
  //#endregion
228
228
  export { R as default };
229
229
 
230
- //# sourceMappingURL=DesignReferenceSidebar-Dl3eEGtS.js.map
230
+ //# sourceMappingURL=DesignReferenceSidebar-C1-SbE_R.js.map