@templatical/editor 0.20.0 → 0.21.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.
- package/README.md +1 -0
- package/dist/{AiChatSidebar-ChrE6pUq.js → AiChatSidebar-K_AehWc-.js} +9 -9
- package/dist/{AiFeatureMenu-DkQ9HhQ6.js → AiFeatureMenu-CcqQDHzJ.js} +6 -6
- package/dist/{BlockIssueBadge-B4QjePax.js → BlockIssueBadge-Dmj9rVra.js} +4 -4
- package/dist/{CloudEditor-CfX-aMiV.js → CloudEditor-Dnww6og-.js} +263 -315
- package/dist/{CollaboratorBar-DTibhQ1j.js → CollaboratorBar-Bj4NZiie.js} +3 -3
- package/dist/{ColorPicker-4vDYhdGe.js → ColorPicker-s9GHXjZs.js} +4 -4
- package/dist/CommentsSidebar-DvNEdugN.js +435 -0
- package/dist/CountdownBlock-J_Q9JRQM.js +91 -0
- package/dist/{CountdownToolbar-D7yQqnA2.js → CountdownToolbar-DPRcv3Fu.js} +3 -4
- package/dist/{DesignReferenceSidebar-DnO6ZdwS.js → DesignReferenceSidebar-DNmK3dxp.js} +8 -8
- package/dist/{IssuesPanel-X_vkNGrs.js → IssuesPanel-CEnEGFtd.js} +7 -7
- package/dist/{MergeTagSuggestion-DCuaSOPQ.js → MergeTagSuggestion-DMe4rqn0.js} +2 -2
- package/dist/NumberWithSuffix-CCPN_oCh.js +121 -0
- package/dist/{ParagraphEditor-C5ixOIYb.js → ParagraphEditor--_zWfxps.js} +47 -47
- package/dist/{RichTextEditorContent-DwhDnMsG.js → RichTextEditorContent-FbnWsh46.js} +6 -6
- package/dist/SaveBlockDialog-CYIEHABH.js +264 -0
- package/dist/SavedBlockPreviewCanvas-2-mzAnZE.js +109 -0
- package/dist/SavedBlocksBrowserModal-D5V5yF_3.js +323 -0
- package/dist/SavedBlocksPanels-Dff38YXu.js +36 -0
- package/dist/SavedBlocksPickBar-rB6iyji6.js +52 -0
- package/dist/{SnapshotHistory-DaVueFv6.js → SnapshotHistory-FtzDq8f0.js} +8 -8
- package/dist/{TemplateScoringPanel-B0UHoTLx.js → TemplateScoringPanel-ifGGGWs5.js} +14 -14
- package/dist/{TestEmailModal-BkauxF7j.js → TestEmailModal-DL2O1H8M.js} +4 -4
- package/dist/{TitleEditor-n6WDVDjW.js → TitleEditor-CPda5lh-.js} +11 -11
- package/dist/{TplModal-BDDyvT-C.js → TplModal-C7Koj5gM.js} +3 -3
- package/dist/{blockTypeIcons-4D0aFXNg.js → blockTypeIcons-CgwECCut.js} +6 -6
- package/dist/bundle-stats.json +8 -8
- package/dist/{cdn/chunks/ca-DbWoy4rG.js → ca-BBgad6y5.js} +50 -5
- package/dist/cdn/chunks/AiChatSidebar-A1CuWGY0.js +203 -0
- package/dist/cdn/chunks/AiChatSidebar-A1CuWGY0.js.map +1 -0
- package/dist/cdn/chunks/{AiFeatureMenu-CIBHAGSz.js → AiFeatureMenu-Duwg413F.js} +12 -8
- package/dist/cdn/chunks/{AiFeatureMenu-CIBHAGSz.js.map → AiFeatureMenu-Duwg413F.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-CxMiLo3p.js → BlockIssueBadge-Da4yR-mh.js} +7 -5
- package/dist/cdn/chunks/{BlockIssueBadge-CxMiLo3p.js.map → BlockIssueBadge-Da4yR-mh.js.map} +1 -1
- package/dist/cdn/chunks/{CloudEditor-CEa3Xgqh.js → CloudEditor-B3hqbwfP.js} +275 -318
- package/dist/cdn/chunks/CloudEditor-B3hqbwfP.js.map +1 -0
- package/dist/cdn/chunks/{CollaboratorBar-Bk1gD02G.js → CollaboratorBar-BS3rJy6-.js} +7 -6
- package/dist/cdn/chunks/{CollaboratorBar-Bk1gD02G.js.map → CollaboratorBar-BS3rJy6-.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-ll8pynws.js → ColorPicker-DZSzZ5ZP.js} +168 -164
- package/dist/cdn/chunks/{ColorPicker-ll8pynws.js.map → ColorPicker-DZSzZ5ZP.js.map} +1 -1
- package/dist/cdn/chunks/CommentsSidebar-DHdRiGPX.js +412 -0
- package/dist/cdn/chunks/CommentsSidebar-DHdRiGPX.js.map +1 -0
- package/dist/cdn/chunks/CountdownBlock-Bffpoppw.js +93 -0
- package/dist/cdn/chunks/{CountdownBlock-eGUE7hhw.js.map → CountdownBlock-Bffpoppw.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-C1bkSjUR.js → CountdownToolbar-DAnmIZjq.js} +4 -5
- package/dist/cdn/chunks/{CountdownToolbar-C1bkSjUR.js.map → CountdownToolbar-DAnmIZjq.js.map} +1 -1
- package/dist/cdn/chunks/DesignReferenceSidebar-Dre99DTH.js +223 -0
- package/dist/cdn/chunks/DesignReferenceSidebar-Dre99DTH.js.map +1 -0
- package/dist/cdn/chunks/{IssuesPanel-Csasylf_.js → IssuesPanel-D80JedOj.js} +11 -10
- package/dist/cdn/chunks/{IssuesPanel-Csasylf_.js.map → IssuesPanel-D80JedOj.js.map} +1 -1
- package/dist/cdn/chunks/LoadingTrack-D-CW2tUa.js +12 -0
- package/dist/cdn/chunks/LoadingTrack-D-CW2tUa.js.map +1 -0
- package/dist/cdn/chunks/{MergeTagSuggestion-BmMmdcRg.js → MergeTagSuggestion-D7TsECll.js} +2 -2
- package/dist/cdn/chunks/{MergeTagSuggestion-BmMmdcRg.js.map → MergeTagSuggestion-D7TsECll.js.map} +1 -1
- package/dist/cdn/chunks/NumberWithSuffix-CjlVNIEe.js +123 -0
- package/dist/cdn/chunks/NumberWithSuffix-CjlVNIEe.js.map +1 -0
- package/dist/cdn/chunks/{ParagraphEditor-v0_XEL-v.js → ParagraphEditor-CtaRa1vr.js} +90 -85
- package/dist/cdn/chunks/{ParagraphEditor-v0_XEL-v.js.map → ParagraphEditor-CtaRa1vr.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-NQExNsaV.js → RichTextEditorContent-BbdgJqWM.js} +9 -6
- package/dist/cdn/chunks/{RichTextEditorContent-NQExNsaV.js.map → RichTextEditorContent-BbdgJqWM.js.map} +1 -1
- package/dist/cdn/chunks/SaveBlockDialog-Bb3fdUEZ.js +263 -0
- package/dist/cdn/chunks/SaveBlockDialog-Bb3fdUEZ.js.map +1 -0
- package/dist/cdn/chunks/SavedBlockPreviewCanvas-DZOMgNsz.js +111 -0
- package/dist/cdn/chunks/SavedBlockPreviewCanvas-DZOMgNsz.js.map +1 -0
- package/dist/cdn/chunks/SavedBlocksBrowserModal-CeUWcPv0.js +322 -0
- package/dist/cdn/chunks/SavedBlocksBrowserModal-CeUWcPv0.js.map +1 -0
- package/dist/cdn/chunks/SavedBlocksPanels-D-pA_wfk.js +38 -0
- package/dist/cdn/chunks/SavedBlocksPanels-D-pA_wfk.js.map +1 -0
- package/dist/cdn/chunks/SavedBlocksPickBar-1rf1KpqS.js +54 -0
- package/dist/cdn/chunks/SavedBlocksPickBar-1rf1KpqS.js.map +1 -0
- package/dist/cdn/chunks/SnapshotHistory-Bf1uovti.js +121 -0
- package/dist/cdn/chunks/SnapshotHistory-Bf1uovti.js.map +1 -0
- package/dist/cdn/chunks/TemplateScoringPanel-CTk8WwxU.js +231 -0
- package/dist/cdn/chunks/TemplateScoringPanel-CTk8WwxU.js.map +1 -0
- package/dist/cdn/chunks/TestEmailModal-BJTDxFHh.js +96 -0
- package/dist/cdn/chunks/TestEmailModal-BJTDxFHh.js.map +1 -0
- package/dist/cdn/chunks/{TitleEditor-CS-_MzYs.js → TitleEditor-BJ7LFykI.js} +18 -15
- package/dist/cdn/chunks/{TitleEditor-CS-_MzYs.js.map → TitleEditor-BJ7LFykI.js.map} +1 -1
- package/dist/cdn/chunks/TplModal-CH9RzKF7.js +50 -0
- package/dist/cdn/chunks/TplModal-CH9RzKF7.js.map +1 -0
- package/dist/cdn/chunks/_plugin-vue_export-helper-B3ysoDQm.js +8 -0
- package/dist/cdn/chunks/blockTypeIcons-etMMQoVv.js +21 -0
- package/dist/cdn/chunks/{blockTypeIcons-B0Hjh42a.js.map → blockTypeIcons-etMMQoVv.js.map} +1 -1
- package/dist/{ca-DbWoy4rG.js → cdn/chunks/ca-BBgad6y5.js} +52 -3
- package/dist/cdn/chunks/ca-BBgad6y5.js.map +1 -0
- package/dist/cdn/chunks/cloud-BJlbXGTJ.js +1787 -0
- package/dist/cdn/chunks/cloud-BJlbXGTJ.js.map +1 -0
- package/dist/{de-CTYXVU9v.js → cdn/chunks/de-Bw6fB5JU.js} +2 -24
- package/dist/cdn/chunks/{de-CTYXVU9v.js.map → de-Bw6fB5JU.js.map} +1 -1
- package/dist/cdn/chunks/{de-CgGOnerg.js → de-D7WbpFBR.js} +50 -3
- package/dist/cdn/chunks/de-D7WbpFBR.js.map +1 -0
- package/dist/cdn/chunks/dist-CiBEt0VR.js +163 -0
- package/dist/cdn/chunks/dist-CiBEt0VR.js.map +1 -0
- package/dist/cdn/chunks/dist-CxTt8FRE.js +403 -0
- package/dist/cdn/chunks/dist-CxTt8FRE.js.map +1 -0
- package/dist/{en-DrUV9xth.js → cdn/chunks/en-BrZ0uGK2.js} +51 -2
- package/dist/cdn/chunks/en-BrZ0uGK2.js.map +1 -0
- package/dist/{en-BtmIaJeb.js → cdn/chunks/en-DAdHJz7h.js} +2 -24
- package/dist/cdn/chunks/{en-BtmIaJeb.js.map → en-DAdHJz7h.js.map} +1 -1
- package/dist/{es-DwbKjU7q.js → cdn/chunks/es-tLSLysPs.js} +51 -2
- package/dist/cdn/chunks/es-tLSLysPs.js.map +1 -0
- package/dist/cdn/chunks/{extensions-Di3hdIRT.js → extensions-ZX2oarW6.js} +28 -26
- package/dist/cdn/chunks/{extensions-Di3hdIRT.js.map → extensions-ZX2oarW6.js.map} +1 -1
- package/dist/cdn/chunks/formatRelativeTime-CtUU-QZ8.js +12 -0
- package/dist/cdn/chunks/formatRelativeTime-CtUU-QZ8.js.map +1 -0
- package/dist/cdn/chunks/htmlparser-CZa2V5cF.js +1008 -0
- package/dist/cdn/chunks/htmlparser-CZa2V5cF.js.map +1 -0
- package/dist/cdn/chunks/icons-CYWw8Xfp.js +1190 -0
- package/dist/cdn/chunks/icons-CYWw8Xfp.js.map +1 -0
- package/dist/cdn/chunks/keys-D7kUmfpE.js +12 -0
- package/dist/cdn/chunks/keys-D7kUmfpE.js.map +1 -0
- package/dist/cdn/chunks/{pt-BR-Bcc5Tjed.js → pt-BR-BWFokdTU.js} +50 -3
- package/dist/cdn/chunks/pt-BR-BWFokdTU.js.map +1 -0
- package/dist/{pt-BR-B2ZvFlRL.js → cdn/chunks/pt-BR-w2sGzHNX.js} +2 -24
- package/dist/cdn/chunks/{pt-BR-B2ZvFlRL.js.map → pt-BR-w2sGzHNX.js.map} +1 -1
- package/dist/cdn/chunks/src-BKI1x1k7.js +546 -0
- package/dist/cdn/chunks/src-BKI1x1k7.js.map +1 -0
- package/dist/cdn/chunks/src-CM0gCvAS.js +1176 -0
- package/dist/cdn/chunks/src-CM0gCvAS.js.map +1 -0
- package/dist/cdn/chunks/{media-library-yYAkAagU.js → src-D6F_3XkE.js} +1754 -1252
- package/dist/cdn/chunks/src-D6F_3XkE.js.map +1 -0
- package/dist/cdn/chunks/{renderer-DpBGiXOg.js → src-DYji-wXB.js} +117 -133
- package/dist/cdn/chunks/src-DYji-wXB.js.map +1 -0
- package/dist/cdn/chunks/{styles-DHSmjlpX.js → styles-DMOEiSX9.js} +1448 -1354
- package/dist/cdn/chunks/styles-DMOEiSX9.js.map +1 -0
- package/dist/cdn/chunks/timeouts-Bmi_yePw.js +6 -0
- package/dist/cdn/chunks/timeouts-Bmi_yePw.js.map +1 -0
- package/dist/cdn/chunks/{tiptap-C2indasq.js → tiptap-BMZ-OENR.js} +2012 -1863
- package/dist/cdn/chunks/tiptap-BMZ-OENR.js.map +1 -0
- package/dist/cdn/chunks/useAliveFlag-aanU8uqB.js +12 -0
- package/dist/cdn/chunks/useAliveFlag-aanU8uqB.js.map +1 -0
- package/dist/cdn/chunks/useCloudI18n-BjNENqkM.js +25 -0
- package/dist/cdn/chunks/useCloudI18n-BjNENqkM.js.map +1 -0
- package/dist/cdn/chunks/useEditorCore-BA6mspzD.js +3019 -0
- package/dist/cdn/chunks/useEditorCore-BA6mspzD.js.map +1 -0
- package/dist/cdn/chunks/useI18n-8Wd7G-cq.js +19 -0
- package/dist/cdn/chunks/useI18n-8Wd7G-cq.js.map +1 -0
- package/dist/cdn/chunks/useMergeTag-C7G31KV-.js +46 -0
- package/dist/cdn/chunks/useMergeTag-C7G31KV-.js.map +1 -0
- package/dist/cdn/chunks/usePopoverPosition-Bx0QewTv.js +61 -0
- package/dist/cdn/chunks/usePopoverPosition-Bx0QewTv.js.map +1 -0
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +132 -120
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-Bl4J_tYV.js → check-BpQDKiPz.js} +1 -1
- package/dist/{chevron-down-COEcl2iP.js → chevron-down-j8GrdTyz.js} +1 -1
- package/dist/chevron-up-BACyVhyC.js +7 -0
- package/dist/{circle-alert-CvHi5etc.js → circle-alert-CgtE46ND.js} +1 -1
- package/dist/{clock-CZZw62e8.js → clock-BzKLLW5L.js} +1 -1
- package/dist/{cloud-B-RebwxI.js → cloud-Du7mnkz2.js} +19 -47
- package/dist/{createLucideIcon-_peTZQtY.js → createLucideIcon-JWHwTI6N.js} +2 -2
- package/dist/{cdn/chunks/de-CTYXVU9v.js → de-Bw6fB5JU.js} +0 -26
- package/dist/{de-CgGOnerg.js → de-D7WbpFBR.js} +49 -2
- package/dist/{dist-CgTKuIUN.js → dist-BEpCh2mG.js} +1158 -1125
- package/dist/{dist-Cx7IUM7J.js → dist-BgPYC_8-.js} +14 -6
- package/dist/{dist-BKKo5wPC.js → dist-BkM8A17N.js} +2 -2
- package/dist/{dist-CcX5sLpz.js → dist-BuNDAAVp.js} +361 -256
- package/dist/{dist-COSN3oDJ.js → dist-CBzCU-a-.js} +2 -2
- package/dist/{dist-YSCPD13F.js → dist-CDO52Rxy.js} +2 -2
- package/dist/{dist-Dk5AdwoQ.js → dist-CrjzhIxB.js} +2 -2
- package/dist/{dist-DXAZUjj3.js → dist-CtcgoC_R.js} +2 -2
- package/dist/{dist-D6M5FAY7.js → dist-CuXha2_7.js} +2 -2
- package/dist/dist-CweLDZrs.js +5 -0
- package/dist/dist-DOsdqAEZ.js +5 -0
- package/dist/{dist-8TKf-Jpg.js → dist-DYClLqy8.js} +6 -3
- package/dist/{cdn/chunks/en-DrUV9xth.js → en-BrZ0uGK2.js} +49 -4
- package/dist/{cdn/chunks/en-BtmIaJeb.js → en-DAdHJz7h.js} +0 -26
- package/dist/{cdn/chunks/es-DwbKjU7q.js → es-tLSLysPs.js} +49 -4
- package/dist/{extensions-BCA9pyer.js → extensions-ClY5Sh7S.js} +5 -5
- package/dist/{image-up-D26rNdfX.js → image-up-CQdtq8ge.js} +1 -1
- package/dist/index.d.ts +81 -4
- package/dist/{info-CE3JSTqk.js → info-BUJBXoE6.js} +1 -1
- package/dist/{keys-DFaIbGR8.js → keys-CHPcp-Nz.js} +2 -2
- package/dist/{list-checks-BQsLrtBB.js → list-checks-BGHaCSD5.js} +1 -1
- package/dist/{loader-circle-s9Rh5lLd.js → loader-circle-C-Z6kgI6.js} +1 -1
- package/dist/{message-circle-CI62d690.js → message-circle-CEm5qckV.js} +1 -1
- package/dist/pencil-DdwmkKLb.js +10 -0
- package/dist/{pt-BR-Bcc5Tjed.js → pt-BR-BWFokdTU.js} +49 -2
- package/dist/{cdn/chunks/pt-BR-B2ZvFlRL.js → pt-BR-w2sGzHNX.js} +0 -26
- package/dist/{refresh-cw-DlYfl65Y.js → refresh-cw-maKlg0dl.js} +1 -1
- package/dist/{scan-line-CHadrYVb.js → scan-line-BFgesTOY.js} +1 -1
- package/dist/{send-CbP40otC.js → send-B70aQDrA.js} +1 -1
- package/dist/{shield-check-Bnj9aRoJ.js → shield-check-B7x5OKt5.js} +1 -1
- package/dist/{sparkles-CncWT57a.js → sparkles-DVlm1Ipp.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{styles-DkF_-uX8.js → styles-PcOPXvLO.js} +1235 -1151
- package/dist/templatical-editor.js +134 -125
- package/dist/{text-align-start-B458vzYw.js → text-align-start-Vay3Kvo2.js} +1 -1
- package/dist/{trash-2-DO0yjSU1.js → trash-2-DE8CmxAG.js} +1 -1
- package/dist/{triangle-alert-C18_ima6.js → triangle-alert-Djg6IG8N.js} +1 -1
- package/dist/{upload-DsCd2zTR.js → upload-BwwE0U6G.js} +1 -1
- package/dist/{useCloudI18n-PCDOC2kz.js → useCloudI18n-BmiygEM4.js} +1 -1
- package/dist/{useEditorCore-kKUemYY6.js → useEditorCore-CT1slufs.js} +997 -817
- package/dist/{useI18n-BNMzZTM5.js → useI18n-D2L9Mkik.js} +1 -1
- package/dist/{useMergeTag-DfEY0eK5.js → useMergeTag-BTdDq5DC.js} +1 -1
- package/dist/{usePopoverPosition-F9STHRIr.js → usePopoverPosition-BO2LNy5n.js} +1 -1
- package/dist/{x-BCFQuE_q.js → x-CkD6A1iB.js} +1 -1
- package/package.json +21 -21
- package/dist/CommentsSidebar-D-o5Stlg.js +0 -442
- package/dist/CountdownBlock-Bn5w7C-p.js +0 -92
- package/dist/ModuleBrowserModal-AJt--JPh.js +0 -202
- package/dist/ModulePreviewCanvas-BKYOcVCh.js +0 -106
- package/dist/NumberWithSuffix-Bf3rNxza.js +0 -89
- package/dist/SaveModuleDialog-B4QX9eiv.js +0 -125
- package/dist/ToggleSwitch-DUNuUPsZ.js +0 -37
- package/dist/cdn/chunks/CloudEditor-CEa3Xgqh.js.map +0 -1
- package/dist/cdn/chunks/CountdownBlock-eGUE7hhw.js +0 -93
- package/dist/cdn/chunks/ModuleBrowserModal-DznWWtBI.js +0 -198
- package/dist/cdn/chunks/ModuleBrowserModal-DznWWtBI.js.map +0 -1
- package/dist/cdn/chunks/ModulePreviewCanvas-D-h7fr87.js +0 -107
- package/dist/cdn/chunks/ModulePreviewCanvas-D-h7fr87.js.map +0 -1
- package/dist/cdn/chunks/NumberWithSuffix-Dt-0odUN.js +0 -91
- package/dist/cdn/chunks/NumberWithSuffix-Dt-0odUN.js.map +0 -1
- package/dist/cdn/chunks/SaveModuleDialog-BpFhTi-k.js +0 -120
- package/dist/cdn/chunks/SaveModuleDialog-BpFhTi-k.js.map +0 -1
- package/dist/cdn/chunks/ToggleSwitch-OrmX_w18.js +0 -39
- package/dist/cdn/chunks/ToggleSwitch-OrmX_w18.js.map +0 -1
- package/dist/cdn/chunks/blockTypeIcons-B0Hjh42a.js +0 -22
- package/dist/cdn/chunks/ca-DbWoy4rG.js.map +0 -1
- package/dist/cdn/chunks/de-CgGOnerg.js.map +0 -1
- package/dist/cdn/chunks/en-DrUV9xth.js.map +0 -1
- package/dist/cdn/chunks/es-DwbKjU7q.js.map +0 -1
- package/dist/cdn/chunks/features-CLIZ7Q-Y.js +0 -7657
- package/dist/cdn/chunks/features-CLIZ7Q-Y.js.map +0 -1
- package/dist/cdn/chunks/icons-GX40Qm2W.js +0 -690
- package/dist/cdn/chunks/icons-GX40Qm2W.js.map +0 -1
- package/dist/cdn/chunks/media-library-yYAkAagU.js.map +0 -1
- package/dist/cdn/chunks/pt-BR-Bcc5Tjed.js.map +0 -1
- package/dist/cdn/chunks/quality-DffYv9j5.js +0 -2180
- package/dist/cdn/chunks/quality-DffYv9j5.js.map +0 -1
- package/dist/cdn/chunks/renderer-DpBGiXOg.js.map +0 -1
- package/dist/cdn/chunks/src-9K6uFiuK.js +0 -509
- package/dist/cdn/chunks/src-9K6uFiuK.js.map +0 -1
- package/dist/cdn/chunks/styles-DHSmjlpX.js.map +0 -1
- package/dist/cdn/chunks/tiptap-C2indasq.js.map +0 -1
- package/dist/dist-BDVm2MrW.js +0 -5
- package/dist/dist-DbekZDmJ.js +0 -5
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { H as e, M as t, O as n, P as r, V as i, Z as a, ct as o, f as s, g as c, h as l, it as u, m as d, n as f, o as p, ot as m, p as h, st as ee, u as g, v as te, x as _, y as v, z as y } from "./draggable-D840DIZB.js";
|
|
2
|
+
import { B as b, h as ne, r as re, u as ie } from "./keys-D7kUmfpE.js";
|
|
3
|
+
import { t as ae } from "./useI18n-8Wd7G-cq.js";
|
|
4
|
+
import { Ft as oe, G as x, I as se, Pt as ce, d as S, et as C, j as w, jt as le, kt as ue, n as de, rt as T } from "./icons-CYWw8Xfp.js";
|
|
5
|
+
import { t as E } from "./_plugin-vue_export-helper-B3ysoDQm.js";
|
|
6
|
+
import { n as fe } from "./useCloudI18n-BjNENqkM.js";
|
|
7
|
+
import { t as pe } from "./formatRelativeTime-CtUU-QZ8.js";
|
|
8
|
+
//#region src/cloud/components/CommentsSidebar.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
+
var me = {
|
|
10
|
+
key: 0,
|
|
11
|
+
class: "tpl-comments-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]"
|
|
12
|
+
}, he = { class: "tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3" }, ge = { class: "tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]" }, _e = {
|
|
13
|
+
key: 0,
|
|
14
|
+
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-bg)]"
|
|
15
|
+
}, ve = { class: "tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-2" }, ye = { class: "tpl:flex-1 tpl:overflow-y-auto" }, be = {
|
|
16
|
+
key: 0,
|
|
17
|
+
class: "tpl:flex tpl:h-full tpl:items-center tpl:justify-center"
|
|
18
|
+
}, xe = {
|
|
19
|
+
key: 1,
|
|
20
|
+
class: "tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:px-6 tpl:text-center"
|
|
21
|
+
}, Se = { class: "tpl:max-w-[240px] tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Ce = {
|
|
22
|
+
key: 2,
|
|
23
|
+
class: "tpl:flex tpl:flex-col tpl:gap-3 tpl:p-3"
|
|
24
|
+
}, we = { class: "tpl-comment-card tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-3" }, Te = { class: "tpl:flex tpl:items-start tpl:justify-between tpl:gap-2" }, Ee = { class: "tpl:flex tpl:items-center tpl:gap-1.5" }, De = { class: "tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Oe = { class: "tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, ke = {
|
|
25
|
+
key: 0,
|
|
26
|
+
class: "tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]"
|
|
27
|
+
}, Ae = { class: "tpl:flex tpl:items-center tpl:gap-0.5" }, je = ["title", "onClick"], Me = ["title", "onClick"], Ne = ["title", "onClick"], Pe = {
|
|
28
|
+
key: 0,
|
|
29
|
+
class: "tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1 tpl:text-[10px] tpl:text-[var(--tpl-primary)]"
|
|
30
|
+
}, Fe = {
|
|
31
|
+
key: 0,
|
|
32
|
+
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)]"
|
|
33
|
+
}, Ie = ["onClick"], Le = {
|
|
34
|
+
key: 2,
|
|
35
|
+
class: "tpl:mt-2"
|
|
36
|
+
}, Re = ["onKeydown"], ze = { class: "tpl:mt-1.5 tpl:flex tpl:gap-1.5" }, Be = ["disabled", "onClick"], Ve = {
|
|
37
|
+
key: 3,
|
|
38
|
+
class: "tpl:mt-1.5 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]"
|
|
39
|
+
}, He = {
|
|
40
|
+
key: 4,
|
|
41
|
+
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)]"
|
|
42
|
+
}, Ue = { class: "tpl:flex-1" }, We = ["onClick"], Ge = {
|
|
43
|
+
key: 5,
|
|
44
|
+
class: "tpl:mt-2 tpl:flex tpl:items-center tpl:gap-2"
|
|
45
|
+
}, Ke = ["title", "onClick"], qe = ["onClick"], Je = {
|
|
46
|
+
key: 0,
|
|
47
|
+
class: "tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2"
|
|
48
|
+
}, Ye = { class: "tpl:flex tpl:items-start tpl:justify-between tpl:gap-2" }, Xe = { class: "tpl:flex tpl:items-center tpl:gap-1.5" }, Ze = { class: "tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Qe = { class: "tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, $e = {
|
|
49
|
+
key: 0,
|
|
50
|
+
class: "tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]"
|
|
51
|
+
}, et = { class: "tpl:flex tpl:items-center tpl:gap-0.5" }, tt = ["title", "onClick"], nt = ["title", "onClick"], rt = {
|
|
52
|
+
key: 0,
|
|
53
|
+
class: "tpl:mt-1.5"
|
|
54
|
+
}, it = ["onKeydown"], at = { class: "tpl:mt-1.5 tpl:flex tpl:gap-1.5" }, ot = ["disabled", "onClick"], st = {
|
|
55
|
+
key: 1,
|
|
56
|
+
class: "tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:whitespace-pre-wrap tpl:text-[var(--tpl-text)]"
|
|
57
|
+
}, ct = {
|
|
58
|
+
key: 2,
|
|
59
|
+
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)]"
|
|
60
|
+
}, lt = { class: "tpl:flex-1" }, ut = ["onClick"], dt = {
|
|
61
|
+
key: 0,
|
|
62
|
+
class: "tpl-comment-replies tpl:ml-5 tpl:pl-3 tpl:pt-2"
|
|
63
|
+
}, ft = { class: "tpl:flex tpl:items-end tpl:gap-2" }, pt = ["placeholder", "onKeydown"], mt = { class: "tpl:flex tpl:shrink-0 tpl:flex-col tpl:gap-1" }, ht = ["disabled", "onClick"], gt = { class: "tpl:border-t tpl:p-3 tpl:border-[var(--tpl-border)]" }, _t = {
|
|
64
|
+
key: 0,
|
|
65
|
+
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)]"
|
|
66
|
+
}, vt = {
|
|
67
|
+
key: 1,
|
|
68
|
+
class: "tpl-comments-input-wrapper 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)]"
|
|
69
|
+
}, yt = ["placeholder", "disabled"], bt = ["disabled"], D = /*#__PURE__*/ E(/* @__PURE__ */ _({
|
|
70
|
+
__name: "CommentsSidebar",
|
|
71
|
+
props: { visible: { type: Boolean } },
|
|
72
|
+
emits: ["close", "filterBlock"],
|
|
73
|
+
setup(_, { expose: E, emit: D }) {
|
|
74
|
+
let xt = _, O = D, { format: k } = ae(), { t: A } = fe(), j = b(ne, "CommentsSidebar"), St = b(re, "CommentsSidebar"), M = b(ie, "CommentsSidebar"), N = a("unresolved"), P = a(null), F = a(""), I = a(null), L = a(""), R = a(null), z = a(""), B = a(/* @__PURE__ */ new Set()), V = a(null), H = a(null), Ct = s(() => St.userConfig?.id ?? null), wt = s(() => {
|
|
75
|
+
let e = /* @__PURE__ */ new Set();
|
|
76
|
+
for (let t of j.content.value.blocks) if (e.add(t.id), t.type === "section") for (let n of t.children) for (let t of n) e.add(t.id);
|
|
77
|
+
return e;
|
|
78
|
+
});
|
|
79
|
+
function Tt(e) {
|
|
80
|
+
return e ? !wt.value.has(e) : !1;
|
|
81
|
+
}
|
|
82
|
+
let Et = s(() => N.value !== "block" || !P.value ? !1 : !(j.savedBlockIds?.value.has(P.value) ?? !0)), U = s(() => {
|
|
83
|
+
let e = M.comments.value;
|
|
84
|
+
return N.value === "unresolved" ? e = e.filter((e) => !e.resolved_at) : N.value === "block" && P.value && (e = e.filter((e) => e.block_id === P.value)), e;
|
|
85
|
+
});
|
|
86
|
+
y(() => xt.visible, (e) => {
|
|
87
|
+
e && M.loadComments();
|
|
88
|
+
}), y(() => j.state.selectedBlockId, (e) => {
|
|
89
|
+
N.value === "block" && e && (P.value = e);
|
|
90
|
+
});
|
|
91
|
+
function W(e, t) {
|
|
92
|
+
N.value = e, P.value = e === "block" ? t ?? null : null;
|
|
93
|
+
}
|
|
94
|
+
function Dt(e) {
|
|
95
|
+
W("block", e), O("filterBlock", e);
|
|
96
|
+
}
|
|
97
|
+
function Ot(e) {
|
|
98
|
+
B.value.has(e) ? B.value.delete(e) : B.value.add(e);
|
|
99
|
+
}
|
|
100
|
+
function kt(e) {
|
|
101
|
+
I.value = e, L.value = "", R.value = null, B.value.has(e) || B.value.add(e);
|
|
102
|
+
}
|
|
103
|
+
function At() {
|
|
104
|
+
I.value = null, L.value = "";
|
|
105
|
+
}
|
|
106
|
+
function G(e) {
|
|
107
|
+
R.value = e.id, z.value = e.body, I.value = null;
|
|
108
|
+
}
|
|
109
|
+
function K() {
|
|
110
|
+
R.value = null, z.value = "";
|
|
111
|
+
}
|
|
112
|
+
function q(e) {
|
|
113
|
+
V.value = e;
|
|
114
|
+
}
|
|
115
|
+
function J() {
|
|
116
|
+
V.value = null;
|
|
117
|
+
}
|
|
118
|
+
async function Y() {
|
|
119
|
+
let e = F.value.trim();
|
|
120
|
+
e && (await M.addComment(e, (N.value === "block" ? P.value : void 0) ?? void 0), F.value = "");
|
|
121
|
+
}
|
|
122
|
+
async function X(e) {
|
|
123
|
+
let t = L.value.trim();
|
|
124
|
+
if (!t) return;
|
|
125
|
+
let n = M.comments.value.find((t) => t.id === e);
|
|
126
|
+
await M.addComment(t, n?.block_id ?? void 0, e), I.value = null, L.value = "";
|
|
127
|
+
}
|
|
128
|
+
async function Z(e) {
|
|
129
|
+
let t = z.value.trim();
|
|
130
|
+
t && (await M.editComment(e, t), R.value = null, z.value = "");
|
|
131
|
+
}
|
|
132
|
+
async function Q(e) {
|
|
133
|
+
await M.removeComment(e), V.value = null;
|
|
134
|
+
}
|
|
135
|
+
async function jt(e) {
|
|
136
|
+
await M.toggleResolve(e);
|
|
137
|
+
}
|
|
138
|
+
function $(e) {
|
|
139
|
+
return e.author_identifier === Ct.value;
|
|
140
|
+
}
|
|
141
|
+
function Mt(e) {
|
|
142
|
+
return pe(e, A.snapshotHistory) ?? e;
|
|
143
|
+
}
|
|
144
|
+
function Nt(e) {
|
|
145
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Y());
|
|
146
|
+
}
|
|
147
|
+
function Pt(e, t) {
|
|
148
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), X(t));
|
|
149
|
+
}
|
|
150
|
+
function Ft(e, t) {
|
|
151
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Z(t)), e.key === "Escape" && K();
|
|
152
|
+
}
|
|
153
|
+
function It() {
|
|
154
|
+
n(() => {
|
|
155
|
+
H.value?.focus();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return E({
|
|
159
|
+
filterByBlock: Dt,
|
|
160
|
+
focusNewComment: It
|
|
161
|
+
}), (n, a) => (t(), d(f, {
|
|
162
|
+
"enter-active-class": "tpl-comments-slide-enter-active",
|
|
163
|
+
"enter-from-class": "tpl:translate-x-full",
|
|
164
|
+
"enter-to-class": "tpl:translate-x-0",
|
|
165
|
+
"leave-active-class": "tpl-comments-slide-leave-active",
|
|
166
|
+
"leave-from-class": "tpl:translate-x-0",
|
|
167
|
+
"leave-to-class": "tpl:translate-x-full"
|
|
168
|
+
}, {
|
|
169
|
+
default: i(() => [_.visible ? (t(), c("div", me, [
|
|
170
|
+
h("div", he, [h("div", ge, [
|
|
171
|
+
v(u(C), {
|
|
172
|
+
size: 13,
|
|
173
|
+
"stroke-width": 2
|
|
174
|
+
}),
|
|
175
|
+
h("span", null, o(u(A).comments.title), 1),
|
|
176
|
+
u(M).unresolvedCount.value > 0 ? (t(), c("span", _e, o(u(M).unresolvedCount.value), 1)) : l("", !0)
|
|
177
|
+
]), h("button", {
|
|
178
|
+
class: "tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]",
|
|
179
|
+
onClick: a[0] ||= (e) => O("close")
|
|
180
|
+
}, [v(u(de), {
|
|
181
|
+
size: 14,
|
|
182
|
+
"stroke-width": 2
|
|
183
|
+
})])]),
|
|
184
|
+
h("div", ve, [
|
|
185
|
+
h("button", {
|
|
186
|
+
class: m(["tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150", N.value === "unresolved" ? "tpl-comment-filter--active" : ""]),
|
|
187
|
+
onClick: a[1] ||= (e) => W("unresolved")
|
|
188
|
+
}, o(u(A).comments.filterUnresolved), 3),
|
|
189
|
+
h("button", {
|
|
190
|
+
class: m(["tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150", N.value === "all" ? "tpl-comment-filter--active" : ""]),
|
|
191
|
+
onClick: a[2] ||= (e) => W("all")
|
|
192
|
+
}, o(u(A).comments.filterAll), 3),
|
|
193
|
+
u(j).state.selectedBlockId ? (t(), c("button", {
|
|
194
|
+
key: 0,
|
|
195
|
+
class: m(["tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150", N.value === "block" ? "tpl-comment-filter--active" : ""]),
|
|
196
|
+
onClick: a[3] ||= (e) => W("block", u(j).state.selectedBlockId ?? void 0)
|
|
197
|
+
}, o(u(A).comments.filterBlock), 3)) : l("", !0)
|
|
198
|
+
]),
|
|
199
|
+
h("div", ye, [u(M).isLoading.value ? (t(), c("div", be, [v(u(T), {
|
|
200
|
+
class: "tpl-spinner tpl:text-[var(--tpl-text-muted)]",
|
|
201
|
+
size: 24,
|
|
202
|
+
"stroke-width": 2
|
|
203
|
+
})])) : U.value.length === 0 ? (t(), c("div", xe, [v(u(C), {
|
|
204
|
+
size: 32,
|
|
205
|
+
"stroke-width": 1.5,
|
|
206
|
+
class: "tpl:text-[var(--tpl-text-dim)]"
|
|
207
|
+
}), h("p", Se, o(N.value === "all" ? u(A).comments.noCommentsHint : u(A).comments.noComments), 1)])) : (t(), c("div", Ce, [(t(!0), c(g, null, r(U.value, (n) => (t(), c("div", {
|
|
208
|
+
key: n.id,
|
|
209
|
+
class: "tpl-comment-thread"
|
|
210
|
+
}, [
|
|
211
|
+
h("div", we, [
|
|
212
|
+
h("div", Te, [h("div", Ee, [
|
|
213
|
+
h("span", De, o($(n) ? u(A).comments.ownedByYou : n.author_name), 1),
|
|
214
|
+
h("span", Oe, o(Mt(n.created_at)), 1),
|
|
215
|
+
n.updated_at === n.created_at ? l("", !0) : (t(), c("span", ke, " (" + o(u(A).comments.edited) + ") ", 1))
|
|
216
|
+
]), h("div", Ae, [
|
|
217
|
+
h("button", {
|
|
218
|
+
class: "tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
219
|
+
title: n.resolved_at ? u(A).comments.unresolve : u(A).comments.resolve,
|
|
220
|
+
onClick: (e) => jt(n.id)
|
|
221
|
+
}, [v(u(ue), {
|
|
222
|
+
size: 13,
|
|
223
|
+
"stroke-width": 2,
|
|
224
|
+
class: "tpl-resolve-icon",
|
|
225
|
+
style: ee({ color: n.resolved_at ? "var(--tpl-primary)" : void 0 })
|
|
226
|
+
}, null, 8, ["style"])], 8, je),
|
|
227
|
+
$(n) ? (t(), c("button", {
|
|
228
|
+
key: 0,
|
|
229
|
+
class: "tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
230
|
+
title: u(A).comments.edit,
|
|
231
|
+
onClick: (e) => G(n)
|
|
232
|
+
}, [v(u(x), {
|
|
233
|
+
size: 12,
|
|
234
|
+
"stroke-width": 2
|
|
235
|
+
})], 8, Me)) : l("", !0),
|
|
236
|
+
$(n) ? (t(), c("button", {
|
|
237
|
+
key: 1,
|
|
238
|
+
class: "tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
239
|
+
title: u(A).comments.delete,
|
|
240
|
+
onClick: (e) => q(n.id)
|
|
241
|
+
}, [v(u(S), {
|
|
242
|
+
size: 12,
|
|
243
|
+
"stroke-width": 2
|
|
244
|
+
})], 8, Ne)) : l("", !0)
|
|
245
|
+
])]),
|
|
246
|
+
v(f, { name: "tpl-resolve" }, {
|
|
247
|
+
default: i(() => [n.resolved_at ? (t(), c("div", Pe, [v(u(oe), {
|
|
248
|
+
size: 10,
|
|
249
|
+
"stroke-width": 2.5
|
|
250
|
+
}), h("span", null, o(u(k)(u(A).comments.resolvedBy, { name: n.resolved_by_name ?? "" })), 1)])) : l("", !0)]),
|
|
251
|
+
_: 2
|
|
252
|
+
}, 1024),
|
|
253
|
+
n.block_id && Tt(n.block_id) ? (t(), c("span", Fe, o(u(A).comments.missingBlock), 1)) : n.block_id ? (t(), c("button", {
|
|
254
|
+
key: 1,
|
|
255
|
+
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:duration-150 tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]",
|
|
256
|
+
onClick: (e) => u(j).selectBlock(n.block_id ?? "")
|
|
257
|
+
}, " Block ", 8, Ie)) : l("", !0),
|
|
258
|
+
R.value === n.id ? (t(), c("div", Le, [e(h("textarea", {
|
|
259
|
+
"onUpdate:modelValue": a[4] ||= (e) => z.value = e,
|
|
260
|
+
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)]",
|
|
261
|
+
rows: "3",
|
|
262
|
+
onKeydown: (e) => Ft(e, n.id)
|
|
263
|
+
}, null, 40, Re), [[p, z.value]]), h("div", ze, [h("button", {
|
|
264
|
+
class: "tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]",
|
|
265
|
+
disabled: !z.value.trim() || u(M).isSubmitting.value,
|
|
266
|
+
onClick: (e) => Z(n.id)
|
|
267
|
+
}, o(u(A).comments.save), 9, Be), h("button", {
|
|
268
|
+
class: "tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]",
|
|
269
|
+
onClick: a[5] ||= (e) => K()
|
|
270
|
+
}, o(u(A).comments.cancel), 1)])])) : (t(), c("p", Ve, o(n.body), 1)),
|
|
271
|
+
V.value === n.id ? (t(), c("div", He, [
|
|
272
|
+
h("span", Ue, o(u(A).comments.deleteConfirm), 1),
|
|
273
|
+
h("button", {
|
|
274
|
+
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)]",
|
|
275
|
+
onClick: (e) => Q(n.id)
|
|
276
|
+
}, o(u(A).comments.delete), 9, We),
|
|
277
|
+
h("button", {
|
|
278
|
+
class: "tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]",
|
|
279
|
+
onClick: a[6] ||= (e) => J()
|
|
280
|
+
}, o(u(A).comments.cancel), 1)
|
|
281
|
+
])) : l("", !0),
|
|
282
|
+
R.value !== n.id && V.value !== n.id ? (t(), c("div", Ge, [h("button", {
|
|
283
|
+
class: "tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
284
|
+
title: u(A).comments.reply,
|
|
285
|
+
onClick: (e) => kt(n.id)
|
|
286
|
+
}, [v(u(se), {
|
|
287
|
+
size: 13,
|
|
288
|
+
"stroke-width": 2,
|
|
289
|
+
class: "tpl:text-[var(--tpl-primary)]"
|
|
290
|
+
})], 8, Ke), (n.replies?.length ?? 0) > 0 ? (t(), c("button", {
|
|
291
|
+
key: 0,
|
|
292
|
+
class: "tpl:flex tpl:items-center tpl:gap-0.5 tpl:text-[11px] tpl:font-medium tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]",
|
|
293
|
+
onClick: (e) => Ot(n.id)
|
|
294
|
+
}, [B.value.has(n.id) ? (t(), d(u(le), {
|
|
295
|
+
key: 0,
|
|
296
|
+
size: 11,
|
|
297
|
+
"stroke-width": 2
|
|
298
|
+
})) : (t(), d(u(ce), {
|
|
299
|
+
key: 1,
|
|
300
|
+
size: 11,
|
|
301
|
+
"stroke-width": 2
|
|
302
|
+
})), te(" " + o((n.replies?.length ?? 0) === 1 ? u(k)(u(A).comments.replyOne, { count: String(n.replies?.length ?? 0) }) : u(k)(u(A).comments.replyMany, { count: String(n.replies?.length ?? 0) })), 1)], 8, qe)) : l("", !0)])) : l("", !0)
|
|
303
|
+
]),
|
|
304
|
+
v(f, { name: "tpl-replies" }, {
|
|
305
|
+
default: i(() => [B.value.has(n.id) && (n.replies?.length ?? 0) > 0 ? (t(), c("div", Je, [(t(!0), c(g, null, r(n.replies, (r, i) => (t(), c("div", {
|
|
306
|
+
key: r.id,
|
|
307
|
+
class: m(["tpl-comment-reply-card tpl:relative tpl:rounded-lg tpl:border tpl:px-3.5 tpl:py-2.5", i < (n.replies?.length ?? 0) - 1 ? "tpl:mb-2" : ""])
|
|
308
|
+
}, [
|
|
309
|
+
h("div", Ye, [h("div", Xe, [
|
|
310
|
+
h("span", Ze, o($(r) ? u(A).comments.ownedByYou : r.author_name), 1),
|
|
311
|
+
h("span", Qe, o(Mt(r.created_at)), 1),
|
|
312
|
+
r.updated_at === r.created_at ? l("", !0) : (t(), c("span", $e, " (" + o(u(A).comments.edited) + ") ", 1))
|
|
313
|
+
]), h("div", et, [$(r) ? (t(), c("button", {
|
|
314
|
+
key: 0,
|
|
315
|
+
class: "tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
316
|
+
title: u(A).comments.edit,
|
|
317
|
+
onClick: (e) => G(r)
|
|
318
|
+
}, [v(u(x), {
|
|
319
|
+
size: 11,
|
|
320
|
+
"stroke-width": 2
|
|
321
|
+
})], 8, tt)) : l("", !0), $(r) ? (t(), c("button", {
|
|
322
|
+
key: 1,
|
|
323
|
+
class: "tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150",
|
|
324
|
+
title: u(A).comments.delete,
|
|
325
|
+
onClick: (e) => q(r.id)
|
|
326
|
+
}, [v(u(S), {
|
|
327
|
+
size: 11,
|
|
328
|
+
"stroke-width": 2
|
|
329
|
+
})], 8, nt)) : l("", !0)])]),
|
|
330
|
+
R.value === r.id ? (t(), c("div", rt, [e(h("textarea", {
|
|
331
|
+
"onUpdate:modelValue": a[7] ||= (e) => z.value = e,
|
|
332
|
+
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)]",
|
|
333
|
+
rows: "2",
|
|
334
|
+
onKeydown: (e) => Ft(e, r.id)
|
|
335
|
+
}, null, 40, it), [[p, z.value]]), h("div", at, [h("button", {
|
|
336
|
+
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-bg)]",
|
|
337
|
+
disabled: !z.value.trim() || u(M).isSubmitting.value,
|
|
338
|
+
onClick: (e) => Z(r.id)
|
|
339
|
+
}, o(u(A).comments.save), 9, ot), h("button", {
|
|
340
|
+
class: "tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]",
|
|
341
|
+
onClick: a[8] ||= (e) => K()
|
|
342
|
+
}, o(u(A).comments.cancel), 1)])])) : (t(), c("p", st, o(r.body), 1)),
|
|
343
|
+
V.value === r.id ? (t(), c("div", ct, [
|
|
344
|
+
h("span", lt, o(u(A).comments.deleteConfirm), 1),
|
|
345
|
+
h("button", {
|
|
346
|
+
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)]",
|
|
347
|
+
onClick: (e) => Q(r.id)
|
|
348
|
+
}, o(u(A).comments.delete), 9, ut),
|
|
349
|
+
h("button", {
|
|
350
|
+
class: "tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]",
|
|
351
|
+
onClick: a[9] ||= (e) => J()
|
|
352
|
+
}, o(u(A).comments.cancel), 1)
|
|
353
|
+
])) : l("", !0)
|
|
354
|
+
], 2))), 128))])) : l("", !0)]),
|
|
355
|
+
_: 2
|
|
356
|
+
}, 1024),
|
|
357
|
+
v(f, { name: "tpl-replies" }, {
|
|
358
|
+
default: i(() => [I.value === n.id ? (t(), c("div", dt, [h("div", ft, [e(h("textarea", {
|
|
359
|
+
"onUpdate:modelValue": a[10] ||= (e) => L.value = e,
|
|
360
|
+
class: "tpl:flex-1 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)]",
|
|
361
|
+
placeholder: u(A).comments.replyPlaceholder,
|
|
362
|
+
rows: "2",
|
|
363
|
+
onKeydown: (e) => Pt(e, n.id)
|
|
364
|
+
}, null, 40, pt), [[p, L.value]]), h("div", mt, [h("button", {
|
|
365
|
+
class: "tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]",
|
|
366
|
+
disabled: !L.value.trim() || u(M).isSubmitting.value,
|
|
367
|
+
onClick: (e) => X(n.id)
|
|
368
|
+
}, [v(u(w), {
|
|
369
|
+
size: 14,
|
|
370
|
+
"stroke-width": 2
|
|
371
|
+
})], 8, ht), h("button", {
|
|
372
|
+
class: "tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]",
|
|
373
|
+
onClick: a[11] ||= (e) => At()
|
|
374
|
+
}, [v(u(de), {
|
|
375
|
+
size: 14,
|
|
376
|
+
"stroke-width": 2
|
|
377
|
+
})])])])])) : l("", !0)]),
|
|
378
|
+
_: 2
|
|
379
|
+
}, 1024)
|
|
380
|
+
]))), 128))]))]),
|
|
381
|
+
h("div", gt, [Et.value ? (t(), c("div", _t, o(u(A).comments.saveTemplateFirst), 1)) : (t(), c("div", vt, [e(h("textarea", {
|
|
382
|
+
ref_key: "newCommentInput",
|
|
383
|
+
ref: H,
|
|
384
|
+
"onUpdate:modelValue": a[12] ||= (e) => F.value = e,
|
|
385
|
+
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)]",
|
|
386
|
+
placeholder: u(A).comments.placeholder,
|
|
387
|
+
disabled: u(M).isSubmitting.value,
|
|
388
|
+
rows: "2",
|
|
389
|
+
onKeydown: Nt
|
|
390
|
+
}, null, 40, yt), [[p, F.value]]), h("button", {
|
|
391
|
+
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:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]",
|
|
392
|
+
disabled: !F.value.trim() || u(M).isSubmitting.value,
|
|
393
|
+
onClick: Y
|
|
394
|
+
}, [u(M).isSubmitting.value ? (t(), d(u(T), {
|
|
395
|
+
key: 0,
|
|
396
|
+
class: "tpl-spinner",
|
|
397
|
+
size: 16,
|
|
398
|
+
"stroke-width": 2
|
|
399
|
+
})) : (t(), d(u(w), {
|
|
400
|
+
key: 1,
|
|
401
|
+
size: 16,
|
|
402
|
+
"stroke-width": 2
|
|
403
|
+
}))], 8, bt)]))])
|
|
404
|
+
])) : l("", !0)]),
|
|
405
|
+
_: 1
|
|
406
|
+
}));
|
|
407
|
+
}
|
|
408
|
+
}), [["__scopeId", "data-v-d204f573"]]);
|
|
409
|
+
//#endregion
|
|
410
|
+
export { D as default };
|
|
411
|
+
|
|
412
|
+
//# sourceMappingURL=CommentsSidebar-DHdRiGPX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommentsSidebar-DHdRiGPX.js","names":[],"sources":["../../../src/cloud/components/CommentsSidebar.vue","../../../src/cloud/components/CommentsSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { formatRelativeTime } from \"../../utils/formatRelativeTime\";\nimport {\n EDITOR_KEY,\n AUTH_MANAGER_KEY,\n COMMENTS_KEY,\n requireInject,\n} from \"../../keys\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\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\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"filterBlock\", blockId: string | null): void;\n}>();\n\nconst { format } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"CommentsSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"CommentsSidebar\");\nconst comments = requireInject(COMMENTS_KEY, \"CommentsSidebar\");\n\ntype FilterMode = \"all\" | \"unresolved\" | \"block\";\nconst filterMode = ref<FilterMode>(\"unresolved\");\nconst filterBlockId = ref<string | null>(null);\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\nconst currentUserId = computed(() => authManager.userConfig?.id ?? null);\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): 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 return !(editor.savedBlockIds?.value.has(filterBlockId.value) ?? true);\n});\n\nconst filteredComments = computed(() => {\n let items = comments.comments.value;\n\n if (filterMode.value === \"unresolved\") {\n items = items.filter((c) => !c.resolved_at);\n } else if (filterMode.value === \"block\" && filterBlockId.value) {\n items = items.filter((c) => c.block_id === filterBlockId.value);\n }\n\n return items;\n});\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible) {\n comments.loadComments();\n }\n },\n);\n\nwatch(\n () => editor.state.selectedBlockId,\n (newBlockId) => {\n if (filterMode.value === \"block\" && newBlockId) {\n filterBlockId.value = newBlockId;\n }\n },\n);\n\nfunction setFilter(mode: FilterMode, blockId?: string): void {\n filterMode.value = mode;\n filterBlockId.value = mode === \"block\" ? (blockId ?? null) : null;\n}\n\nfunction filterByBlock(blockId: string): void {\n setFilter(\"block\", blockId);\n emit(\"filterBlock\", 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\nasync function handleAddComment(): Promise<void> {\n const body = newCommentBody.value.trim();\n if (!body) {\n return;\n }\n\n const blockId =\n filterMode.value === \"block\" ? filterBlockId.value : undefined;\n await comments.addComment(body, blockId ?? undefined);\n newCommentBody.value = \"\";\n}\n\nasync function handleReply(parentId: string): Promise<void> {\n const body = replyBody.value.trim();\n if (!body) {\n return;\n }\n\n const parent = comments.comments.value.find((c) => c.id === parentId);\n await comments.addComment(body, parent?.block_id ?? undefined, parentId);\n replyingTo.value = null;\n replyBody.value = \"\";\n}\n\nasync function handleEdit(commentId: string): Promise<void> {\n const body = editBody.value.trim();\n if (!body) {\n return;\n }\n\n await comments.editComment(commentId, body);\n editingId.value = null;\n editBody.value = \"\";\n}\n\nasync function handleDelete(commentId: string): Promise<void> {\n await comments.removeComment(commentId);\n deletingId.value = null;\n}\n\nasync function handleResolve(commentId: string): Promise<void> {\n await comments.toggleResolve(commentId);\n}\n\nfunction isOwnComment(comment: Comment): boolean {\n return comment.author_identifier === currentUserId.value;\n}\n\nfunction formatTime(dateString: string): string {\n return formatRelativeTime(dateString, cloudT.snapshotHistory) ?? 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 class=\"tpl-comments-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel 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>{{ cloudT.comments.title }}</span>\n <span\n v-if=\"comments.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-bg)]\"\n >\n {{ comments.unresolvedCount.value }}\n </span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\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 class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150\"\n :class=\"\n filterMode === 'unresolved' ? 'tpl-comment-filter--active' : ''\n \"\n @click=\"setFilter('unresolved')\"\n >\n {{ cloudT.comments.filterUnresolved }}\n </button>\n <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 tpl:duration-150\"\n :class=\"filterMode === 'all' ? 'tpl-comment-filter--active' : ''\"\n @click=\"setFilter('all')\"\n >\n {{ cloudT.comments.filterAll }}\n </button>\n <button\n v-if=\"editor.state.selectedBlockId\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150\"\n :class=\"filterMode === 'block' ? 'tpl-comment-filter--active' : ''\"\n @click=\"setFilter('block', editor.state.selectedBlockId ?? undefined)\"\n >\n {{ cloudT.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=\"comments.isLoading.value\"\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 ? cloudT.comments.noCommentsHint\n : cloudT.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 >\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 isOwnComment(thread)\n ? cloudT.comments.ownedByYou\n : thread.author_name\n }}\n </span>\n <span class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\">\n {{ formatTime(thread.created_at) }}\n </span>\n <span\n v-if=\"thread.updated_at !== thread.created_at\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ cloudT.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <!-- Resolve toggle -->\n <button\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"\n thread.resolved_at\n ? cloudT.comments.unresolve\n : cloudT.comments.resolve\n \"\n @click=\"handleResolve(thread.id)\"\n >\n <CircleCheck\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl-resolve-icon\"\n :style=\"{\n color: thread.resolved_at\n ? 'var(--tpl-primary)'\n : undefined,\n }\"\n />\n </button>\n <!-- Edit (own only) -->\n <button\n v-if=\"isOwnComment(thread)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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=\"isOwnComment(thread)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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.resolved_at\"\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(cloudT.comments.resolvedBy, {\n name: thread.resolved_by_name ?? \"\",\n })\n }}\n </span>\n </div>\n </Transition>\n\n <!-- Block anchor indicator -->\n <span\n v-if=\"thread.block_id && isBlockMissing(thread.block_id)\"\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 {{ cloudT.comments.missingBlock }}\n </span>\n <button\n v-else-if=\"thread.block_id\"\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:duration-150 tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"editor.selectBlock(thread.block_id ?? '')\"\n >\n Block\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)]\"\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:duration-150 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!editBody.trim() || comments.isSubmitting.value\"\n @click=\"handleEdit(thread.id)\"\n >\n {{ cloudT.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:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ cloudT.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 {{ cloudT.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 @click=\"handleDelete(thread.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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-[11px] tpl:font-medium tpl:transition-colors tpl:duration-150 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(cloudT.comments.replyOne, {\n count: String(thread.replies?.length ?? 0),\n })\n : format(cloudT.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 isOwnComment(reply)\n ? cloudT.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.created_at) }}\n </span>\n <span\n v-if=\"reply.updated_at !== reply.created_at\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ cloudT.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <button\n v-if=\"isOwnComment(reply)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.comments.edit\"\n @click=\"startEdit(reply)\"\n >\n <Pencil :size=\"11\" :stroke-width=\"2\" />\n </button>\n <button\n v-if=\"isOwnComment(reply)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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)]\"\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-bg)]\"\n :disabled=\"\n !editBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleEdit(reply.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 @click=\"handleDelete(reply.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 <div class=\"tpl:flex tpl:items-end tpl:gap-2\">\n <textarea\n v-model=\"replyBody\"\n class=\"tpl:flex-1 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)]\"\n :placeholder=\"cloudT.comments.replyPlaceholder\"\n rows=\"2\"\n @keydown=\"handleReplyKeydown($event, thread.id)\"\n />\n <div class=\"tpl:flex tpl:shrink-0 tpl:flex-col tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n :disabled=\"\n !replyBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleReply(thread.id)\"\n >\n <Send :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelReply()\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <!-- New comment input -->\n <div class=\"tpl:border-t tpl:p-3 tpl:border-[var(--tpl-border)]\">\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 {{ cloudT.comments.saveTemplateFirst }}\n </div>\n <div\n v-else\n class=\"tpl-comments-input-wrapper 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 :placeholder=\"cloudT.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:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\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 {\n EDITOR_KEY,\n AUTH_MANAGER_KEY,\n COMMENTS_KEY,\n requireInject,\n} from \"../../keys\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\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\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"filterBlock\", blockId: string | null): void;\n}>();\n\nconst { format } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"CommentsSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"CommentsSidebar\");\nconst comments = requireInject(COMMENTS_KEY, \"CommentsSidebar\");\n\ntype FilterMode = \"all\" | \"unresolved\" | \"block\";\nconst filterMode = ref<FilterMode>(\"unresolved\");\nconst filterBlockId = ref<string | null>(null);\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\nconst currentUserId = computed(() => authManager.userConfig?.id ?? null);\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): 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 return !(editor.savedBlockIds?.value.has(filterBlockId.value) ?? true);\n});\n\nconst filteredComments = computed(() => {\n let items = comments.comments.value;\n\n if (filterMode.value === \"unresolved\") {\n items = items.filter((c) => !c.resolved_at);\n } else if (filterMode.value === \"block\" && filterBlockId.value) {\n items = items.filter((c) => c.block_id === filterBlockId.value);\n }\n\n return items;\n});\n\nwatch(\n () => props.visible,\n (isVisible) => {\n if (isVisible) {\n comments.loadComments();\n }\n },\n);\n\nwatch(\n () => editor.state.selectedBlockId,\n (newBlockId) => {\n if (filterMode.value === \"block\" && newBlockId) {\n filterBlockId.value = newBlockId;\n }\n },\n);\n\nfunction setFilter(mode: FilterMode, blockId?: string): void {\n filterMode.value = mode;\n filterBlockId.value = mode === \"block\" ? (blockId ?? null) : null;\n}\n\nfunction filterByBlock(blockId: string): void {\n setFilter(\"block\", blockId);\n emit(\"filterBlock\", 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\nasync function handleAddComment(): Promise<void> {\n const body = newCommentBody.value.trim();\n if (!body) {\n return;\n }\n\n const blockId =\n filterMode.value === \"block\" ? filterBlockId.value : undefined;\n await comments.addComment(body, blockId ?? undefined);\n newCommentBody.value = \"\";\n}\n\nasync function handleReply(parentId: string): Promise<void> {\n const body = replyBody.value.trim();\n if (!body) {\n return;\n }\n\n const parent = comments.comments.value.find((c) => c.id === parentId);\n await comments.addComment(body, parent?.block_id ?? undefined, parentId);\n replyingTo.value = null;\n replyBody.value = \"\";\n}\n\nasync function handleEdit(commentId: string): Promise<void> {\n const body = editBody.value.trim();\n if (!body) {\n return;\n }\n\n await comments.editComment(commentId, body);\n editingId.value = null;\n editBody.value = \"\";\n}\n\nasync function handleDelete(commentId: string): Promise<void> {\n await comments.removeComment(commentId);\n deletingId.value = null;\n}\n\nasync function handleResolve(commentId: string): Promise<void> {\n await comments.toggleResolve(commentId);\n}\n\nfunction isOwnComment(comment: Comment): boolean {\n return comment.author_identifier === currentUserId.value;\n}\n\nfunction formatTime(dateString: string): string {\n return formatRelativeTime(dateString, cloudT.snapshotHistory) ?? 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 class=\"tpl-comments-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel 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>{{ cloudT.comments.title }}</span>\n <span\n v-if=\"comments.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-bg)]\"\n >\n {{ comments.unresolvedCount.value }}\n </span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\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 class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150\"\n :class=\"\n filterMode === 'unresolved' ? 'tpl-comment-filter--active' : ''\n \"\n @click=\"setFilter('unresolved')\"\n >\n {{ cloudT.comments.filterUnresolved }}\n </button>\n <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 tpl:duration-150\"\n :class=\"filterMode === 'all' ? 'tpl-comment-filter--active' : ''\"\n @click=\"setFilter('all')\"\n >\n {{ cloudT.comments.filterAll }}\n </button>\n <button\n v-if=\"editor.state.selectedBlockId\"\n class=\"tpl-comment-filter tpl:rounded-md tpl:px-2.5 tpl:py-1 tpl:text-xs tpl:font-medium tpl:transition-colors tpl:duration-150\"\n :class=\"filterMode === 'block' ? 'tpl-comment-filter--active' : ''\"\n @click=\"setFilter('block', editor.state.selectedBlockId ?? undefined)\"\n >\n {{ cloudT.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=\"comments.isLoading.value\"\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 ? cloudT.comments.noCommentsHint\n : cloudT.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 >\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 isOwnComment(thread)\n ? cloudT.comments.ownedByYou\n : thread.author_name\n }}\n </span>\n <span class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\">\n {{ formatTime(thread.created_at) }}\n </span>\n <span\n v-if=\"thread.updated_at !== thread.created_at\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ cloudT.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <!-- Resolve toggle -->\n <button\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"\n thread.resolved_at\n ? cloudT.comments.unresolve\n : cloudT.comments.resolve\n \"\n @click=\"handleResolve(thread.id)\"\n >\n <CircleCheck\n :size=\"13\"\n :stroke-width=\"2\"\n class=\"tpl-resolve-icon\"\n :style=\"{\n color: thread.resolved_at\n ? 'var(--tpl-primary)'\n : undefined,\n }\"\n />\n </button>\n <!-- Edit (own only) -->\n <button\n v-if=\"isOwnComment(thread)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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=\"isOwnComment(thread)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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.resolved_at\"\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(cloudT.comments.resolvedBy, {\n name: thread.resolved_by_name ?? \"\",\n })\n }}\n </span>\n </div>\n </Transition>\n\n <!-- Block anchor indicator -->\n <span\n v-if=\"thread.block_id && isBlockMissing(thread.block_id)\"\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 {{ cloudT.comments.missingBlock }}\n </span>\n <button\n v-else-if=\"thread.block_id\"\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:duration-150 tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n @click=\"editor.selectBlock(thread.block_id ?? '')\"\n >\n Block\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)]\"\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:duration-150 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!editBody.trim() || comments.isSubmitting.value\"\n @click=\"handleEdit(thread.id)\"\n >\n {{ cloudT.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:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelEdit()\"\n >\n {{ cloudT.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 {{ cloudT.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 @click=\"handleDelete(thread.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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-[11px] tpl:font-medium tpl:transition-colors tpl:duration-150 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(cloudT.comments.replyOne, {\n count: String(thread.replies?.length ?? 0),\n })\n : format(cloudT.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 isOwnComment(reply)\n ? cloudT.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.created_at) }}\n </span>\n <span\n v-if=\"reply.updated_at !== reply.created_at\"\n class=\"tpl:text-[10px] tpl:italic tpl:text-[var(--tpl-text-dim)]\"\n >\n ({{ cloudT.comments.edited }})\n </span>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-0.5\">\n <button\n v-if=\"isOwnComment(reply)\"\n class=\"tpl-comment-action tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.comments.edit\"\n @click=\"startEdit(reply)\"\n >\n <Pencil :size=\"11\" :stroke-width=\"2\" />\n </button>\n <button\n v-if=\"isOwnComment(reply)\"\n class=\"tpl-comment-action tpl-comment-delete tpl:rounded tpl:p-1 tpl:transition-colors tpl:duration-150\"\n :title=\"cloudT.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)]\"\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-bg)]\"\n :disabled=\"\n !editBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleEdit(reply.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 {{ cloudT.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 @click=\"handleDelete(reply.id)\"\n >\n {{ cloudT.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 {{ cloudT.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 <div class=\"tpl:flex tpl:items-end tpl:gap-2\">\n <textarea\n v-model=\"replyBody\"\n class=\"tpl:flex-1 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)]\"\n :placeholder=\"cloudT.comments.replyPlaceholder\"\n rows=\"2\"\n @keydown=\"handleReplyKeydown($event, thread.id)\"\n />\n <div class=\"tpl:flex tpl:shrink-0 tpl:flex-col tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\n :disabled=\"\n !replyBody.trim() || comments.isSubmitting.value\n \"\n @click=\"handleReply(thread.id)\"\n >\n <Send :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:rounded-md tpl:p-1.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"cancelReply()\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n\n <!-- New comment input -->\n <div class=\"tpl:border-t tpl:p-3 tpl:border-[var(--tpl-border)]\">\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 {{ cloudT.comments.saveTemplateFirst }}\n </div>\n <div\n v-else\n class=\"tpl-comments-input-wrapper 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 :placeholder=\"cloudT.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:duration-150 tpl:disabled:opacity-40 tpl:text-[var(--tpl-primary)]\"\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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"}
|