@templatical/editor 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +84 -31
- package/dist/{AiChatSidebar-KeS30iRG.js → AiChatSidebar-Bq99wU01.js} +14 -14
- package/dist/{AiFeatureMenu-CCdTgCy7.js → AiFeatureMenu-F-C59LAV.js} +7 -7
- package/dist/{BlockIssueBadge-DQ6jBV1T.js → BlockIssueBadge-Dhk7Hv4m.js} +5 -5
- package/dist/{BlockPreviewCanvas-MgXVXOG_.js → BlockPreviewCanvas-CqU1kwmG.js} +3 -3
- package/dist/{CloudHeaderExtras-L_b51XVJ.js → CloudHeaderExtras-vnGV_mLD.js} +5 -5
- package/dist/{CloudPanels-XUtScYFl.js → CloudPanels-Dv-MD1Tc.js} +7 -7
- package/dist/{CollaboratorBar-Cx5klY7H.js → CollaboratorBar-DmssyJoB.js} +4 -4
- package/dist/{ColorPicker-DjfecH79.js → ColorPicker-CXPSbnRD.js} +7 -7
- package/dist/{CommentsPanel-5QxgkKF-.js → CommentsPanel-DnWdgQHg.js} +2 -2
- package/dist/{CommentsSidebar-CScszOsC.js → CommentsSidebar-ZOqmuo4D.js} +16 -16
- package/dist/{CountdownBlock-Drr1WErd.js → CountdownBlock-BgNK7fZY.js} +3 -3
- package/dist/{CountdownToolbar-CiBrLbq0.js → CountdownToolbar-SwmE7CIk.js} +19 -19
- package/dist/{DesignReferenceSidebar-B1__DJJI.js → DesignReferenceSidebar-9cLTzOqP.js} +11 -11
- package/dist/{NumberWithSuffix-C1D0WFrN.js → FieldRow-0-UbQR4a.js} +24 -17
- package/dist/{IssuesPanel-CkuV9Fjp.js → IssuesPanel-CDk7lUna.js} +8 -8
- package/dist/{LoadingTrack-Db9j_aBr.js → LoadingTrack-5KBr5cE8.js} +1 -1
- package/dist/{MergeTagModeToggle-oX-n91fH.js → MergeTagModeToggle-DtD_Xl9w.js} +6 -6
- package/dist/{MergeTagSuggestion-BUozNVtA.js → MergeTagSuggestion-DPfXtDzP.js} +4 -4
- package/dist/{ParagraphEditor-BVlMj-YW.js → ParagraphEditor-C0NbqDmV.js} +25 -25
- package/dist/{RestoreVersionDialog-CZRLWoqP.js → RestoreVersionDialog-DCX0rJiV.js} +5 -5
- package/dist/{RichTextEditorContent-BN6RBSZG.js → RichTextEditorContent-DZ6GiAUi.js} +8 -8
- package/dist/{SaveBlockDialog-KNQVLcUG.js → SaveBlockDialog-e5LtQRVk.js} +10 -10
- package/dist/{SavedBlocksBrowserModal-D7qatvfw.js → SavedBlocksBrowserModal-ZQIQoARO.js} +11 -11
- package/dist/{SavedBlocksPanels-bizAFdNK.js → SavedBlocksPanels-pGZWjSmp.js} +2 -2
- package/dist/{SavedBlocksPickBar-BXUSoa38.js → SavedBlocksPickBar-OZaOG1Si.js} +4 -4
- package/dist/{TemplateScoringPanel-Dm83xt9R.js → TemplateScoringPanel-CDwlSFEe.js} +15 -15
- package/dist/{TestEmailModal-DAdO9Gmo.js → TestEmailModal-PVa1VL2p.js} +11 -11
- package/dist/{TestEmailPanel-DORs6xk0.js → TestEmailPanel-RekBdAtO.js} +2 -2
- package/dist/{TitleEditor-B_aWxZER.js → TitleEditor-Cs2tELCL.js} +12 -12
- package/dist/{TplModal-7623HR5x.js → TplModal-Bw1i8_eE.js} +5 -5
- package/dist/{VersionHistoryMenu-D7lgozpw.js → VersionHistoryMenu-B6zAcmAC.js} +8 -8
- package/dist/{VersionHistoryPanels-Dor0p1Ur.js → VersionHistoryPanels-TqTC4ofA.js} +2 -2
- package/dist/{VersionPreviewBanner-CQt2il1L.js → VersionPreviewBanner-CQlSzQ2B.js} +3 -3
- package/dist/{blockTypeIcons-Di4AsjQx.js → blockTypeIcons-C7WedEOG.js} +2 -2
- package/dist/bundle-stats.json +6 -6
- package/dist/{cdn/chunks/ca-DY0AYOLG.js → ca-DZSrLNXK.js} +2 -2
- package/dist/cdn/chunks/{AiChatSidebar-Cwr9eqTh.js → AiChatSidebar-DmZ1Nvxo.js} +11 -11
- package/dist/cdn/chunks/{AiChatSidebar-Cwr9eqTh.js.map → AiChatSidebar-DmZ1Nvxo.js.map} +1 -1
- package/dist/cdn/chunks/{AiFeatureMenu-CT2QiHmi.js → AiFeatureMenu-D4tax9j3.js} +6 -6
- package/dist/cdn/chunks/{AiFeatureMenu-CT2QiHmi.js.map → AiFeatureMenu-D4tax9j3.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-CXlXn9Sm.js → BlockIssueBadge-Bry71Wjz.js} +5 -5
- package/dist/cdn/chunks/{BlockIssueBadge-CXlXn9Sm.js.map → BlockIssueBadge-Bry71Wjz.js.map} +1 -1
- package/dist/cdn/chunks/{BlockPreviewCanvas-BisO3JEy.js → BlockPreviewCanvas-DDOqFJlT.js} +4 -4
- package/dist/cdn/chunks/{BlockPreviewCanvas-BisO3JEy.js.map → BlockPreviewCanvas-DDOqFJlT.js.map} +1 -1
- package/dist/cdn/chunks/{CloudHeaderExtras-MRCYqip8.js → CloudHeaderExtras-g-_SFPna.js} +5 -5
- package/dist/cdn/chunks/{CloudHeaderExtras-MRCYqip8.js.map → CloudHeaderExtras-g-_SFPna.js.map} +1 -1
- package/dist/cdn/chunks/{CloudPanels-Bb9qSwKJ.js → CloudPanels-Cnh07_2G.js} +7 -7
- package/dist/cdn/chunks/{CloudPanels-Bb9qSwKJ.js.map → CloudPanels-Cnh07_2G.js.map} +1 -1
- package/dist/cdn/chunks/{CollaboratorBar-CG13kDHu.js → CollaboratorBar-CLGB0prG.js} +5 -5
- package/dist/cdn/chunks/{CollaboratorBar-CG13kDHu.js.map → CollaboratorBar-CLGB0prG.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-UpN24AsB.js → ColorPicker-Ww4xHHMI.js} +7 -7
- package/dist/cdn/chunks/{ColorPicker-UpN24AsB.js.map → ColorPicker-Ww4xHHMI.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsPanel-CIcIXzOB.js → CommentsPanel-DjId-XH-.js} +3 -3
- package/dist/cdn/chunks/{CommentsPanel-CIcIXzOB.js.map → CommentsPanel-DjId-XH-.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsSidebar-CB5Z8xNz.js → CommentsSidebar-DXXGWSr4.js} +5 -5
- package/dist/cdn/chunks/{CommentsSidebar-CB5Z8xNz.js.map → CommentsSidebar-DXXGWSr4.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownBlock-_7TUBqJS.js → CountdownBlock-CWeJBC0L.js} +4 -4
- package/dist/cdn/chunks/{CountdownBlock-_7TUBqJS.js.map → CountdownBlock-CWeJBC0L.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-B1L0IK1T.js → CountdownToolbar-CO6xLgYc.js} +20 -20
- package/dist/cdn/chunks/{CountdownToolbar-B1L0IK1T.js.map → CountdownToolbar-CO6xLgYc.js.map} +1 -1
- package/dist/cdn/chunks/{DesignReferenceSidebar-BwsfOFsc.js → DesignReferenceSidebar-DRGqm33h.js} +7 -7
- package/dist/cdn/chunks/{DesignReferenceSidebar-BwsfOFsc.js.map → DesignReferenceSidebar-DRGqm33h.js.map} +1 -1
- package/dist/cdn/chunks/{NumberWithSuffix-CWvM_I3T.js → FieldRow-CbcpsxZ4.js} +25 -18
- package/dist/cdn/chunks/FieldRow-CbcpsxZ4.js.map +1 -0
- package/dist/cdn/chunks/{IssuesPanel-3nTCyKgK.js → IssuesPanel-iC2hgrXx.js} +6 -6
- package/dist/cdn/chunks/{IssuesPanel-3nTCyKgK.js.map → IssuesPanel-iC2hgrXx.js.map} +1 -1
- package/dist/cdn/chunks/{LoadingTrack-D_wweXVz.js → LoadingTrack-Cz1sT0wP.js} +2 -2
- package/dist/cdn/chunks/{LoadingTrack-D_wweXVz.js.map → LoadingTrack-Cz1sT0wP.js.map} +1 -1
- package/dist/cdn/chunks/{MergeTagModeToggle-BUNsRKs6.js → MergeTagModeToggle-DZtnCj0F.js} +6 -6
- package/dist/cdn/chunks/{MergeTagModeToggle-BUNsRKs6.js.map → MergeTagModeToggle-DZtnCj0F.js.map} +1 -1
- package/dist/cdn/chunks/{MergeTagSuggestion-Dt6I8bZi.js → MergeTagSuggestion-VP_nC-8W.js} +3 -3
- package/dist/cdn/chunks/{MergeTagSuggestion-Dt6I8bZi.js.map → MergeTagSuggestion-VP_nC-8W.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-BtzkHfv2.js → ParagraphEditor-BalgLjBt.js} +27 -27
- package/dist/cdn/chunks/{ParagraphEditor-BtzkHfv2.js.map → ParagraphEditor-BalgLjBt.js.map} +1 -1
- package/dist/cdn/chunks/{RestoreVersionDialog-DNoo-EBT.js → RestoreVersionDialog-DGOXQ0ou.js} +5 -5
- package/dist/cdn/chunks/{RestoreVersionDialog-DNoo-EBT.js.map → RestoreVersionDialog-DGOXQ0ou.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-BQATmyd9.js → RichTextEditorContent-DpvOKKH-.js} +7 -7
- package/dist/cdn/chunks/{RichTextEditorContent-BQATmyd9.js.map → RichTextEditorContent-DpvOKKH-.js.map} +1 -1
- package/dist/cdn/chunks/{SaveBlockDialog-DB37Vmqt.js → SaveBlockDialog-Dod3jX9X.js} +8 -8
- package/dist/cdn/chunks/{SaveBlockDialog-DB37Vmqt.js.map → SaveBlockDialog-Dod3jX9X.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-CTU2rGWQ.js → SavedBlocksBrowserModal-BysGB8zb.js} +9 -9
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-CTU2rGWQ.js.map → SavedBlocksBrowserModal-BysGB8zb.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksPanels-B9h9wI6d.js → SavedBlocksPanels-DJ4fceEM.js} +3 -3
- package/dist/cdn/chunks/{SavedBlocksPanels-B9h9wI6d.js.map → SavedBlocksPanels-DJ4fceEM.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksPickBar-CTY5WZum.js → SavedBlocksPickBar-DGgbqAsb.js} +5 -5
- package/dist/cdn/chunks/{SavedBlocksPickBar-CTY5WZum.js.map → SavedBlocksPickBar-DGgbqAsb.js.map} +1 -1
- package/dist/cdn/chunks/{TemplateScoringPanel-DZ0cpe_D.js → TemplateScoringPanel-B0YcaJsR.js} +6 -6
- package/dist/cdn/chunks/{TemplateScoringPanel-DZ0cpe_D.js.map → TemplateScoringPanel-B0YcaJsR.js.map} +1 -1
- package/dist/cdn/chunks/{TestEmailModal-Dqx1RyDE.js → TestEmailModal-DvZFnhWa.js} +10 -10
- package/dist/cdn/chunks/{TestEmailModal-Dqx1RyDE.js.map → TestEmailModal-DvZFnhWa.js.map} +1 -1
- package/dist/cdn/chunks/{TestEmailPanel-DrcQhnON.js → TestEmailPanel-BdxpMiWc.js} +3 -3
- package/dist/cdn/chunks/{TestEmailPanel-DrcQhnON.js.map → TestEmailPanel-BdxpMiWc.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-CSeDvWVn.js → TitleEditor-CfHWnThg.js} +12 -12
- package/dist/cdn/chunks/{TitleEditor-CSeDvWVn.js.map → TitleEditor-CfHWnThg.js.map} +1 -1
- package/dist/cdn/chunks/{TplModal-BNBLwBxJ.js → TplModal-BMTW5bEN.js} +5 -5
- package/dist/cdn/chunks/{TplModal-BNBLwBxJ.js.map → TplModal-BMTW5bEN.js.map} +1 -1
- package/dist/cdn/chunks/{VersionHistoryMenu-cZlb0sMi.js → VersionHistoryMenu-DJGcA_RX.js} +5 -5
- package/dist/cdn/chunks/{VersionHistoryMenu-cZlb0sMi.js.map → VersionHistoryMenu-DJGcA_RX.js.map} +1 -1
- package/dist/cdn/chunks/{VersionHistoryPanels-DgrjTK3z.js → VersionHistoryPanels-2l_FLtix.js} +3 -3
- package/dist/cdn/chunks/{VersionHistoryPanels-DgrjTK3z.js.map → VersionHistoryPanels-2l_FLtix.js.map} +1 -1
- package/dist/cdn/chunks/{VersionPreviewBanner-BNq7PRun.js → VersionPreviewBanner-C8pfES5A.js} +4 -4
- package/dist/cdn/chunks/{VersionPreviewBanner-BNq7PRun.js.map → VersionPreviewBanner-C8pfES5A.js.map} +1 -1
- package/dist/cdn/chunks/{blockTypeIcons-BNGmmzB_.js → blockTypeIcons-JUwZtKaq.js} +2 -2
- package/dist/cdn/chunks/{blockTypeIcons-BNGmmzB_.js.map → blockTypeIcons-JUwZtKaq.js.map} +1 -1
- package/dist/{ca-DY0AYOLG.js → cdn/chunks/ca-DZSrLNXK.js} +4 -0
- package/dist/cdn/chunks/ca-DZSrLNXK.js.map +1 -0
- package/dist/cdn/chunks/{cloud-CRCcXTc5.js → cloud-CITqEHEc.js} +3 -3
- package/dist/cdn/chunks/{cloud-CRCcXTc5.js.map → cloud-CITqEHEc.js.map} +1 -1
- package/dist/cdn/chunks/{createCloudRuntime-B_HRgFxU.js → createCloudRuntime-jLhuyKds.js} +8 -8
- package/dist/cdn/chunks/{createCloudRuntime-B_HRgFxU.js.map → createCloudRuntime-jLhuyKds.js.map} +1 -1
- package/dist/{de-CYLWugqb.js → cdn/chunks/de-DbayqpK-.js} +4 -0
- package/dist/cdn/chunks/{de-CYLWugqb.js.map → de-DbayqpK-.js.map} +1 -1
- package/dist/cdn/chunks/{dist-eKG4m5Wx.js → dist-CgR3inPR.js} +3 -3
- package/dist/cdn/chunks/{dist-eKG4m5Wx.js.map → dist-CgR3inPR.js.map} +1 -1
- package/dist/cdn/chunks/{dist-2fOlspdg.js → dist-El3fQnfk.js} +4 -4
- package/dist/cdn/chunks/{dist-2fOlspdg.js.map → dist-El3fQnfk.js.map} +1 -1
- package/dist/cdn/chunks/{draggable-BLMjz0GK.js → draggable-D2vo-aQI.js} +1465 -1435
- package/dist/cdn/chunks/draggable-D2vo-aQI.js.map +1 -0
- package/dist/cdn/chunks/en-CwXyUb6-.js +2 -0
- package/dist/{en-C3TM249M.js → cdn/chunks/en-l0ib5X0f.js} +4 -0
- package/dist/cdn/chunks/en-l0ib5X0f.js.map +1 -0
- package/dist/cdn/chunks/{es-D1SF4nCR.js → es-CMQ-5PJP.js} +3 -1
- package/dist/cdn/chunks/es-CMQ-5PJP.js.map +1 -0
- package/dist/cdn/chunks/{extensions-Deqilwp8.js → extensions-D30V0hzj.js} +7 -7
- package/dist/cdn/chunks/{extensions-Deqilwp8.js.map → extensions-D30V0hzj.js.map} +1 -1
- package/dist/cdn/chunks/{fr-CdLhJ7kw.js → fr-DNXmZWAq.js} +3 -1
- package/dist/cdn/chunks/fr-DNXmZWAq.js.map +1 -0
- package/dist/cdn/chunks/{icons-C2kP4vOJ.js → icons-BTdLXRXn.js} +13 -13
- package/dist/cdn/chunks/icons-BTdLXRXn.js.map +1 -0
- package/dist/cdn/chunks/{keys-D7OpfE2s.js → keys-BJ770Mwm.js} +2 -2
- package/dist/cdn/chunks/{keys-D7OpfE2s.js.map → keys-BJ770Mwm.js.map} +1 -1
- package/dist/cdn/chunks/{liquid.browser-UlAOKBjI.js → liquid.browser-BPvKXNfO.js} +2 -2
- package/dist/cdn/chunks/{liquid.browser-UlAOKBjI.js.map → liquid.browser-BPvKXNfO.js.map} +1 -1
- package/dist/{nl-BvsLoyKp.js → cdn/chunks/nl-BylRRHYJ.js} +4 -0
- package/dist/cdn/chunks/nl-BylRRHYJ.js.map +1 -0
- package/dist/cdn/chunks/{preRenderCustomBlocks-CbA3RcAy.js → preRenderCustomBlocks-DyCIgmqJ.js} +2 -2
- package/dist/cdn/chunks/{preRenderCustomBlocks-CbA3RcAy.js.map → preRenderCustomBlocks-DyCIgmqJ.js.map} +1 -1
- package/dist/cdn/chunks/{pt-BR-CAGS5YXp.js → pt-BR-BVoJhpie.js} +3 -1
- package/dist/cdn/chunks/pt-BR-BVoJhpie.js.map +1 -0
- package/dist/cdn/chunks/{src-CgIXdQlX.js → src-65H3RNcb.js} +8 -8
- package/dist/cdn/chunks/{src-CgIXdQlX.js.map → src-65H3RNcb.js.map} +1 -1
- package/dist/cdn/chunks/{src-Cx70q30o.js → src-6J-yyZoD.js} +2 -2
- package/dist/cdn/chunks/{src-Cx70q30o.js.map → src-6J-yyZoD.js.map} +1 -1
- package/dist/cdn/chunks/{src-B_QJduQD.js → src-BD60gqPW.js} +72 -67
- package/dist/cdn/chunks/src-BD60gqPW.js.map +1 -0
- package/dist/cdn/chunks/{src-CnQgwcsc.js → src-CLTXM4PM.js} +166 -136
- package/dist/cdn/chunks/src-CLTXM4PM.js.map +1 -0
- package/dist/cdn/chunks/{tiptap-DOw5Yb4H.js → tiptap-Bqy-H6CT.js} +2295 -2205
- package/dist/cdn/chunks/tiptap-Bqy-H6CT.js.map +1 -0
- package/dist/cdn/chunks/{useAliveFlag-C1pwLtPO.js → useAliveFlag-DrXxPV8V.js} +2 -2
- package/dist/cdn/chunks/{useAliveFlag-C1pwLtPO.js.map → useAliveFlag-DrXxPV8V.js.map} +1 -1
- package/dist/cdn/chunks/{useCloudI18n-D4gzxtqF.js → useCloudI18n-D0AoLV0a.js} +3 -3
- package/dist/cdn/chunks/{useCloudI18n-D4gzxtqF.js.map → useCloudI18n-D0AoLV0a.js.map} +1 -1
- package/dist/cdn/chunks/{useEditorCore-Dq5gdn5i.js → useEditorCore-BQeEwEQ9.js} +356 -361
- package/dist/cdn/chunks/{useEditorCore-Dq5gdn5i.js.map → useEditorCore-BQeEwEQ9.js.map} +1 -1
- package/dist/cdn/chunks/useEditorRoot-BFGRBGts.js +10 -0
- package/dist/cdn/chunks/{useEditorRoot-BY-amddA.js.map → useEditorRoot-BFGRBGts.js.map} +1 -1
- package/dist/cdn/chunks/{useI18n-DYb1SgLB.js → useI18n-DIhawILp.js} +3 -3
- package/dist/cdn/chunks/{useI18n-DYb1SgLB.js.map → useI18n-DIhawILp.js.map} +1 -1
- package/dist/cdn/chunks/{useMergeTag-D-SE_XN8.js → useMergeTag-lWo7So18.js} +4 -4
- package/dist/cdn/chunks/{useMergeTag-D-SE_XN8.js.map → useMergeTag-lWo7So18.js.map} +1 -1
- package/dist/cdn/chunks/{usePopoverPosition-CJZ6EkS4.js → usePopoverPosition-DkiTLnBm.js} +3 -3
- package/dist/cdn/chunks/{usePopoverPosition-CJZ6EkS4.js.map → usePopoverPosition-DkiTLnBm.js.map} +1 -1
- package/dist/cdn/chunks/{useScrollToBlock-D6gyIzT1.js → useScrollToBlock-KcSAuXKz.js} +3 -3
- package/dist/cdn/chunks/{useScrollToBlock-D6gyIzT1.js.map → useScrollToBlock-KcSAuXKz.js.map} +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +1036 -1002
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-Cv96tY88.js → check-CTe6VejT.js} +1 -1
- package/dist/{chevron-down-BwEyjSjd.js → chevron-down-BBThVMu-.js} +1 -1
- package/dist/{chevron-up-C6Ye-Nzc.js → chevron-up-iECwf-Bc.js} +1 -1
- package/dist/{circle-alert-BLt_gkcU.js → circle-alert-Z_eeykrO.js} +1 -1
- package/dist/{clock-D-2BI0Co.js → clock-CEj1c29j.js} +1 -1
- package/dist/{cloud-DXFUA8H_.js → cloud-DwJ8ebNl.js} +2 -2
- package/dist/{createCloudRuntime-CWPQ7puE.js → createCloudRuntime-CdXQdl34.js} +6 -6
- package/dist/{createLucideIcon-DGRWivCX.js → createLucideIcon-DphAT3tw.js} +3 -3
- package/dist/{cdn/chunks/de-CYLWugqb.js → de-DbayqpK-.js} +2 -2
- package/dist/{dist-DQ_4a0hb.js → dist-9fjN9cuD.js} +2 -2
- package/dist/{dist-WE11SQGe.js → dist-BUpBA58N.js} +2 -2
- package/dist/{dist-DAR13YWM.js → dist-BdIwIlKz.js} +2 -2
- package/dist/{dist-Btz6cFCI.js → dist-BfcqryVg.js} +4 -4
- package/dist/{dist-q3Puexqv.js → dist-CaR5TLiM.js} +2 -2
- package/dist/{dist-CkSgS-K9.js → dist-CuuPnIMy.js} +3 -3
- package/dist/dist-CzMzshfS.js +5 -0
- package/dist/{dist-B1kLn1rs.js → dist-CzVIVbSa.js} +90 -85
- package/dist/{dist-BIFUEhwQ.js → dist-D2P8U3p6.js} +2 -2
- package/dist/{dist-BtPhkQ2u.js → dist-DLaIQ5H5.js} +12 -8
- package/dist/{dist-BG2cHdqw.js → dist-DSgmnQ-f.js} +2 -2
- package/dist/dist-DTVuNn8M.js +5 -0
- package/dist/{dist-CHp21juN.js → dist-DifptC-U.js} +2 -2
- package/dist/{dist-BpHkLruF.js → dist-gWM8H2hz.js} +4 -4
- package/dist/{dist-BUh4MT4D.js → dist-nmzPlqlz.js} +266 -176
- package/dist/{cdn/chunks/en-C3TM249M.js → en-l0ib5X0f.js} +2 -2
- package/dist/{es-D1SF4nCR.js → es-CMQ-5PJP.js} +2 -0
- package/dist/{extensions-BlYe3NFn.js → extensions-Bjsdx6Vm.js} +18 -18
- package/dist/{fr-CdLhJ7kw.js → fr-DNXmZWAq.js} +2 -0
- package/dist/{image-up-KRq0lcdU.js → image-up-W1INxCu8.js} +1 -1
- package/dist/{info-DbmE2JAo.js → info-5t4WYKfh.js} +1 -1
- package/dist/{keys-CAor2T5q.js → keys-BJtoezGU.js} +1 -1
- package/dist/{liquid.browser-B-rGrnrQ.js → liquid.browser-CJgDDvQz.js} +1 -1
- package/dist/{loader-circle-dSap9DfQ.js → loader-circle-DJCZ_JMY.js} +1 -1
- package/dist/{message-circle-qSKKLbac.js → message-circle-Biu6lHh_.js} +1 -1
- package/dist/{cdn/chunks/nl-BvsLoyKp.js → nl-BylRRHYJ.js} +2 -2
- package/dist/{pencil-DMP8vL2F.js → pencil-DkPlMca7.js} +1 -1
- package/dist/{preRenderCustomBlocks-BebjKtmw.js → preRenderCustomBlocks-2KyBJocY.js} +1 -1
- package/dist/{pt-BR-CAGS5YXp.js → pt-BR-BVoJhpie.js} +2 -0
- package/dist/{refresh-cw-WXtBeMva.js → refresh-cw-DHbpkkol.js} +1 -1
- package/dist/{runtime-dom.esm-bundler-CHEsXNQz.js → runtime-dom.esm-bundler-Co_SPqAz.js} +180 -166
- package/dist/{scan-line-BTrngVuN.js → scan-line-DojwGzjU.js} +1 -1
- package/dist/{send-CLa8Cxmh.js → send-c6ppcE9I.js} +1 -1
- package/dist/{shield-check-42WZrKQu.js → shield-check-F9T4ylNK.js} +1 -1
- package/dist/{sparkles-DcOb0umz.js → sparkles-D-0OiTZD.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/templatical-editor.js +1083 -1049
- package/dist/{text-align-start-zoZhXJ2T.js → text-align-start-kEEtoRrD.js} +1 -1
- package/dist/{trash-2-Capv4cSE.js → trash-2-DRQzhY9R.js} +1 -1
- package/dist/{triangle-alert-CnJUGX-6.js → triangle-alert-Bt4-GU3t.js} +1 -1
- package/dist/{upload-Bc0ZTywl.js → upload-D2wtjvH8.js} +1 -1
- package/dist/{useAliveFlag-1yBub211.js → useAliveFlag-AFbwfHlJ.js} +1 -1
- package/dist/{useCloudI18n-Dwo7usTl.js → useCloudI18n-BA69BpQ_.js} +2 -2
- package/dist/{useEditorCore-CIjO_NM9.js → useEditorCore-Du9Vx-qr.js} +873 -878
- package/dist/{useEditorRoot-CUIzpMYc.js → useEditorRoot-Cdo8Vhxa.js} +2 -2
- package/dist/{useI18n-DT_DKupw.js → useI18n-B135ASaG.js} +2 -2
- package/dist/{useMergeTag-CcHQCcPN.js → useMergeTag-Cx36rtrt.js} +5 -5
- package/dist/{usePopoverPosition-DaHz1kqb.js → usePopoverPosition-DAOHr_iM.js} +2 -2
- package/dist/{useScrollToBlock-C2-9xCr7.js → useScrollToBlock-B0Qpjfa4.js} +3 -3
- package/dist/{vue.runtime.esm-bundler-CdH_W9qX.js → vue.runtime.esm-bundler-BhOUdXMi.js} +1244 -1204
- package/dist/{x-CvPZ3OAd.js → x-YiG-TUGN.js} +1 -1
- package/package.json +25 -25
- package/dist/cdn/chunks/NumberWithSuffix-CWvM_I3T.js.map +0 -1
- package/dist/cdn/chunks/ca-DY0AYOLG.js.map +0 -1
- package/dist/cdn/chunks/draggable-BLMjz0GK.js.map +0 -1
- package/dist/cdn/chunks/en-BZlZVGwM.js +0 -2
- package/dist/cdn/chunks/en-C3TM249M.js.map +0 -1
- package/dist/cdn/chunks/es-D1SF4nCR.js.map +0 -1
- package/dist/cdn/chunks/fr-CdLhJ7kw.js.map +0 -1
- package/dist/cdn/chunks/icons-C2kP4vOJ.js.map +0 -1
- package/dist/cdn/chunks/nl-BvsLoyKp.js.map +0 -1
- package/dist/cdn/chunks/pt-BR-CAGS5YXp.js.map +0 -1
- package/dist/cdn/chunks/src-B_QJduQD.js.map +0 -1
- package/dist/cdn/chunks/src-CnQgwcsc.js.map +0 -1
- package/dist/cdn/chunks/tiptap-DOw5Yb4H.js.map +0 -1
- package/dist/cdn/chunks/useEditorRoot-BY-amddA.js +0 -10
- package/dist/dist-CCfNhI54.js +0 -5
- package/dist/dist-CMcNDUcw.js +0 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-CRCcXTc5.js","names":[],"sources":["../../../../core/src/cloud/auth.ts","../../../../core/src/cloud/url-builder.ts","../../../../core/src/cloud/api.ts","../../../../core/src/cloud/websocket-client.ts","../../../../core/src/cloud/mcp-operation-handler.ts","../../../../core/src/cloud/ai-chat.ts","../../../../core/src/cloud/ai-config.ts","../../../../core/src/cloud/template-scoring.ts","../../../../core/src/cloud/design-reference.ts","../../../../core/src/cloud/comments-provider.ts","../../../../core/src/cloud/collaboration.ts","../../../../core/src/cloud/collaboration-broadcast.ts","../../../../core/src/cloud/web-socket.ts","../../../../core/src/cloud/saved-blocks-provider.ts","../../../../core/src/cloud/templates-provider.ts","../../../../core/src/cloud/export.ts","../../../../core/src/cloud/render-provider.ts","../../../../core/src/cloud/version-history-provider.ts","../../../../core/src/cloud/test-email.ts","../../../../core/src/cloud/test-email-provider.ts","../../../../core/src/cloud/plan-config.ts","../../../../core/src/cloud/health-check.ts","../../../../core/src/cloud/mcp-listener.ts"],"sourcesContent":["import type {\n AuthConfig,\n AuthRequestOptions,\n SdkAuthConfig,\n TestEmailConfig,\n TokenData,\n UserConfig,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\n\nexport type {\n AuthConfig,\n AuthRequestOptions,\n SdkAuthConfig,\n TestEmailConfig,\n UserConfig,\n};\n\n/**\n * Whether a token-refresh failure is worth interrupting the user over.\n *\n * **Fatal** means the endpoint actively rejected the credentials — a `4xx`. No\n * amount of retrying fixes that: every subsequent request will fail the same\n * way, so the session really is over and the editor should say so.\n *\n * **Everything else is transient**: a network blip, a `5xx`, a timeout, a\n * malformed response. Those resolve on their own, and blanking the editor over\n * one interrupts someone mid-edit on a template that is very likely unsaved.\n * `AuthManager` reports every failure to `onError` and re-throws regardless, so\n * a caller that genuinely cannot proceed still finds out; this only decides\n * whether the *overlay* goes up.\n *\n * `404` is treated as fatal deliberately: a refresh URL that does not exist is a\n * misconfiguration, and retrying a typo forever is worse than saying so.\n */\nexport function isFatalAuthError(error: unknown): boolean {\n const status = error instanceof SdkError ? error.statusCode : undefined;\n return status !== undefined && status >= 400 && status < 500;\n}\n\nexport class AuthManager {\n private static readonly DEFAULT_BASE_URL = \"https://templatical.com\";\n\n private accessToken: string | null = null;\n private expiresAt: Date | null = null;\n private _projectId: string | null = null;\n private _tenantId: string | null = null;\n private _tenantSlug: string | null = null;\n private _testEmailConfig: TestEmailConfig | null = null;\n private _userConfig: UserConfig | null = null;\n private readonly url: string;\n private readonly baseUrl: string;\n private readonly requestOptions: AuthRequestOptions;\n private readonly onError?: (error: Error) => void;\n private refreshPromise: Promise<string> | null = null;\n\n private static readonly REFRESH_THRESHOLD_MS = 60 * 1000;\n\n constructor(config: AuthConfig) {\n this.url = config.url;\n this.baseUrl = (config.baseUrl ?? AuthManager.DEFAULT_BASE_URL).replace(\n /\\/$/,\n \"\",\n );\n this.requestOptions = config.requestOptions ?? {};\n this.onError = config.onError;\n }\n\n resolveUrl(path: string): string {\n if (path.startsWith(\"http://\") || path.startsWith(\"https://\")) {\n return path;\n }\n\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${this.baseUrl}${normalizedPath}`;\n }\n\n get projectId(): string {\n if (!this._projectId) {\n throw new Error(\"Project ID not available. Call initialize() first.\");\n }\n return this._projectId;\n }\n\n get tenantId(): string {\n if (!this._tenantId) {\n throw new Error(\"Tenant ID not available. Call initialize() first.\");\n }\n return this._tenantId;\n }\n\n get tenantSlug(): string {\n if (!this._tenantSlug) {\n throw new Error(\"Tenant slug not available. Call initialize() first.\");\n }\n return this._tenantSlug;\n }\n\n get testEmailConfig(): TestEmailConfig | null {\n return this._testEmailConfig;\n }\n\n get userConfig(): UserConfig | null {\n return this._userConfig;\n }\n\n get accessTokenValue(): string | null {\n return this.accessToken;\n }\n\n async initialize(): Promise<void> {\n await this.ensureToken();\n }\n\n private async ensureToken(): Promise<string> {\n if (this.accessToken && !this.isTokenExpiringSoon()) {\n return this.accessToken;\n }\n return this.refreshToken();\n }\n\n private isTokenExpiringSoon(): boolean {\n if (!this.expiresAt) {\n return true;\n }\n const timeUntilExpiry = this.expiresAt.getTime() - Date.now();\n return timeUntilExpiry < AuthManager.REFRESH_THRESHOLD_MS;\n }\n\n async refreshToken(): Promise<string> {\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.performRefresh();\n\n try {\n const token = await this.refreshPromise;\n return token;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async performRefresh(): Promise<string> {\n try {\n const method = this.requestOptions.method ?? \"POST\";\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n ...this.requestOptions.headers,\n };\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n credentials: this.requestOptions.credentials ?? \"include\",\n };\n\n if (method === \"POST\" && this.requestOptions.body) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchOptions.body = JSON.stringify(this.requestOptions.body);\n }\n\n const response = await fetch(this.url, fetchOptions);\n\n if (!response.ok) {\n throw new SdkError(\n `Token refresh failed: ${response.status}`,\n response.status,\n );\n }\n\n const data: TokenData = await response.json();\n\n if (!data.token || !data.expires_at || !data.project_id || !data.tenant) {\n throw new Error(\n \"Invalid token response: missing token, expires_at, project_id, or tenant\",\n );\n }\n\n this.accessToken = data.token;\n this.expiresAt = new Date(data.expires_at * 1000);\n this._projectId = data.project_id;\n this._tenantSlug = data.tenant;\n\n if (data.test_email?.allowed_emails && data.test_email?.signature) {\n this._testEmailConfig = {\n allowedEmails: data.test_email.allowed_emails,\n signature: data.test_email.signature,\n };\n } else {\n this._testEmailConfig = null;\n }\n\n if (data.user?.id && data.user?.name && data.user?.signature) {\n this._userConfig = {\n id: data.user.id,\n name: data.user.name,\n signature: data.user.signature,\n };\n } else {\n this._userConfig = null;\n }\n\n return this.accessToken;\n } catch (error) {\n const wrappedError =\n error instanceof Error\n ? error\n : new Error(\"Token refresh failed\", { cause: error });\n this.onError?.(wrappedError);\n throw wrappedError;\n }\n }\n\n async authenticatedFetch(\n url: string,\n options: RequestInit = {},\n ): Promise<Response> {\n const token = await this.ensureToken();\n const resolvedUrl = this.resolveUrl(url);\n\n const makeRequest = async (authToken: string): Promise<Response> => {\n return fetch(resolvedUrl, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${authToken}`,\n },\n });\n };\n\n let response = await makeRequest(token);\n\n if (response.status === 401) {\n const newToken = await this.refreshToken();\n response = await makeRequest(newToken);\n }\n\n return response;\n }\n}\n\nexport function createSdkAuthManager(\n config: SdkAuthConfig,\n onError?: (error: Error) => void,\n): AuthManager {\n if (config.mode === \"direct\") {\n const baseUrl = (config.baseUrl ?? \"https://templatical.com\").replace(\n /\\/$/,\n \"\",\n );\n\n return new AuthManager({\n url: `${baseUrl}/api/v1/auth/token`,\n baseUrl: config.baseUrl,\n requestOptions: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: {\n client_id: config.clientId,\n client_secret: config.clientSecret,\n tenant: config.tenant,\n client_type: \"sdk\",\n },\n },\n onError,\n });\n }\n\n return new AuthManager({\n url: config.url,\n baseUrl: config.baseUrl,\n requestOptions: config.requestOptions,\n onError,\n });\n}\n","export function buildUrl(\n template: string,\n params: Record<string, string>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, key: string) =>\n encodeURIComponent(params[key] ?? \"\"),\n );\n}\n\nconst BASE = \"/api/v1/projects/{project}/tenants/{tenant}\";\nconst TEMPLATE = `${BASE}/templates/{template}`;\nconst AI = `${TEMPLATE}/ai`;\nconst MEDIA = `${BASE}/media`;\nconst FOLDERS = `${MEDIA}/folders`;\nconst MODULES = `${BASE}/saved-modules`;\n\nexport const API_ROUTES = {\n health: \"/api/v1/health\",\n \"projects.config\": `${BASE}/config`,\n \"broadcasting.auth\": `${BASE}/broadcasting/auth`,\n \"templates.store\": `${BASE}/templates`,\n \"templates.show\": `${TEMPLATE}`,\n \"templates.update\": `${TEMPLATE}`,\n \"templates.destroy\": `${TEMPLATE}`,\n \"templates.export\": `${TEMPLATE}/export`,\n \"templates.importFromBeefree\": `${BASE}/templates/import/from-beefree`,\n \"templates.sendTestEmail\": `${TEMPLATE}/send-test-email`,\n \"versions.index\": `${TEMPLATE}/versions`,\n \"versions.store\": `${TEMPLATE}/versions`,\n \"versions.show\": `${TEMPLATE}/versions/{version}`,\n \"versions.restore\": `${TEMPLATE}/versions/{version}/restore`,\n \"comments.index\": `${TEMPLATE}/comments`,\n \"comments.store\": `${TEMPLATE}/comments`,\n \"comments.update\": `${TEMPLATE}/comments/{comment}`,\n \"comments.destroy\": `${TEMPLATE}/comments/{comment}`,\n \"comments.resolve\": `${TEMPLATE}/comments/{comment}/resolve`,\n \"ai.generate\": `${AI}/generate`,\n \"ai.conversationMessages\": `${AI}/conversation-messages`,\n \"ai.suggestions\": `${AI}/suggestions`,\n \"ai.rewriteText\": `${AI}/rewrite-text`,\n \"ai.score\": `${AI}/score`,\n \"ai.fixFinding\": `${AI}/fix-finding`,\n \"ai.generateFromDesign\": `${AI}/generate-from-design`,\n \"media.upload\": `${MEDIA}/upload`,\n \"media.browse\": `${MEDIA}/browse`,\n \"media.delete\": `${MEDIA}/delete`,\n \"media.move\": `${MEDIA}/move`,\n \"media.update\": `${MEDIA}/{media}`,\n \"media.replace\": `${MEDIA}/{media}/replace`,\n \"media.checkUsage\": `${MEDIA}/check-usage`,\n \"media.frequentlyUsed\": `${MEDIA}/frequently-used`,\n \"media.importFromUrl\": `${MEDIA}/import-from-url`,\n \"folders.index\": `${FOLDERS}`,\n \"folders.store\": `${FOLDERS}`,\n \"folders.update\": `${FOLDERS}/{mediaFolder}`,\n \"folders.destroy\": `${FOLDERS}/{mediaFolder}`,\n \"savedModules.index\": `${MODULES}`,\n \"savedModules.store\": `${MODULES}`,\n \"savedModules.update\": `${MODULES}/{savedModule}`,\n \"savedModules.destroy\": `${MODULES}/{savedModule}`,\n} as const;\n","import type {\n ApiError,\n ApiResponse,\n CommentResponse,\n CustomFont,\n PlanConfig,\n SavedBlock,\n Template,\n TemplateContent,\n TemplatePatch,\n TemplateVersionResponse,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport type { Block } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\n\nexport class ApiClient {\n constructor(private readonly authManager: AuthManager) {}\n\n private get projectId(): string {\n return this.authManager.projectId;\n }\n\n private get tenantSlug(): string {\n return this.authManager.tenantSlug;\n }\n\n private get baseParams(): Record<string, string> {\n return { project: this.projectId, tenant: this.tenantSlug };\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const response = await this.authManager.authenticatedFetch(path, {\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n\n const errorMessage = this.extractFirstValidationError(error);\n throw new SdkError(errorMessage, response.status);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const json: ApiResponse<T> = await response.json();\n return json.data;\n }\n\n private extractFirstValidationError(error: ApiError): string {\n if (error.errors) {\n const firstField = Object.keys(error.errors)[0];\n if (firstField && error.errors[firstField]?.length > 0) {\n return error.errors[firstField][0];\n }\n }\n return error.message;\n }\n\n async createTemplate(\n content: TemplateContent,\n name?: string,\n ): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.store\"], this.baseParams),\n {\n method: \"POST\",\n // `name` is forwarded only when set. Cloud's backend does not store it\n // yet; until it does the key is simply ignored, and an absent name reads\n // as \"unnamed\" rather than as an explicit null — the same\n // forward-compatible treatment `category` gets on saved blocks.\n body: JSON.stringify(\n name !== undefined ? { content, name } : { content },\n ),\n },\n );\n }\n\n async getTemplate(id: string): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.show\"], {\n ...this.baseParams,\n template: id,\n }),\n );\n }\n\n /**\n * Apply a partial update. Takes a patch rather than bare content so a rename\n * can travel without content and vice versa — the shape `TemplatePatch`\n * defines, which is what `createCloudTemplatesProvider` forwards verbatim. Only\n * the keys present are sent.\n */\n async updateTemplate(id: string, patch: TemplatePatch): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.update\"], {\n ...this.baseParams,\n template: id,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify(patch),\n },\n );\n }\n\n async createVersion(\n templateId: string,\n content: TemplateContent,\n label?: string,\n ): Promise<TemplateVersionResponse> {\n return this.request<TemplateVersionResponse>(\n buildUrl(API_ROUTES[\"versions.store\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(\n label !== undefined ? { content, label } : { content },\n ),\n },\n );\n }\n\n async deleteTemplate(id: string): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"templates.destroy\"], {\n ...this.baseParams,\n template: id,\n }),\n {\n method: \"DELETE\",\n },\n );\n }\n\n async getVersions(templateId: string): Promise<TemplateVersionResponse[]> {\n return this.request<TemplateVersionResponse[]>(\n buildUrl(API_ROUTES[\"versions.index\"], {\n ...this.baseParams,\n template: templateId,\n }),\n );\n }\n\n async getVersion(\n templateId: string,\n versionId: string,\n ): Promise<TemplateVersionResponse> {\n return this.request<TemplateVersionResponse>(\n buildUrl(API_ROUTES[\"versions.show\"], {\n ...this.baseParams,\n template: templateId,\n version: versionId,\n }),\n );\n }\n\n async restoreVersion(\n templateId: string,\n versionId: string,\n ): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"versions.restore\"], {\n ...this.baseParams,\n template: templateId,\n version: versionId,\n }),\n {\n method: \"POST\",\n },\n );\n }\n\n async exportTemplate(\n templateId: string,\n fontsPayload?: {\n customFonts: CustomFont[];\n defaultFallback: string;\n },\n ): Promise<{ html: string; mjml: string }> {\n const body = fontsPayload\n ? JSON.stringify({\n custom_fonts: fontsPayload.customFonts,\n default_fallback: fontsPayload.defaultFallback,\n })\n : undefined;\n\n return this.request<{ html: string; mjml: string }>(\n buildUrl(API_ROUTES[\"templates.export\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body,\n },\n );\n }\n\n async sendTestEmail(\n templateId: string,\n payload: {\n recipient: string;\n html: string;\n allowed_emails: string[];\n signature: string;\n },\n ): Promise<void> {\n await this.request<void>(\n buildUrl(API_ROUTES[\"templates.sendTestEmail\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n }\n\n private commentsUrl(templateId: string, commentId?: string): string {\n if (commentId) {\n return buildUrl(API_ROUTES[\"comments.update\"], {\n ...this.baseParams,\n template: templateId,\n comment: commentId,\n });\n }\n return buildUrl(API_ROUTES[\"comments.index\"], {\n ...this.baseParams,\n template: templateId,\n });\n }\n\n async getComments(templateId: string): Promise<CommentResponse[]> {\n return this.request<CommentResponse[]>(this.commentsUrl(templateId));\n }\n\n async createComment(\n templateId: string,\n data: {\n body: string;\n block_id?: string;\n parent_id?: string;\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(this.commentsUrl(templateId), {\n method: \"POST\",\n body: JSON.stringify(data),\n headers,\n });\n }\n\n async updateComment(\n templateId: string,\n commentId: string,\n data: {\n body: string;\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(\n this.commentsUrl(templateId, commentId),\n {\n method: \"PUT\",\n body: JSON.stringify(data),\n headers,\n },\n );\n }\n\n async deleteComment(\n templateId: string,\n commentId: string,\n data: {\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<void> {\n return this.request<void>(this.commentsUrl(templateId, commentId), {\n method: \"DELETE\",\n body: JSON.stringify(data),\n headers,\n });\n }\n\n async resolveComment(\n templateId: string,\n commentId: string,\n data: {\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(\n buildUrl(API_ROUTES[\"comments.resolve\"], {\n ...this.baseParams,\n template: templateId,\n comment: commentId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(data),\n headers,\n },\n );\n }\n\n async fetchConfig(): Promise<PlanConfig> {\n return this.request<PlanConfig>(\n buildUrl(API_ROUTES[\"projects.config\"], this.baseParams),\n );\n }\n\n async listModules(search?: string, category?: string): Promise<SavedBlock[]> {\n const url = buildUrl(API_ROUTES[\"savedModules.index\"], this.baseParams);\n const params = new URLSearchParams();\n if (search) params.set(\"search\", search);\n if (category) params.set(\"category\", category);\n const query = params.size > 0 ? `?${params}` : \"\";\n return this.request<SavedBlock[]>(`${url}${query}`);\n }\n\n async createModule(data: {\n name: string;\n content: Block[];\n category?: string;\n }): Promise<SavedBlock> {\n return this.request<SavedBlock>(\n buildUrl(API_ROUTES[\"savedModules.store\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify(data),\n },\n );\n }\n\n async updateModule(\n id: string,\n data: Partial<{ name: string; content: Block[]; category: string }>,\n ): Promise<SavedBlock> {\n return this.request<SavedBlock>(\n buildUrl(API_ROUTES[\"savedModules.update\"], {\n ...this.baseParams,\n savedModule: id,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify(data),\n },\n );\n }\n\n async deleteModule(id: string): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"savedModules.destroy\"], {\n ...this.baseParams,\n savedModule: id,\n }),\n {\n method: \"DELETE\",\n },\n );\n }\n}\n","import type { WebSocketServerConfig } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type Pusher from \"pusher-js\";\nimport type { Channel, PresenceChannel } from \"pusher-js\";\n\ntype PusherType = Pusher;\n\nexport interface WebSocketConfig {\n host: string;\n port: number;\n appKey: string;\n}\n\nexport function resolveWebSocketConfig(\n serverConfig: WebSocketServerConfig,\n): WebSocketConfig {\n return {\n host: serverConfig.host,\n port: serverConfig.port,\n appKey: serverConfig.app_key,\n };\n}\n\nexport interface PresenceMember {\n id: string;\n name: string;\n type: \"user\" | \"mcp\";\n}\n\nexport interface WebSocketClientOptions {\n authManager: AuthManager;\n config: WebSocketConfig;\n onError?: (error: Error) => void;\n}\n\nexport class WebSocketClient {\n private pusher: PusherType | null = null;\n private readonly authManager: AuthManager;\n private readonly config: WebSocketConfig;\n private readonly onError?: (error: Error) => void;\n\n constructor(options: WebSocketClientOptions) {\n this.authManager = options.authManager;\n this.config = options.config;\n this.onError = options.onError;\n }\n\n async connect(): Promise<void> {\n if (this.pusher) {\n return;\n }\n\n let Pusher: typeof import(\"pusher-js\").default;\n try {\n ({ default: Pusher } = await import(\"pusher-js\"));\n } catch {\n throw new Error(\n \"[Templatical] Cloud features require the optional peer dependency 'pusher-js'. Please install it.\",\n );\n }\n\n const { host, port, appKey } = this.config;\n const authEndpoint = this.authManager.resolveUrl(\n buildUrl(API_ROUTES[\"broadcasting.auth\"], {\n project: this.authManager.projectId,\n tenant: this.authManager.tenantSlug,\n }),\n );\n\n this.pusher = new Pusher(appKey, {\n wsHost: host,\n wsPort: port,\n wssPort: port,\n forceTLS: true,\n disableStats: true,\n enabledTransports: [\"ws\", \"wss\"],\n cluster: \"\",\n channelAuthorization: {\n transport: \"ajax\",\n endpoint: authEndpoint,\n headers: {\n Authorization: `Bearer ${this.authManager.accessTokenValue}`,\n Accept: \"application/json\",\n },\n params: {\n user_id: this.authManager.userConfig?.id ?? \"\",\n user_name: this.authManager.userConfig?.name ?? \"\",\n user_signature: this.authManager.userConfig?.signature ?? \"\",\n },\n },\n });\n\n this.pusher.connection.bind(\"error\", (error: unknown) => {\n this.onError?.(\n error instanceof Error\n ? error\n : new Error(\"WebSocket connection error\"),\n );\n });\n }\n\n subscribePresence(channelName: string): PresenceChannel {\n if (!this.pusher) {\n throw new Error(\"WebSocketClient not connected. Call connect() first.\");\n }\n\n return this.pusher.subscribe(channelName) as PresenceChannel;\n }\n\n unsubscribe(channelName: string): void {\n this.pusher?.unsubscribe(channelName);\n }\n\n getChannel(channelName: string): Channel | undefined {\n return this.pusher?.channel(channelName);\n }\n\n disconnect(): void {\n if (this.pusher) {\n this.pusher.disconnect();\n this.pusher = null;\n }\n }\n\n getSocketId(): string | null {\n return this.pusher?.connection.socket_id ?? null;\n }\n\n get isConnected(): boolean {\n return this.pusher?.connection.state === \"connected\";\n }\n}\n","import type { McpOperationPayload } from \"@templatical/types\";\nimport type {\n Block,\n TemplateContent,\n TemplateSettings,\n} from \"@templatical/types\";\nimport type { UseEditorReturn } from \"../editor\";\n\nexport function handleOperation(\n editor: UseEditorReturn,\n payload: McpOperationPayload,\n): void {\n const { operation, data } = payload;\n\n switch (operation) {\n case \"add_block\":\n editor.addBlock(\n data.block as Block,\n data.section_id as string | undefined,\n data.column_index as number | undefined,\n data.index as number | undefined,\n );\n break;\n\n case \"update_block\":\n editor.updateBlock(\n data.block_id as string,\n data.updates as Partial<Block>,\n );\n break;\n\n case \"delete_block\":\n editor.removeBlock(data.block_id as string);\n break;\n\n case \"move_block\":\n editor.moveBlock(\n data.block_id as string,\n data.index as number,\n data.section_id as string | undefined,\n data.column_index as number | undefined,\n );\n break;\n\n case \"update_settings\":\n editor.updateSettings(data.updates as Partial<TemplateSettings>);\n break;\n\n case \"set_content\":\n editor.setContent(data.content as TemplateContent);\n break;\n\n case \"update_block_style\":\n editor.updateBlock(\n data.block_id as string,\n {\n styles: data.styles,\n } as Partial<Block>,\n );\n break;\n }\n}\n","import type { AiChatMessage } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { MergeTag, TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseAiChatOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n onApply?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseAiChatReturn {\n messages: ReturnType<typeof ref<AiChatMessage[]>>;\n isGenerating: ReturnType<typeof ref<boolean>>;\n isLoadingHistory: ReturnType<typeof ref<boolean>>;\n isLastChangeReverted: ReturnType<typeof ref<boolean>>;\n lastApplyMessageId: ReturnType<typeof ref<string | null>>;\n error: ReturnType<typeof ref<string | null>>;\n failedPrompt: ReturnType<typeof ref<string | null>>;\n suggestions: ReturnType<typeof ref<string[]>>;\n isLoadingSuggestions: ReturnType<typeof ref<boolean>>;\n sendPrompt: (\n prompt: string,\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<TemplateContent | null>;\n toggleLastRevert: () => void;\n loadConversation: () => Promise<void>;\n loadSuggestions: (\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<void>;\n clearChat: () => void;\n}\n\nlet messageIdCounter = 0;\n\nfunction generateMessageId(): string {\n return `msg_${Date.now()}_${++messageIdCounter}`;\n}\n\nexport function useAiChat(options: UseAiChatOptions): UseAiChatReturn {\n const { authManager, getTemplateId, onApply, onError } = options;\n\n const messages = ref<AiChatMessage[]>([]);\n const isGenerating = ref(false);\n const isLoadingHistory = ref(false);\n const error = ref<string | null>(null);\n const failedPrompt = ref<string | null>(null);\n const conversationId = ref<string | null>(null);\n const lastApplyMessageId = ref<string | null>(null);\n const lastPreviousContent = ref<TemplateContent | null>(null);\n const lastAppliedContent = ref<TemplateContent | null>(null);\n const isLastChangeReverted = ref(false);\n const suggestions = ref<string[]>([]);\n const isLoadingSuggestions = ref(false);\n\n function updateMessage(msgId: string, updates: Partial<AiChatMessage>): void {\n const idx = messages.value.findIndex((m) => m.id === msgId);\n if (idx === -1) {\n return;\n }\n\n const updated = { ...messages.value[idx], ...updates };\n messages.value = [\n ...messages.value.slice(0, idx),\n updated,\n ...messages.value.slice(idx + 1),\n ];\n }\n\n async function loadConversation(): Promise<void> {\n const templateId = getTemplateId();\n if (!templateId) {\n return;\n }\n\n isLoadingHistory.value = true;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.conversationMessages\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n return;\n }\n\n const data = await response.json();\n\n if (data.conversation_id) {\n conversationId.value = data.conversation_id;\n }\n\n if (Array.isArray(data.data) && data.data.length > 0) {\n messages.value = data.data.map(\n (msg: {\n id: string;\n role: \"user\" | \"assistant\";\n content: string;\n created_at: string;\n }) => ({\n id: msg.id,\n role: msg.role,\n content: msg.content,\n timestamp: new Date(msg.created_at).getTime(),\n }),\n );\n }\n } catch {\n // Silently fail - chat will start fresh\n } finally {\n isLoadingHistory.value = false;\n }\n }\n\n async function loadSuggestions(\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<void> {\n const templateId = getTemplateId();\n if (!templateId) {\n return;\n }\n\n isLoadingSuggestions.value = true;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.suggestions\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n current_content: currentContent,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n return;\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"done\" && Array.isArray(event.suggestions)) {\n suggestions.value = event.suggestions.slice(0, 3);\n }\n }\n }\n } catch {\n // Silently fail - suggestions are non-critical\n } finally {\n isLoadingSuggestions.value = false;\n }\n }\n\n async function sendPrompt(\n prompt: string,\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<TemplateContent | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new Error(\"Template must be saved before using AI generation\");\n }\n\n isGenerating.value = true;\n error.value = null;\n failedPrompt.value = null;\n suggestions.value = [];\n\n const userMsgId = generateMessageId();\n messages.value = [\n ...messages.value,\n {\n id: userMsgId,\n role: \"user\",\n content: prompt,\n timestamp: Date.now(),\n },\n ];\n\n const assistantMsgId = generateMessageId();\n messages.value = [\n ...messages.value,\n {\n id: assistantMsgId,\n role: \"assistant\",\n content: \"\",\n timestamp: Date.now(),\n },\n ];\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.generate\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n prompt,\n current_content: currentContent,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n conversation_id: conversationId.value,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null);\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n throw new Error(errorData?.message || \"Failed to generate template\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: TemplateContent | null = null;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n const jsonStr = line.slice(6);\n\n let event;\n try {\n event = JSON.parse(jsonStr);\n } catch {\n continue;\n }\n\n if (event.type === \"text\") {\n updateMessage(assistantMsgId, {\n content:\n (messages.value.find((m) => m.id === assistantMsgId)\n ?.content ?? \"\") + event.text,\n });\n } else if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to generate template\");\n } else if (event.type === \"done\") {\n if (event.conversation_id) {\n conversationId.value = event.conversation_id;\n }\n\n updateMessage(assistantMsgId, {\n content: event.text,\n });\n\n result = event.content ?? null;\n\n if (result) {\n lastPreviousContent.value = currentContent;\n lastAppliedContent.value = result;\n lastApplyMessageId.value = assistantMsgId;\n isLastChangeReverted.value = false;\n onApply?.(result);\n } else {\n error.value = \"ai_apply_failed\";\n }\n }\n }\n }\n } finally {\n // Release the underlying network resource even on early exit.\n // Without this, throwing out of the loop (e.g. on an `error` SSE\n // event) leaves the socket and read buffer attached until GC.\n reader.cancel().catch(() => {});\n }\n\n return result;\n } catch (err) {\n const wrappedError =\n err instanceof Error\n ? err\n : new Error(\"Failed to generate template\", { cause: err });\n error.value = wrappedError.message;\n failedPrompt.value = prompt;\n onError?.(wrappedError);\n\n messages.value = messages.value.filter(\n (m) => m.id !== userMsgId && m.id !== assistantMsgId,\n );\n\n return null;\n } finally {\n isGenerating.value = false;\n }\n }\n\n function toggleLastRevert(): void {\n if (isLastChangeReverted.value) {\n if (lastAppliedContent.value) {\n onApply?.(lastAppliedContent.value);\n }\n isLastChangeReverted.value = false;\n } else {\n if (lastPreviousContent.value) {\n onApply?.(lastPreviousContent.value);\n }\n isLastChangeReverted.value = true;\n }\n }\n\n function clearChat(): void {\n messages.value = [];\n conversationId.value = null;\n error.value = null;\n lastApplyMessageId.value = null;\n lastPreviousContent.value = null;\n lastAppliedContent.value = null;\n isLastChangeReverted.value = false;\n }\n\n return {\n messages,\n isGenerating,\n isLoadingHistory,\n isLastChangeReverted,\n lastApplyMessageId,\n error,\n failedPrompt,\n suggestions,\n isLoadingSuggestions,\n sendPrompt,\n toggleLastRevert,\n loadConversation,\n loadSuggestions,\n clearChat,\n };\n}\n","import type { AiConfig } from \"@templatical/types\";\nimport { computed, type ComputedRef } from \"vue\";\n\nexport interface UseAiConfigReturn {\n isFeatureEnabled: (feature: keyof AiConfig) => boolean;\n hasAnyMenuFeature: ComputedRef<boolean>;\n}\n\nexport function useAiConfig(config?: AiConfig | false): UseAiConfigReturn {\n function isFeatureEnabled(feature: keyof AiConfig): boolean {\n if (config === false) {\n return false;\n }\n\n return config?.[feature] !== false;\n }\n\n const hasAnyMenuFeature = computed(\n () =>\n isFeatureEnabled(\"chat\") ||\n isFeatureEnabled(\"scoring\") ||\n isFeatureEnabled(\"designToTemplate\"),\n );\n\n return {\n isFeatureEnabled,\n hasAnyMenuFeature,\n };\n}\n","import type {\n ScoringCategory,\n ScoringFinding,\n ScoringResult,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { MergeTag, TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseTemplateScoringOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n}\n\nexport interface UseTemplateScoringReturn {\n isScoring: ReturnType<typeof ref<boolean>>;\n scoringResult: ReturnType<typeof ref<ScoringResult | null>>;\n error: ReturnType<typeof ref<string | null>>;\n fixingFindingId: ReturnType<typeof ref<string | null>>;\n fixStreamingText: ReturnType<typeof ref<string>>;\n fixError: ReturnType<typeof ref<string | null>>;\n score: (\n content: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<ScoringResult | null>;\n fixFinding: (\n blockContent: string,\n finding: ScoringFinding,\n mergeTags: MergeTag[],\n ) => Promise<string | null>;\n removeFinding: (category: ScoringCategory, findingId: string) => void;\n reset: () => void;\n}\n\nexport function useTemplateScoring(\n options: UseTemplateScoringOptions,\n): UseTemplateScoringReturn {\n const { authManager, getTemplateId } = options;\n\n const isScoring = ref(false);\n const scoringResult = ref<ScoringResult | null>(null);\n const error = ref<string | null>(null);\n const fixingFindingId = ref<string | null>(null);\n const fixStreamingText = ref(\"\");\n const fixError = ref<string | null>(null);\n\n async function score(\n content: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<ScoringResult | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n return null;\n }\n\n isScoring.value = true;\n error.value = null;\n scoringResult.value = null;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.score\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n current_content: content,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n const errorData = await response.json().catch(() => null);\n throw new Error(errorData?.message || \"Failed to score template\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: ScoringResult | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to score template\");\n }\n\n if (event.type === \"done\") {\n result = event.result ?? null;\n }\n }\n }\n\n if (result) {\n for (const [category, categoryData] of Object.entries(\n result.categories,\n )) {\n for (const finding of categoryData.findings) {\n finding.category = category as ScoringCategory;\n }\n }\n }\n\n scoringResult.value = result;\n return result;\n } catch (err) {\n error.value =\n err instanceof Error ? err.message : \"Failed to score template\";\n return null;\n } finally {\n isScoring.value = false;\n }\n }\n\n async function fixFinding(\n blockContent: string,\n finding: ScoringFinding,\n mergeTags: MergeTag[],\n ): Promise<string | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n return null;\n }\n\n fixingFindingId.value = finding.id;\n fixStreamingText.value = \"\";\n fixError.value = null;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.fixFinding\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n content: blockContent,\n finding: {\n id: finding.id,\n message: finding.message,\n suggestion: finding.suggestion,\n category: finding.category,\n },\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n const errorData = await response.json().catch(() => null);\n throw new Error(errorData?.message || \"Failed to fix finding\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: string | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"text\") {\n fixStreamingText.value += event.text;\n } else if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to fix finding\");\n } else if (event.type === \"done\") {\n result = event.content ?? null;\n }\n }\n }\n\n return result;\n } catch (err) {\n fixError.value =\n err instanceof Error ? err.message : \"Failed to fix finding\";\n return null;\n } finally {\n fixingFindingId.value = null;\n }\n }\n\n function removeFinding(category: ScoringCategory, findingId: string): void {\n if (!scoringResult.value) {\n return;\n }\n\n const cat = scoringResult.value.categories[category];\n if (!cat) {\n return;\n }\n\n cat.findings = cat.findings.filter((f) => f.id !== findingId);\n }\n\n function reset(): void {\n isScoring.value = false;\n scoringResult.value = null;\n error.value = null;\n fixingFindingId.value = null;\n fixStreamingText.value = \"\";\n fixError.value = null;\n }\n\n return {\n isScoring,\n scoringResult,\n error,\n fixingFindingId,\n fixStreamingText,\n fixError,\n score,\n fixFinding,\n removeFinding,\n reset,\n };\n}\n","import type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseDesignReferenceOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n onApply?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseDesignReferenceReturn {\n isGenerating: ReturnType<typeof ref<boolean>>;\n error: ReturnType<typeof ref<string | null>>;\n generate: (input: DesignReferenceInput) => Promise<TemplateContent | null>;\n reset: () => void;\n}\n\nexport interface DesignReferenceInput {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n}\n\nexport function useDesignReference(\n options: UseDesignReferenceOptions,\n): UseDesignReferenceReturn {\n const { authManager, getTemplateId, onApply, onError } = options;\n\n const isGenerating = ref(false);\n const error = ref<string | null>(null);\n\n async function generate(\n input: DesignReferenceInput,\n ): Promise<TemplateContent | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new Error(\"Template must be saved before using design reference\");\n }\n\n isGenerating.value = true;\n error.value = null;\n\n try {\n const formData = new FormData();\n\n if (input.prompt) {\n formData.append(\"prompt\", input.prompt);\n }\n\n if (input.imageUpload) {\n formData.append(\"image_upload\", input.imageUpload);\n }\n\n if (input.pdfUpload) {\n formData.append(\"pdf_upload\", input.pdfUpload);\n }\n\n const url = buildUrl(API_ROUTES[\"ai.generateFromDesign\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n Accept: \"text/event-stream\",\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null);\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n throw new Error(\n errorData?.message || \"Failed to generate template from design\",\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: TemplateContent | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n const jsonStr = line.slice(6);\n\n let event;\n try {\n event = JSON.parse(jsonStr);\n } catch {\n continue;\n }\n\n if (event.type === \"error\") {\n throw new Error(\n event.message || \"Failed to generate template from design\",\n );\n }\n\n if (event.type === \"done\") {\n result = event.content ?? null;\n\n if (result) {\n onApply?.(result);\n }\n }\n }\n }\n\n return result;\n } catch (err) {\n const wrappedError =\n err instanceof Error\n ? err\n : new Error(\"Failed to generate template from design\", {\n cause: err,\n });\n error.value = wrappedError.message;\n onError?.(wrappedError);\n\n return null;\n } finally {\n isGenerating.value = false;\n }\n }\n\n function reset(): void {\n isGenerating.value = false;\n error.value = null;\n }\n\n return {\n isGenerating,\n error,\n generate,\n reset,\n };\n}\n","import type {\n Comment,\n CommentChange,\n CommentInput,\n CommentPatch,\n CommentResponse,\n CommentsProvider,\n UserConfig,\n} from \"@templatical/types\";\nimport { watch, type Ref } from \"vue\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * The two methods this adapter needs from a Pusher channel, named structurally.\n *\n * Deliberately not `PresenceChannel`: `pusher-js` is an **optional** peer of this\n * package, so a type imported from it would make `@templatical/editor` — which\n * holds the channel ref for us — fail to typecheck without it installed. A\n * `PresenceChannel` satisfies this shape, so nothing at the call site changes.\n */\nexport interface RealtimeChannel {\n bind(event: string, handler: (payload: any) => void): unknown;\n unbind(event: string, handler?: (payload: any) => void): unknown;\n}\n\n/** The Pusher event Cloud's backend fans comment writes out on. */\nconst BROADCAST_EVENT = \"comment-broadcast\";\n\ninterface CommentBroadcastPayload {\n action:\n | \"comment_created\"\n | \"comment_updated\"\n | \"comment_deleted\"\n | \"comment_resolved\"\n | \"comment_unresolved\";\n comment: CommentResponse;\n}\n\n/**\n * Cloud's wire shape → the contract shape. The whole job of this adapter, plus\n * auth: snake_case to camelCase, and the two flat `author_*` / `resolved_by_*`\n * pairs into {@link CommentAuthor} objects.\n */\nfunction toComment(record: CommentResponse): Comment {\n const comment: Comment = {\n id: record.id,\n body: record.body,\n author: { id: record.author_identifier, name: record.author_name },\n createdAt: record.created_at,\n blockId: record.block_id,\n parentId: record.parent_id,\n resolvedAt: record.resolved_at,\n };\n\n // Cloud stamps `updated_at` on creation, so forwarding it unconditionally would\n // mark every comment \"(edited)\". The contract says the key is present *only*\n // when the body has changed since.\n if (record.updated_at !== record.created_at) {\n comment.updatedAt = record.updated_at;\n }\n if (record.resolved_by_identifier) {\n comment.resolvedBy = {\n id: record.resolved_by_identifier,\n name: record.resolved_by_name ?? \"\",\n };\n }\n if (record.replies?.length) {\n comment.replies = record.replies.map(toComment);\n }\n return comment;\n}\n\nexport interface CreateCloudCommentsProviderOptions {\n authManager: AuthManager;\n /**\n * Cloud's presence channel for the open template, or `null` before it joins.\n *\n * A ref rather than a value because the channel arrives after the template\n * loads and is replaced when another one is opened — {@link subscribe} watches\n * it and rebinds, which is what keeps realtime optional from the editor's point\n * of view: the contract only ever sees `subscribe`.\n */\n channel: Ref<RealtimeChannel | null>;\n /**\n * The socket id to stamp on writes, so Cloud's backend can skip echoing them\n * back to their author. Returns `null` before the socket connects.\n */\n getSocketId?: () => string | null;\n}\n\n/**\n * Cloud-backed {@link CommentsProvider} — the Templatical Cloud adapter for the\n * same review contract consumers implement themselves.\n *\n * All four mutations are enabled: comment storage and its realtime fan-out are\n * what the `commenting` plan feature pays for, so there is no Cloud tier that can\n * read a thread but not reply to it. A consumer who wants a read-only review\n * supplies their own provider with the mutations set to `false`.\n *\n * Two things about the write payloads are Cloud's alone and stay on this side of\n * the seam:\n *\n * - **The author is signed.** `user_id` / `user_name` / `user_signature` come from\n * the JWT, not from the editor's `user` config, so a browser cannot attribute a\n * comment to someone else. The editor's `user` key still gates the feature and\n * drives \"you wrote this\" in the UI; the two agree because `initCloud()` fills\n * `user` from the same JWT.\n * - **`X-Socket-ID`.** Cloud excludes the originating socket from the broadcast,\n * which is why a local write and its echo can't double-post. Every implementation\n * is nonetheless echo-safe (`upsert` replaces by id), so a backend without that\n * header is fine too.\n */\nexport function createCloudCommentsProvider(\n options: CreateCloudCommentsProviderOptions,\n): CommentsProvider {\n const { authManager, channel, getSocketId } = options;\n const api = new ApiClient(authManager);\n\n function userPayload(): {\n user_id: string;\n user_name: string;\n user_signature: string;\n } {\n const user: UserConfig | null = authManager.userConfig;\n if (!user) {\n // Unreachable through the editor — `initCloud()` derives its `user` config\n // from this same field, so the feature is unavailable without it and no\n // action renders. A headless caller can still get here.\n throw new Error(\n \"[Templatical] Cloud comments need a signed user in the auth token. The project's token endpoint must include a `user` claim.\",\n );\n }\n return {\n user_id: user.id,\n user_name: user.name,\n user_signature: user.signature,\n };\n }\n\n function socketHeaders(): Record<string, string> | undefined {\n const socketId = getSocketId?.();\n return socketId ? { \"X-Socket-ID\": socketId } : undefined;\n }\n\n return {\n async list(templateId: string): Promise<Comment[]> {\n const records = await api.getComments(templateId);\n return records.map(toComment);\n },\n\n async create(templateId: string, input: CommentInput): Promise<Comment> {\n return toComment(\n await api.createComment(\n templateId,\n {\n body: input.body,\n block_id: input.blockId,\n parent_id: input.parentId,\n ...userPayload(),\n },\n socketHeaders(),\n ),\n );\n },\n\n async update(\n templateId: string,\n commentId: string,\n patch: CommentPatch,\n ): Promise<Comment> {\n if (patch.body === undefined) {\n // `body` is the only editable field, so an empty patch has nothing to\n // send. Refuse rather than PUT an undefined body over a real one.\n throw new Error(\n \"[Templatical] Cloud comments: update() needs a `body` in the patch.\",\n );\n }\n return toComment(\n await api.updateComment(\n templateId,\n commentId,\n { body: patch.body, ...userPayload() },\n socketHeaders(),\n ),\n );\n },\n\n async delete(templateId: string, commentId: string): Promise<void> {\n await api.deleteComment(\n templateId,\n commentId,\n userPayload(),\n socketHeaders(),\n );\n },\n\n async setResolved(\n templateId: string,\n commentId: string,\n _resolved: boolean,\n ): Promise<Comment> {\n // Cloud's endpoint toggles server-side, so the requested state is not sent.\n // The response is authoritative either way, and `useComments` fires its\n // event off `resolvedAt` in that response rather than off what was asked —\n // so a disagreement surfaces as the truth rather than as a wrong event.\n return toComment(\n await api.resolveComment(\n templateId,\n commentId,\n userPayload(),\n socketHeaders(),\n ),\n );\n },\n\n subscribe(\n _templateId: string,\n onChange: (change: CommentChange) => void,\n ): () => void {\n function handle(payload: CommentBroadcastPayload): void {\n const comment = toComment(payload.comment);\n switch (payload.action) {\n case \"comment_created\":\n onChange({ type: \"created\", comment });\n break;\n case \"comment_deleted\":\n onChange({\n type: \"deleted\",\n commentId: comment.id,\n parentId: comment.parentId,\n });\n break;\n // A resolve and an unresolve are both a changed comment; `resolvedAt`\n // in the payload is what says which, and `useComments` reads it.\n case \"comment_updated\":\n case \"comment_resolved\":\n case \"comment_unresolved\":\n onChange({ type: \"updated\", comment });\n break;\n }\n }\n\n // The channel is joined when the template loads, i.e. after this provider is\n // built, and is replaced when another template is opened — so the binding\n // follows the ref rather than being made once. `_templateId` is unused\n // because Cloud's channel *is* per-template: a channel change already means\n // a template change.\n const stopWatching = watch(\n channel,\n (next, previous) => {\n previous?.unbind(BROADCAST_EVENT, handle);\n next?.bind(BROADCAST_EVENT, handle);\n },\n { immediate: true },\n );\n\n return () => {\n stopWatching();\n // The watcher only unbinds on a *transition*; without this the handler\n // stays attached to the current channel and keeps mutating a torn-down\n // list.\n channel.value?.unbind(BROADCAST_EVENT, handle);\n };\n },\n };\n}\n","import type { UseEditorReturn } from \"../editor\";\nimport type { AuthManager } from \"./auth\";\nimport type { Collaborator, McpOperationPayload } from \"@templatical/types\";\nimport { handleOperation } from \"./mcp-operation-handler\";\nimport type { PresenceMember } from \"./websocket-client\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { computed, onScopeDispose, ref, watch, type Ref } from \"vue\";\n\nconst COLLAB_EVENTS = [\n \"pusher:member_added\",\n \"pusher:member_removed\",\n \"client-block_locked\",\n \"client-block_unlocked\",\n \"client-operation\",\n \"mcp-operation\",\n] as const;\n\nfunction unbindCollabEvents(channel: PresenceChannel): void {\n for (const event of COLLAB_EVENTS) {\n channel.unbind(event);\n }\n}\n\nconst COLLABORATOR_COLORS = [\n \"#3b82f6\",\n \"#ef4444\",\n \"#10b981\",\n \"#f59e0b\",\n \"#8b5cf6\",\n \"#ec4899\",\n \"#06b6d4\",\n \"#f97316\",\n \"#6366f1\",\n \"#14b8a6\",\n];\n\nexport interface UseCollaborationOptions {\n authManager: AuthManager;\n editor: UseEditorReturn;\n channel: Ref<PresenceChannel | null>;\n onError?: (error: Error) => void;\n onCollaboratorJoined?: (collaborator: Collaborator) => void;\n onCollaboratorLeft?: (collaborator: Collaborator) => void;\n onBlockLocked?: (event: {\n blockId: string;\n collaborator: Collaborator;\n }) => void;\n onBlockUnlocked?: (event: {\n blockId: string;\n collaborator: Collaborator;\n }) => void;\n}\n\nexport interface UseCollaborationReturn {\n collaborators: Ref<Collaborator[]>;\n lockedBlocks: Ref<Map<string, Collaborator>>;\n}\n\nexport function useCollaboration(\n options: UseCollaborationOptions,\n): UseCollaborationReturn & {\n _broadcastOperation: (payload: McpOperationPayload) => void;\n _isProcessingRemoteOperation: () => boolean;\n} {\n const { authManager, editor, channel } = options;\n\n const collaborators = ref<Collaborator[]>([]);\n const lockedBlocks = ref<Map<string, Collaborator>>(new Map());\n\n let colorIndex = 0;\n let isProcessingRemoteOperation = false;\n\n const myUserId = computed(() => authManager.userConfig?.id ?? \"\");\n\n function assignColor(): string {\n const color = COLLABORATOR_COLORS[colorIndex % COLLABORATOR_COLORS.length];\n colorIndex++;\n return color;\n }\n\n function addCollaborator(member: PresenceMember): Collaborator | undefined {\n if (member.id === myUserId.value) {\n return undefined;\n }\n\n if (collaborators.value.some((c) => c.id === member.id)) {\n return undefined;\n }\n\n const collaborator: Collaborator = {\n id: member.id,\n name: member.name,\n color: assignColor(),\n selectedBlockId: null,\n };\n\n collaborators.value = [...collaborators.value, collaborator];\n\n return collaborator;\n }\n\n function removeCollaborator(memberId: string): void {\n const newLockedBlocks = new Map(lockedBlocks.value);\n for (const [blockId, collaborator] of newLockedBlocks) {\n if (collaborator.id === memberId) {\n newLockedBlocks.delete(blockId);\n }\n }\n lockedBlocks.value = newLockedBlocks;\n\n collaborators.value = collaborators.value.filter((c) => c.id !== memberId);\n }\n\n function handleBlockLocked(data: { blockId: string; userId: string }): void {\n const collaborator = collaborators.value.find((c) => c.id === data.userId);\n if (!collaborator) {\n return;\n }\n\n collaborators.value = collaborators.value.map((c) =>\n c.id === data.userId ? { ...c, selectedBlockId: data.blockId } : c,\n );\n\n const newLockedBlocks = new Map(lockedBlocks.value);\n for (const [blockId, holder] of newLockedBlocks) {\n if (holder.id === data.userId) {\n newLockedBlocks.delete(blockId);\n }\n }\n\n newLockedBlocks.set(data.blockId, {\n ...collaborator,\n selectedBlockId: data.blockId,\n });\n lockedBlocks.value = newLockedBlocks;\n\n if (editor.state.selectedBlockId === data.blockId) {\n editor.selectBlock(null);\n }\n }\n\n function handleBlockUnlocked(data: { blockId: string }): void {\n const newLockedBlocks = new Map(lockedBlocks.value);\n const holder = newLockedBlocks.get(data.blockId);\n newLockedBlocks.delete(data.blockId);\n lockedBlocks.value = newLockedBlocks;\n\n if (holder) {\n collaborators.value = collaborators.value.map((c) =>\n c.id === holder.id ? { ...c, selectedBlockId: null } : c,\n );\n }\n }\n\n function handleRemoteOperation(payload: McpOperationPayload): void {\n isProcessingRemoteOperation = true;\n try {\n handleOperation(editor, payload);\n } finally {\n isProcessingRemoteOperation = false;\n }\n }\n\n function broadcastOperation(payload: McpOperationPayload): void {\n if (!channel.value || isProcessingRemoteOperation) {\n return;\n }\n\n channel.value.trigger(\"client-operation\", payload);\n }\n\n function broadcastBlockLocked(blockId: string): void {\n if (!channel.value) {\n return;\n }\n\n channel.value.trigger(\"client-block_locked\", {\n blockId,\n userId: myUserId.value,\n });\n }\n\n function broadcastBlockUnlocked(blockId: string): void {\n if (!channel.value) {\n return;\n }\n\n channel.value.trigger(\"client-block_unlocked\", { blockId });\n }\n\n watch(\n () => editor.state.selectedBlockId,\n (newBlockId, oldBlockId) => {\n if (isProcessingRemoteOperation) {\n return;\n }\n\n if (oldBlockId) {\n broadcastBlockUnlocked(oldBlockId);\n }\n\n if (newBlockId) {\n broadcastBlockLocked(newBlockId);\n }\n },\n );\n\n watch(channel, (newChannel, oldChannel) => {\n if (oldChannel) {\n unbindCollabEvents(oldChannel);\n }\n\n if (!newChannel) {\n collaborators.value = [];\n lockedBlocks.value = new Map();\n colorIndex = 0;\n return;\n }\n\n const members = (\n newChannel as PresenceChannel & {\n members: {\n each: (\n callback: (member: { id: string; info: PresenceMember }) => void,\n ) => void;\n };\n }\n ).members;\n if (members) {\n members.each((member: { id: string; info: PresenceMember }) => {\n addCollaborator(member.info);\n });\n }\n\n newChannel.bind(\n \"pusher:member_added\",\n (member: { id: string; info: PresenceMember }) => {\n const collaborator = addCollaborator(member.info);\n if (collaborator) {\n options.onCollaboratorJoined?.(collaborator);\n }\n },\n );\n\n newChannel.bind(\n \"pusher:member_removed\",\n (member: { id: string; info: PresenceMember }) => {\n const collaborator = collaborators.value.find(\n (c) => c.id === member.id,\n );\n removeCollaborator(member.id);\n if (collaborator) {\n options.onCollaboratorLeft?.(collaborator);\n }\n },\n );\n\n newChannel.bind(\n \"client-block_locked\",\n (data: { blockId: string; userId: string }) => {\n handleBlockLocked(data);\n const collaborator = collaborators.value.find(\n (c) => c.id === data.userId,\n );\n if (collaborator) {\n options.onBlockLocked?.({\n blockId: data.blockId,\n collaborator,\n });\n }\n },\n );\n\n newChannel.bind(\"client-block_unlocked\", (data: { blockId: string }) => {\n const holder = lockedBlocks.value.get(data.blockId);\n handleBlockUnlocked(data);\n if (holder) {\n options.onBlockUnlocked?.({\n blockId: data.blockId,\n collaborator: holder,\n });\n }\n });\n\n newChannel.bind(\"client-operation\", (payload: McpOperationPayload) => {\n handleRemoteOperation(payload);\n });\n\n newChannel.bind(\"mcp-operation\", (payload: McpOperationPayload) => {\n handleRemoteOperation(payload);\n });\n });\n\n // The watch above only unbinds when the channel ref *changes*. On scope\n // dispose (component unmount) the watch is auto-stopped but the currently\n // bound listeners stay attached forever — handlers fire on a dead component\n // and accumulate on remount. Tear them down explicitly.\n onScopeDispose(() => {\n if (channel.value) {\n unbindCollabEvents(channel.value);\n }\n });\n\n return {\n collaborators,\n lockedBlocks,\n _broadcastOperation: broadcastOperation,\n _isProcessingRemoteOperation: () => isProcessingRemoteOperation,\n };\n}\n","import type { McpOperationPayload } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"../editor\";\n\ninterface BroadcastTarget {\n _broadcastOperation: (payload: McpOperationPayload) => void;\n}\n\n/**\n * Wraps editor mutation methods to broadcast operations to collaboration\n * peers after each mutation executes. Mutates the editor object in place.\n *\n * Must be applied **before** the history interceptor so the call chain is:\n * history.record() → broadcast + original mutation.\n */\nexport function useCollaborationBroadcast(\n // The six members it actually replaces, rather than a whole editor. It is\n // handed the *cloud* editor while this annotation names the OSS one, so any\n // member either gains that the other lacks would break this call site for\n // reasons that have nothing to do with broadcasting.\n editor: Pick<\n UseEditorReturn,\n | \"addBlock\"\n | \"updateBlock\"\n | \"removeBlock\"\n | \"moveBlock\"\n | \"updateSettings\"\n | \"setContent\"\n >,\n collaboration: BroadcastTarget,\n): void {\n const originalAddBlock = editor.addBlock;\n const originalUpdateBlock = editor.updateBlock;\n const originalRemoveBlock = editor.removeBlock;\n const originalMoveBlock = editor.moveBlock;\n const originalUpdateSettings = editor.updateSettings;\n const originalSetContent = editor.setContent;\n\n editor.addBlock = (block, targetSectionId?, columnIndex?, index?) => {\n originalAddBlock(block, targetSectionId, columnIndex, index);\n collaboration._broadcastOperation({\n operation: \"add_block\",\n data: {\n block,\n section_id: targetSectionId,\n column_index: columnIndex,\n index,\n },\n timestamp: Date.now(),\n });\n };\n\n editor.updateBlock = (blockId, updates) => {\n originalUpdateBlock(blockId, updates);\n collaboration._broadcastOperation({\n operation: \"update_block\",\n data: { block_id: blockId, updates },\n timestamp: Date.now(),\n });\n };\n\n editor.removeBlock = (blockId) => {\n originalRemoveBlock(blockId);\n collaboration._broadcastOperation({\n operation: \"delete_block\",\n data: { block_id: blockId },\n timestamp: Date.now(),\n });\n };\n\n editor.moveBlock = (blockId, newIndex, targetSectionId?, columnIndex?) => {\n originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);\n collaboration._broadcastOperation({\n operation: \"move_block\",\n data: {\n block_id: blockId,\n index: newIndex,\n section_id: targetSectionId,\n column_index: columnIndex,\n },\n timestamp: Date.now(),\n });\n };\n\n editor.updateSettings = (updates) => {\n originalUpdateSettings(updates);\n collaboration._broadcastOperation({\n operation: \"update_settings\",\n data: { updates },\n timestamp: Date.now(),\n });\n };\n\n editor.setContent = (content, markDirty?) => {\n originalSetContent(content, markDirty);\n collaboration._broadcastOperation({\n operation: \"set_content\",\n data: { content },\n timestamp: Date.now(),\n });\n };\n}\n","import type { AuthManager } from \"./auth\";\nimport type { WebSocketConfig } from \"./websocket-client\";\nimport { WebSocketClient } from \"./websocket-client\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { ref, type Ref } from \"vue\";\n\nexport interface UseWebSocketOptions {\n authManager: AuthManager;\n onError?: (error: Error) => void;\n}\n\nexport interface UseWebSocketReturn {\n channel: Ref<PresenceChannel | null>;\n isConnected: Ref<boolean>;\n connect: (templateId: string, config: WebSocketConfig) => Promise<void>;\n disconnect: () => void;\n getSocketId: () => string | null;\n}\n\nexport function useWebSocket(options: UseWebSocketOptions): UseWebSocketReturn {\n const { authManager, onError } = options;\n\n const channel = ref<PresenceChannel | null>(\n null,\n ) as Ref<PresenceChannel | null>;\n const isConnected = ref(false);\n\n let wsClient: WebSocketClient | null = null;\n let channelName: string | null = null;\n\n async function connect(\n templateId: string,\n config: WebSocketConfig,\n ): Promise<void> {\n if (wsClient) {\n return;\n }\n\n wsClient = new WebSocketClient({\n authManager,\n config,\n onError,\n });\n\n await wsClient.connect();\n\n channelName = `presence-template.${templateId}`;\n const presenceChannel = wsClient.subscribePresence(channelName);\n\n presenceChannel.bind(\"pusher:subscription_succeeded\", () => {\n isConnected.value = true;\n channel.value = presenceChannel;\n });\n\n presenceChannel.bind(\"pusher:subscription_error\", (error: unknown) => {\n isConnected.value = false;\n channel.value = null;\n onError?.(\n error instanceof Error\n ? error\n : new Error(\"Failed to subscribe to template channel\"),\n );\n });\n }\n\n function disconnect(): void {\n if (channelName && wsClient) {\n wsClient.unsubscribe(channelName);\n }\n\n wsClient?.disconnect();\n wsClient = null;\n channelName = null;\n channel.value = null;\n isConnected.value = false;\n }\n\n function getSocketId(): string | null {\n return wsClient?.getSocketId() ?? null;\n }\n\n return {\n channel,\n isConnected,\n connect,\n disconnect,\n getSocketId,\n };\n}\n","import type {\n SavedBlock,\n SavedBlockInput,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * Cloud-backed {@link SavedBlocksProvider} — the Templatical Cloud adapter for\n * the same contract consumers implement themselves.\n *\n * Auth (JWT via {@link AuthManager}) and project/tenant scoping live entirely\n * on this side of the seam: the shared `useSavedBlocks` composable and the\n * editor UI never see them. Plan gating is applied by the caller before this\n * provider is constructed.\n *\n * Wraps the `ApiClient` saved-module endpoints. The REST contract keeps the\n * `savedModules.*` route names even though the type is `SavedBlock`, so the\n * names deliberately differ from the contract's.\n *\n * Rows pass straight through, unmapped: the Cloud endpoints speak the same\n * `SavedBlock` shape the contract defines, so this adapter only supplies auth\n * and scoping. That means the payload's field names track the contract — the\n * backend serialises `category`, `canUpdate`, `canDelete`, `createdAt` and\n * `updatedAt`. Until it does, those fields simply arrive absent, which degrades\n * to \"no timestamp label, every action allowed\" rather than breaking.\n */\nexport function createCloudSavedBlocksProvider(\n authManager: AuthManager,\n): SavedBlocksProvider {\n const api = new ApiClient(authManager);\n\n return {\n list(params?: SavedBlocksListParams): Promise<SavedBlock[]> {\n return api.listModules(params?.search, params?.category);\n },\n // All three enabled: Cloud gates saved blocks by plan entitlement above\n // this seam, and row-level permissions arrive as `canUpdate`/`canDelete`\n // on the entries themselves.\n create(input: SavedBlockInput): Promise<SavedBlock> {\n return api.createModule(input);\n },\n update(id: string, patch: SavedBlockPatch): Promise<SavedBlock> {\n return api.updateModule(id, patch);\n },\n delete(id: string): Promise<void> {\n return api.deleteModule(id);\n },\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplatePatch,\n TemplatesProvider,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * How often a save may also record an automatic version. Cloud's storage, Cloud's\n * number — an editor-side default would be guessing on someone else's behalf.\n */\nconst AUTO_VERSION_INTERVAL_MS = 60_000;\n\n/**\n * Cloud-backed {@link TemplatesProvider} — the Templatical Cloud adapter for the\n * same save/load contract consumers implement themselves.\n *\n * Auth (JWT via {@link AuthManager}) and project/tenant scoping live entirely on\n * this side of the seam; the editor's header, Cmd+S, autosave and\n * unsaved-changes guard never see them.\n *\n * All three methods are enabled: Cloud's template storage is what the plan pays\n * for, so there is no tier in which a Cloud session can load but not save. A\n * consumer who wants read-only supplies their own provider with `save: false`.\n *\n * **This is also where automatic versions are recorded.** The editor never\n * creates one on its own — whoever implements `save` decides whether a save also\n * records a version, which keeps throttling and retention with the side that\n * pays for the storage. Cloud throttles to one automatic version per\n * {@link AUTO_VERSION_INTERVAL_MS}, so an autosave firing every few seconds does\n * not turn history into a keystroke log. Keep this on the provider side: an\n * editor-side timer would put Cloud's retention policy in the editor.\n *\n * Note `initCloud()` **rejects** a consumer-supplied templates provider, unlike\n * `savedBlocks` and `testEmail`. Those are inert — nothing keys off them. The\n * template id is the join key for collaboration, version history, comments, AI\n * rewrite, scoring and the server-side export, so an id Cloud never issued would\n * degrade all six silently.\n */\nexport function createCloudTemplatesProvider(\n authManager: AuthManager,\n): TemplatesProvider {\n const api = new ApiClient(authManager);\n\n // `0` rather than `Date.now()`: the first save of a session always records a\n // version, so a short session that saves once still has history.\n let lastAutoVersionAt = 0;\n\n /**\n * Fire-and-forget: a version is a convenience on top of a save that already\n * succeeded, so a failing history endpoint must not turn a successful save\n * into a failed one. The clock advances before the request, so a slow endpoint\n * can't let a burst of saves queue several at once.\n *\n * Logged rather than swallowed: history quietly not filling up is exactly the\n * kind of failure nobody notices until they need a version that was never\n * recorded.\n */\n function maybeRecordVersion(\n templateId: string,\n content: TemplateContent,\n ): void {\n const now = Date.now();\n if (now - lastAutoVersionAt < AUTO_VERSION_INTERVAL_MS) return;\n lastAutoVersionAt = now;\n void api.createVersion(templateId, content).catch((error: unknown) => {\n console.warn(\n \"[Templatical] Automatic version not recorded — the save itself succeeded:\",\n error,\n );\n });\n }\n\n return {\n load(id: string): Promise<Template> {\n return api.getTemplate(id);\n },\n create(input: {\n name?: string;\n content: TemplateContent;\n }): Promise<Template> {\n return api.createTemplate(input.content, input.name);\n },\n async save(id: string, patch: TemplatePatch): Promise<Template> {\n // Forwarded verbatim: the endpoint speaks the same patch shape, so this\n // adapter only supplies auth and scoping. `name` is inert until the backend\n // stores the column, then live with no SDK change.\n const template = await api.updateTemplate(id, patch);\n // Only content changes are worth a version — a rename patch carries no\n // content, and versioning one would record a duplicate of the last state.\n if (patch.content) maybeRecordVersion(id, patch.content);\n return template;\n },\n };\n}\n","import type { CustomFont, ExportResult, FontsConfig } from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/** The fonts half of an export request, as Cloud's endpoint expects it. */\nexport interface ExportFontsPayload {\n customFonts: CustomFont[];\n defaultFallback: string;\n}\n\nexport interface UseExportOptions {\n authManager: AuthManager;\n}\n\nexport interface UseExportReturn {\n exportHtml: (\n templateId: string,\n fonts: ExportFontsPayload,\n ) => Promise<ExportResult>;\n getMjmlSource: (\n templateId: string,\n fonts: ExportFontsPayload,\n ) => Promise<string>;\n}\n\n/**\n * Flatten a {@link FontsConfig} into the export payload.\n *\n * Unconditional: gating fonts by entitlement meters no resource Cloud buys, and\n * would only make the paid tier render fewer fonts than the free editor.\n */\nexport function resolveExportFonts(\n fonts: FontsConfig | undefined,\n): ExportFontsPayload {\n return {\n customFonts: fonts?.customFonts ?? [],\n defaultFallback: fonts?.defaultFallback ?? \"Arial, sans-serif\",\n };\n}\n\n/**\n * Cloud's server-side export endpoint, as a plain API wrapper.\n *\n * Both calls render from the **stored** template, so callers save first when the\n * canvas may have moved on — see `createCloudRenderProvider`, which is where that\n * policy lives.\n */\nexport function useExport(options: UseExportOptions): UseExportReturn {\n const api = new ApiClient(options.authManager);\n\n async function exportHtml(\n templateId: string,\n fonts: ExportFontsPayload,\n ): Promise<ExportResult> {\n const result = await api.exportTemplate(templateId, fonts);\n\n return {\n html: result.html,\n mjml: result.mjml,\n };\n }\n\n async function getMjmlSource(\n templateId: string,\n fonts: ExportFontsPayload,\n ): Promise<string> {\n const result = await api.exportTemplate(templateId, fonts);\n return result.mjml;\n }\n\n return {\n exportHtml,\n getMjmlSource,\n };\n}\n","import type {\n RenderPayload,\n RenderProvider,\n Template,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { useExport, type ExportFontsPayload } from \"./export\";\n\nexport interface CreateCloudRenderProviderOptions {\n authManager: AuthManager;\n /** The loaded template's id, or `null` before one exists. */\n getTemplateId: () => string | null;\n /** Persist the canvas so the endpoint renders what the user is looking at. */\n save: () => Promise<Template>;\n}\n\n/**\n * Templatical Cloud's renderer, shaped as a {@link RenderProvider} so it plugs\n * into the same editor seam a consumer's own backend would.\n *\n * **Why Cloud renders server-side at all**, rather than running the bundled\n * renderer like an OSS consumer: its output is a deliberate *superset* that a\n * browser cannot produce. A countdown block resolves to a URL serving a live,\n * on-demand animated GIF; a video block gets a composited play button. Both are\n * injected into the published renderer through `blockRenderers` in Cloud's Node\n * sidecar, so the delta is two functions and parity on the other twelve block\n * types holds by construction.\n *\n * Two consequences of the endpoint rendering the **stored** template:\n *\n * - **Every render saves first.** The same trade the test-email adapter makes, for\n * the same reason: exporting a stale version of what is on screen is worse than\n * a write the caller did not ask for. It also means `toMjml()` needs a template\n * to exist — a Cloud session that never created one gets a clear rejection\n * rather than an export of nothing.\n * - **`payload.content` is ignored**, along with the custom-block `renderedHtml`\n * the editor pre-rendered into it. Cloud's `save()` persists that same content\n * immediately before, so the server reads it from storage rather than trusting an\n * echo. `payload.fonts` *is* read, because it is the only place the editor's\n * effective font set is expressed. Don't \"fix\" the content branch.\n *\n * `compileMjml` is deliberately absent: `toMjml` and `toHtml` are both whole-\n * pipeline calls here, so there is no MJML the editor would hand back for\n * compiling.\n */\nexport function createCloudRenderProvider(\n options: CreateCloudRenderProviderOptions,\n): RenderProvider {\n const exporter = useExport({ authManager: options.authManager });\n\n // Every plan renders the fonts the canvas is using: an entitlement on editor\n // capability that OSS gives away free would make the paid tier render *less*\n // than the free one.\n function exportFonts(payload: RenderPayload): ExportFontsPayload {\n return {\n customFonts: payload.fonts?.customFonts ?? [],\n defaultFallback: payload.fonts?.defaultFallback ?? \"Arial, sans-serif\",\n };\n }\n\n async function saveThenResolveId(): Promise<string> {\n if (options.getTemplateId() === null) {\n throw new Error(\n \"[Templatical] Cloud renders from the saved template, so one must exist first. Call create() or load() before toMjml() / toHtml().\",\n );\n }\n\n // Save before exporting: the endpoint reads storage, so skipping this would\n // render whatever was persisted last rather than what is on the canvas.\n const template = await options.save();\n return template.id;\n }\n\n return {\n async toMjml(payload: RenderPayload): Promise<string> {\n const fonts = exportFonts(payload);\n const templateId = await saveThenResolveId();\n return exporter.getMjmlSource(templateId, fonts);\n },\n\n async toHtml(payload: RenderPayload): Promise<string> {\n const fonts = exportFonts(payload);\n const templateId = await saveThenResolveId();\n const { html } = await exporter.exportHtml(templateId, fonts);\n return html;\n },\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplateVersion,\n TemplateVersionResponse,\n VersionHistoryProvider,\n VersionHistoryListResult,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * Cloud's wire shape → the contract shape. The only real work this adapter does:\n * snake_case to camelCase, and `is_autosave` to `isAutomatic`.\n *\n * `content` is carried through deliberately — Cloud's index endpoint already\n * returns it, so every listed version arrives hydrated and the editor never\n * calls `get` while scrubbing. See {@link TemplateVersion.content}: that is the\n * hint working as intended, not a shortcut. `get` is implemented anyway, because\n * it is the contract's operation and Cloud is one implementation of it like any\n * other.\n */\nfunction toVersion(record: TemplateVersionResponse): TemplateVersion {\n return {\n id: record.id,\n createdAt: record.created_at,\n isAutomatic: record.is_autosave,\n content: record.content,\n };\n}\n\n/**\n * Cloud-backed {@link VersionHistoryProvider} — the Templatical Cloud adapter for\n * the same contract consumers implement themselves.\n *\n * Both mutations are enabled: version storage is what the plan pays for, so\n * there is no Cloud tier that can list history but not restore it. A consumer\n * who wants read-only history supplies their own provider with `restore: false`.\n *\n * **Automatic versions are not created here.** They are recorded by\n * `createCloudTemplatesProvider`'s `save`, because the contract puts that\n * decision on whoever implements `save` — the side that knows the storage cost.\n * `create` here is for a version a person asked for.\n */\nexport function createCloudVersionHistoryProvider(\n authManager: AuthManager,\n): VersionHistoryProvider {\n const api = new ApiClient(authManager);\n\n return {\n async list(templateId: string): Promise<VersionHistoryListResult> {\n const records = await api.getVersions(templateId);\n // No `nextCursor`: Cloud returns the template's whole history in one\n // response — with each entry's content — so there is never a further page.\n return { versions: records.map(toVersion) };\n },\n async get(templateId: string, versionId: string): Promise<TemplateContent> {\n const record = await api.getVersion(templateId, versionId);\n return record.content;\n },\n async create(\n templateId: string,\n content: TemplateContent,\n meta?: { label?: string },\n ): Promise<TemplateVersion> {\n return toVersion(\n await api.createVersion(templateId, content, meta?.label),\n );\n },\n restore(templateId: string, versionId: string): Promise<Template> {\n // Cloud keeps an atomic, audited server endpoint. It is append-only like\n // every other implementation of this method: restoring records a new entry\n // rather than rewriting the one being restored.\n return api.restoreVersion(templateId, versionId);\n },\n };\n}\n","import type { TestEmailConfig } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport type { ComputedRef, Ref } from \"vue\";\nimport { computed, ref, watch } from \"vue\";\n\n/**\n * Cloud's test-email *configuration* — whether the project has the feature, who\n * may be sent to, and the signature that lets the backend verify that list.\n *\n * Sending itself lives in `createCloudTestEmailProvider`, which plugs into the\n * shared `useTestEmailFeature` seam in `@templatical/editor` alongside a\n * consumer's own sender. Keeping the send body in exactly one place is the point:\n * both editors then drive identical UI, state and error handling, so the sending\n * spinner, the inline error and the success confirmation can't drift apart.\n */\nexport interface UseTestEmailOptions {\n authManager: AuthManager;\n /** Gates reading the config off the auth manager until the JWT has resolved. */\n isAuthReady?: Ref<boolean>;\n}\n\nexport interface UseTestEmailReturn {\n /** Whether the project's token carries a test-email config at all. */\n isEnabled: ComputedRef<boolean>;\n /**\n * Addresses the project permits. Empty until auth resolves — read it\n * reactively, never snapshot it, or it stays empty for the session.\n */\n allowedEmails: ComputedRef<string[]>;\n /** The signature accompanying {@link allowedEmails}, or `null` when disabled. */\n getSignature: () => string | null;\n}\n\nexport function useTestEmail(options: UseTestEmailOptions): UseTestEmailReturn {\n const { authManager, isAuthReady } = options;\n\n const testEmailConfig = ref<TestEmailConfig | null>(null);\n\n if (isAuthReady) {\n watch(\n isAuthReady,\n (ready) => {\n if (ready) {\n testEmailConfig.value = authManager.testEmailConfig;\n }\n },\n { immediate: true },\n );\n }\n\n const isEnabled = computed<boolean>(() => testEmailConfig.value !== null);\n\n const allowedEmails = computed<string[]>(\n () => testEmailConfig.value?.allowedEmails ?? [],\n );\n\n function getSignature(): string | null {\n return testEmailConfig.value?.signature ?? null;\n }\n\n return { isEnabled, allowedEmails, getSignature };\n}\n","import type {\n ExportResult,\n Template,\n TestEmailProvider,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { ApiClient } from \"./api\";\nimport type { ComputedRef } from \"vue\";\n\nexport interface CreateCloudTestEmailProviderOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n save: () => Promise<Template>;\n exportHtml: (templateId: string) => Promise<ExportResult>;\n /**\n * The project's allowed recipients, from the signed JWT.\n *\n * Taken as a `ComputedRef` rather than an array because it only becomes\n * populated once auth resolves — see the getter note on the returned provider.\n */\n allowedEmails: ComputedRef<string[]>;\n /** The signature that lets the backend verify {@link allowedEmails}. */\n getSignature: () => string | null;\n onBeforeTestEmail?: (html: string) => string | Promise<string>;\n}\n\n/**\n * Templatical Cloud's test-email sender, shaped as a {@link TestEmailProvider}\n * so it plugs into the same editor seam a consumer's own sender would.\n *\n * Two things about it are deliberately unlike a BYO provider:\n *\n * - **It ignores `payload.content` and `payload.allowedRecipients`.** Cloud saves\n * the template, has its backend render the HTML, and derives the allowlist plus\n * its signature from the auth manager. Trusting the echoed payload would swap a\n * server-signed list for an unsigned browser value. The contract is \"send a test\n * of the current template\" — how an adapter obtains the bytes is its business,\n * so this is honest rather than a smell. Don't \"fix\" it.\n * - **It never sets `includeMjml`.** Cloud renders server-side, so the editor\n * should not spend a render pass producing MJML nobody reads.\n */\nexport function createCloudTestEmailProvider(\n options: CreateCloudTestEmailProviderOptions,\n): TestEmailProvider {\n const api = new ApiClient(options.authManager);\n\n return {\n /**\n * A getter, not a snapshot: the allowlist arrives with the JWT, so it starts\n * empty and fills once auth is ready. The editor reads this property inside a\n * computed, which is what makes the trigger appear at that point — reading it\n * once at construction would pin it to `[]` and, because an empty allowlist\n * means \"nobody may be sent to\", hide the button forever.\n */\n get allowedRecipients(): string[] {\n return options.allowedEmails.value;\n },\n\n async send({ recipient }): Promise<void> {\n const signature = options.getSignature();\n if (signature === null) {\n throw new Error(\"Test email is not enabled for this project\");\n }\n\n const templateId = options.getTemplateId();\n if (templateId === null) {\n throw new Error(\"Template must be saved before sending a test email\");\n }\n\n // Save first: the backend renders from the stored template, so sending\n // without this would mail a stale version of what's on screen.\n await options.save();\n\n let { html } = await options.exportHtml(templateId);\n if (options.onBeforeTestEmail) {\n html = await options.onBeforeTestEmail(html);\n }\n\n await api.sendTestEmail(templateId, {\n recipient,\n html,\n allowed_emails: options.allowedEmails.value,\n signature,\n });\n },\n };\n}\n","import type { PlanConfig, PlanFeatures } from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\n\nexport interface UsePlanConfigOptions {\n authManager: AuthManager;\n onError?: (error: Error) => void;\n}\n\nexport interface UsePlanConfigReturn {\n config: Ref<PlanConfig | null>;\n isLoading: Ref<boolean>;\n hasFeature: (feature: keyof PlanFeatures) => boolean;\n features: ComputedRef<PlanFeatures | null>;\n fetchConfig: () => Promise<void>;\n}\n\nexport function usePlanConfig(\n options: UsePlanConfigOptions,\n): UsePlanConfigReturn {\n const { authManager, onError } = options;\n\n const config = ref<PlanConfig | null>(null);\n const isLoading = ref(false);\n\n const apiClient = new ApiClient(authManager);\n\n const features = computed(() => config.value?.features ?? null);\n\n function hasFeature(feature: keyof PlanFeatures): boolean {\n return config.value?.features[feature] ?? false;\n }\n\n async function fetchConfig(): Promise<void> {\n if (isLoading.value) {\n return;\n }\n\n isLoading.value = true;\n\n try {\n config.value = await apiClient.fetchConfig();\n } catch (error) {\n onError?.(\n error instanceof Error\n ? error\n : new Error(\"Failed to fetch config\", { cause: error }),\n );\n } finally {\n isLoading.value = false;\n }\n }\n\n return {\n config,\n isLoading,\n hasFeature,\n features,\n fetchConfig,\n };\n}\n","import type { HealthCheckResult } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES } from \"./url-builder\";\n\ninterface HealthCheckOptions {\n authManager?: AuthManager;\n baseUrl?: string;\n}\n\nconst WS_HANDSHAKE_TIMEOUT = 5000;\n\nfunction resolveHealthUrl(options: HealthCheckOptions): string {\n if (options.authManager) {\n return options.authManager.resolveUrl(API_ROUTES.health);\n }\n\n const base = (options.baseUrl ?? \"https://templatical.com\").replace(\n /\\/$/,\n \"\",\n );\n return `${base}${API_ROUTES.health}`;\n}\n\nasync function checkApiAndAuth(\n url: string,\n authManager?: AuthManager,\n): Promise<{\n api: { ok: boolean; latency: number };\n auth: { ok: boolean; error?: string };\n wsConfig?: { host: string; port: number; app_key: string };\n}> {\n const start = performance.now();\n\n try {\n const response = authManager\n ? await authManager.authenticatedFetch(API_ROUTES.health, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n })\n : await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n const latency = Math.round(performance.now() - start);\n\n if (response.status === 401) {\n return {\n api: { ok: true, latency },\n auth: { ok: false, error: \"HTTP 401\" },\n };\n }\n\n if (!response.ok) {\n return {\n api: { ok: false, latency },\n auth: {\n ok: !authManager,\n error: authManager ? `HTTP ${response.status}` : undefined,\n },\n };\n }\n\n const data = await response.json();\n\n return {\n api: { ok: data.status === \"ok\", latency },\n auth: { ok: true },\n wsConfig: data.websocket,\n };\n } catch (error) {\n const latency = Math.round(performance.now() - start);\n\n return {\n api: { ok: false, latency },\n auth: {\n ok: !authManager,\n error: authManager\n ? error instanceof Error\n ? error.message\n : \"Authentication check failed\"\n : undefined,\n },\n };\n }\n}\n\nasync function checkWebSocket(wsConfig?: {\n host: string;\n port: number;\n app_key: string;\n}): Promise<{ ok: boolean; error?: string }> {\n if (!wsConfig?.host || !wsConfig?.app_key) {\n return { ok: false, error: \"WebSocket configuration not available\" };\n }\n\n if (typeof WebSocket === \"undefined\") {\n return {\n ok: false,\n error: \"WebSocket not supported in this environment\",\n };\n }\n\n const protocol = wsConfig.port === 443 ? \"wss\" : \"ws\";\n const url = `${protocol}://${wsConfig.host}:${wsConfig.port}/app/${wsConfig.app_key}?protocol=7&client=js&version=8.4.0-rc2&flash=false`;\n\n return new Promise((resolve) => {\n let ws: WebSocket | null = null;\n\n const timeout = setTimeout(() => {\n ws?.close();\n resolve({ ok: false, error: \"WebSocket connection timed out\" });\n }, WS_HANDSHAKE_TIMEOUT);\n\n try {\n ws = new WebSocket(url);\n } catch (error) {\n // Some browsers throw synchronously from `new WebSocket(...)`\n // (e.g. SecurityError on mixed content, InvalidAccessError on\n // malformed URL). Without this catch the queued timeout would\n // later access `ws` in TDZ and the outer promise would hang.\n clearTimeout(timeout);\n resolve({\n ok: false,\n error:\n error instanceof Error\n ? error.message\n : \"WebSocket connection failed\",\n });\n return;\n }\n\n ws.onopen = () => {\n clearTimeout(timeout);\n ws?.close();\n resolve({ ok: true });\n };\n\n ws.onerror = () => {\n clearTimeout(timeout);\n resolve({ ok: false, error: \"WebSocket connection failed\" });\n };\n });\n}\n\nexport async function performHealthCheck(\n options: HealthCheckOptions = {},\n): Promise<HealthCheckResult> {\n const healthUrl = resolveHealthUrl(options);\n\n const result = await checkApiAndAuth(healthUrl, options.authManager);\n const wsResult = await checkWebSocket(result.wsConfig);\n\n return {\n api: result.api,\n websocket: wsResult,\n auth: result.auth,\n overall: result.api.ok && result.auth.ok,\n };\n}\n","import type { UseEditorReturn } from \"../editor\";\nimport type { McpOperationPayload } from \"@templatical/types\";\nimport { handleOperation } from \"./mcp-operation-handler\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { watch, type Ref } from \"vue\";\n\nexport interface UseMcpListenerOptions {\n editor: UseEditorReturn;\n channel: Ref<PresenceChannel | null>;\n onOperation?: (payload: McpOperationPayload) => void;\n}\n\nexport function useMcpListener(options: UseMcpListenerOptions): void {\n const { editor, channel, onOperation } = options;\n\n watch(channel, (newChannel, oldChannel) => {\n if (oldChannel) {\n oldChannel.unbind(\"mcp-operation\");\n }\n\n if (newChannel) {\n newChannel.bind(\"mcp-operation\", (payload: McpOperationPayload) => {\n handleOperation(editor, payload);\n onOperation?.(payload);\n });\n }\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloud-CITqEHEc.js","names":[],"sources":["../../../../core/src/cloud/auth.ts","../../../../core/src/cloud/url-builder.ts","../../../../core/src/cloud/api.ts","../../../../core/src/cloud/websocket-client.ts","../../../../core/src/cloud/mcp-operation-handler.ts","../../../../core/src/cloud/ai-chat.ts","../../../../core/src/cloud/ai-config.ts","../../../../core/src/cloud/template-scoring.ts","../../../../core/src/cloud/design-reference.ts","../../../../core/src/cloud/comments-provider.ts","../../../../core/src/cloud/collaboration.ts","../../../../core/src/cloud/collaboration-broadcast.ts","../../../../core/src/cloud/web-socket.ts","../../../../core/src/cloud/saved-blocks-provider.ts","../../../../core/src/cloud/templates-provider.ts","../../../../core/src/cloud/export.ts","../../../../core/src/cloud/render-provider.ts","../../../../core/src/cloud/version-history-provider.ts","../../../../core/src/cloud/test-email.ts","../../../../core/src/cloud/test-email-provider.ts","../../../../core/src/cloud/plan-config.ts","../../../../core/src/cloud/health-check.ts","../../../../core/src/cloud/mcp-listener.ts"],"sourcesContent":["import type {\n AuthConfig,\n AuthRequestOptions,\n SdkAuthConfig,\n TestEmailConfig,\n TokenData,\n UserConfig,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\n\nexport type {\n AuthConfig,\n AuthRequestOptions,\n SdkAuthConfig,\n TestEmailConfig,\n UserConfig,\n};\n\n/**\n * Whether a token-refresh failure is worth interrupting the user over.\n *\n * **Fatal** means the endpoint actively rejected the credentials — a `4xx`. No\n * amount of retrying fixes that: every subsequent request will fail the same\n * way, so the session really is over and the editor should say so.\n *\n * **Everything else is transient**: a network blip, a `5xx`, a timeout, a\n * malformed response. Those resolve on their own, and blanking the editor over\n * one interrupts someone mid-edit on a template that is very likely unsaved.\n * `AuthManager` reports every failure to `onError` and re-throws regardless, so\n * a caller that genuinely cannot proceed still finds out; this only decides\n * whether the *overlay* goes up.\n *\n * `404` is treated as fatal deliberately: a refresh URL that does not exist is a\n * misconfiguration, and retrying a typo forever is worse than saying so.\n */\nexport function isFatalAuthError(error: unknown): boolean {\n const status = error instanceof SdkError ? error.statusCode : undefined;\n return status !== undefined && status >= 400 && status < 500;\n}\n\nexport class AuthManager {\n private static readonly DEFAULT_BASE_URL = \"https://templatical.com\";\n\n private accessToken: string | null = null;\n private expiresAt: Date | null = null;\n private _projectId: string | null = null;\n private _tenantId: string | null = null;\n private _tenantSlug: string | null = null;\n private _testEmailConfig: TestEmailConfig | null = null;\n private _userConfig: UserConfig | null = null;\n private readonly url: string;\n private readonly baseUrl: string;\n private readonly requestOptions: AuthRequestOptions;\n private readonly onError?: (error: Error) => void;\n private refreshPromise: Promise<string> | null = null;\n\n private static readonly REFRESH_THRESHOLD_MS = 60 * 1000;\n\n constructor(config: AuthConfig) {\n this.url = config.url;\n this.baseUrl = (config.baseUrl ?? AuthManager.DEFAULT_BASE_URL).replace(\n /\\/$/,\n \"\",\n );\n this.requestOptions = config.requestOptions ?? {};\n this.onError = config.onError;\n }\n\n resolveUrl(path: string): string {\n if (path.startsWith(\"http://\") || path.startsWith(\"https://\")) {\n return path;\n }\n\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return `${this.baseUrl}${normalizedPath}`;\n }\n\n get projectId(): string {\n if (!this._projectId) {\n throw new Error(\"Project ID not available. Call initialize() first.\");\n }\n return this._projectId;\n }\n\n get tenantId(): string {\n if (!this._tenantId) {\n throw new Error(\"Tenant ID not available. Call initialize() first.\");\n }\n return this._tenantId;\n }\n\n get tenantSlug(): string {\n if (!this._tenantSlug) {\n throw new Error(\"Tenant slug not available. Call initialize() first.\");\n }\n return this._tenantSlug;\n }\n\n get testEmailConfig(): TestEmailConfig | null {\n return this._testEmailConfig;\n }\n\n get userConfig(): UserConfig | null {\n return this._userConfig;\n }\n\n get accessTokenValue(): string | null {\n return this.accessToken;\n }\n\n async initialize(): Promise<void> {\n await this.ensureToken();\n }\n\n private async ensureToken(): Promise<string> {\n if (this.accessToken && !this.isTokenExpiringSoon()) {\n return this.accessToken;\n }\n return this.refreshToken();\n }\n\n private isTokenExpiringSoon(): boolean {\n if (!this.expiresAt) {\n return true;\n }\n const timeUntilExpiry = this.expiresAt.getTime() - Date.now();\n return timeUntilExpiry < AuthManager.REFRESH_THRESHOLD_MS;\n }\n\n async refreshToken(): Promise<string> {\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.performRefresh();\n\n try {\n const token = await this.refreshPromise;\n return token;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async performRefresh(): Promise<string> {\n try {\n const method = this.requestOptions.method ?? \"POST\";\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n ...this.requestOptions.headers,\n };\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n credentials: this.requestOptions.credentials ?? \"include\",\n };\n\n if (method === \"POST\" && this.requestOptions.body) {\n headers[\"Content-Type\"] = \"application/json\";\n fetchOptions.body = JSON.stringify(this.requestOptions.body);\n }\n\n const response = await fetch(this.url, fetchOptions);\n\n if (!response.ok) {\n throw new SdkError(\n `Token refresh failed: ${response.status}`,\n response.status,\n );\n }\n\n const data: TokenData = await response.json();\n\n if (!data.token || !data.expires_at || !data.project_id || !data.tenant) {\n throw new Error(\n \"Invalid token response: missing token, expires_at, project_id, or tenant\",\n );\n }\n\n this.accessToken = data.token;\n this.expiresAt = new Date(data.expires_at * 1000);\n this._projectId = data.project_id;\n this._tenantSlug = data.tenant;\n\n if (data.test_email?.allowed_emails && data.test_email?.signature) {\n this._testEmailConfig = {\n allowedEmails: data.test_email.allowed_emails,\n signature: data.test_email.signature,\n };\n } else {\n this._testEmailConfig = null;\n }\n\n if (data.user?.id && data.user?.name && data.user?.signature) {\n this._userConfig = {\n id: data.user.id,\n name: data.user.name,\n signature: data.user.signature,\n };\n } else {\n this._userConfig = null;\n }\n\n return this.accessToken;\n } catch (error) {\n const wrappedError =\n error instanceof Error\n ? error\n : new Error(\"Token refresh failed\", { cause: error });\n this.onError?.(wrappedError);\n throw wrappedError;\n }\n }\n\n async authenticatedFetch(\n url: string,\n options: RequestInit = {},\n ): Promise<Response> {\n const token = await this.ensureToken();\n const resolvedUrl = this.resolveUrl(url);\n\n const makeRequest = async (authToken: string): Promise<Response> => {\n return fetch(resolvedUrl, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${authToken}`,\n },\n });\n };\n\n let response = await makeRequest(token);\n\n if (response.status === 401) {\n const newToken = await this.refreshToken();\n response = await makeRequest(newToken);\n }\n\n return response;\n }\n}\n\nexport function createSdkAuthManager(\n config: SdkAuthConfig,\n onError?: (error: Error) => void,\n): AuthManager {\n if (config.mode === \"direct\") {\n const baseUrl = (config.baseUrl ?? \"https://templatical.com\").replace(\n /\\/$/,\n \"\",\n );\n\n return new AuthManager({\n url: `${baseUrl}/api/v1/auth/token`,\n baseUrl: config.baseUrl,\n requestOptions: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: {\n client_id: config.clientId,\n client_secret: config.clientSecret,\n tenant: config.tenant,\n client_type: \"sdk\",\n },\n },\n onError,\n });\n }\n\n return new AuthManager({\n url: config.url,\n baseUrl: config.baseUrl,\n requestOptions: config.requestOptions,\n onError,\n });\n}\n","export function buildUrl(\n template: string,\n params: Record<string, string>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, key: string) =>\n encodeURIComponent(params[key] ?? \"\"),\n );\n}\n\nconst BASE = \"/api/v1/projects/{project}/tenants/{tenant}\";\nconst TEMPLATE = `${BASE}/templates/{template}`;\nconst AI = `${TEMPLATE}/ai`;\nconst MEDIA = `${BASE}/media`;\nconst FOLDERS = `${MEDIA}/folders`;\nconst MODULES = `${BASE}/saved-modules`;\n\nexport const API_ROUTES = {\n health: \"/api/v1/health\",\n \"projects.config\": `${BASE}/config`,\n \"broadcasting.auth\": `${BASE}/broadcasting/auth`,\n \"templates.store\": `${BASE}/templates`,\n \"templates.show\": `${TEMPLATE}`,\n \"templates.update\": `${TEMPLATE}`,\n \"templates.destroy\": `${TEMPLATE}`,\n \"templates.export\": `${TEMPLATE}/export`,\n \"templates.importFromBeefree\": `${BASE}/templates/import/from-beefree`,\n \"templates.sendTestEmail\": `${TEMPLATE}/send-test-email`,\n \"versions.index\": `${TEMPLATE}/versions`,\n \"versions.store\": `${TEMPLATE}/versions`,\n \"versions.show\": `${TEMPLATE}/versions/{version}`,\n \"versions.restore\": `${TEMPLATE}/versions/{version}/restore`,\n \"comments.index\": `${TEMPLATE}/comments`,\n \"comments.store\": `${TEMPLATE}/comments`,\n \"comments.update\": `${TEMPLATE}/comments/{comment}`,\n \"comments.destroy\": `${TEMPLATE}/comments/{comment}`,\n \"comments.resolve\": `${TEMPLATE}/comments/{comment}/resolve`,\n \"ai.generate\": `${AI}/generate`,\n \"ai.conversationMessages\": `${AI}/conversation-messages`,\n \"ai.suggestions\": `${AI}/suggestions`,\n \"ai.rewriteText\": `${AI}/rewrite-text`,\n \"ai.score\": `${AI}/score`,\n \"ai.fixFinding\": `${AI}/fix-finding`,\n \"ai.generateFromDesign\": `${AI}/generate-from-design`,\n \"media.upload\": `${MEDIA}/upload`,\n \"media.browse\": `${MEDIA}/browse`,\n \"media.delete\": `${MEDIA}/delete`,\n \"media.move\": `${MEDIA}/move`,\n \"media.update\": `${MEDIA}/{media}`,\n \"media.replace\": `${MEDIA}/{media}/replace`,\n \"media.checkUsage\": `${MEDIA}/check-usage`,\n \"media.frequentlyUsed\": `${MEDIA}/frequently-used`,\n \"media.importFromUrl\": `${MEDIA}/import-from-url`,\n \"folders.index\": `${FOLDERS}`,\n \"folders.store\": `${FOLDERS}`,\n \"folders.update\": `${FOLDERS}/{mediaFolder}`,\n \"folders.destroy\": `${FOLDERS}/{mediaFolder}`,\n \"savedModules.index\": `${MODULES}`,\n \"savedModules.store\": `${MODULES}`,\n \"savedModules.update\": `${MODULES}/{savedModule}`,\n \"savedModules.destroy\": `${MODULES}/{savedModule}`,\n} as const;\n","import type {\n ApiError,\n ApiResponse,\n CommentResponse,\n CustomFont,\n PlanConfig,\n SavedBlock,\n Template,\n TemplateContent,\n TemplatePatch,\n TemplateVersionResponse,\n} from \"@templatical/types\";\nimport { SdkError } from \"@templatical/types\";\nimport type { Block } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\n\nexport class ApiClient {\n constructor(private readonly authManager: AuthManager) {}\n\n private get projectId(): string {\n return this.authManager.projectId;\n }\n\n private get tenantSlug(): string {\n return this.authManager.tenantSlug;\n }\n\n private get baseParams(): Record<string, string> {\n return { project: this.projectId, tenant: this.tenantSlug };\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const response = await this.authManager.authenticatedFetch(path, {\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n\n const errorMessage = this.extractFirstValidationError(error);\n throw new SdkError(errorMessage, response.status);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n const json: ApiResponse<T> = await response.json();\n return json.data;\n }\n\n private extractFirstValidationError(error: ApiError): string {\n if (error.errors) {\n const firstField = Object.keys(error.errors)[0];\n if (firstField && error.errors[firstField]?.length > 0) {\n return error.errors[firstField][0];\n }\n }\n return error.message;\n }\n\n async createTemplate(\n content: TemplateContent,\n name?: string,\n ): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.store\"], this.baseParams),\n {\n method: \"POST\",\n // `name` is forwarded only when set. Cloud's backend does not store it\n // yet; until it does the key is simply ignored, and an absent name reads\n // as \"unnamed\" rather than as an explicit null — the same\n // forward-compatible treatment `category` gets on saved blocks.\n body: JSON.stringify(\n name !== undefined ? { content, name } : { content },\n ),\n },\n );\n }\n\n async getTemplate(id: string): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.show\"], {\n ...this.baseParams,\n template: id,\n }),\n );\n }\n\n /**\n * Apply a partial update. Takes a patch rather than bare content so a rename\n * can travel without content and vice versa — the shape `TemplatePatch`\n * defines, which is what `createCloudTemplatesProvider` forwards verbatim. Only\n * the keys present are sent.\n */\n async updateTemplate(id: string, patch: TemplatePatch): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"templates.update\"], {\n ...this.baseParams,\n template: id,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify(patch),\n },\n );\n }\n\n async createVersion(\n templateId: string,\n content: TemplateContent,\n label?: string,\n ): Promise<TemplateVersionResponse> {\n return this.request<TemplateVersionResponse>(\n buildUrl(API_ROUTES[\"versions.store\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(\n label !== undefined ? { content, label } : { content },\n ),\n },\n );\n }\n\n async deleteTemplate(id: string): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"templates.destroy\"], {\n ...this.baseParams,\n template: id,\n }),\n {\n method: \"DELETE\",\n },\n );\n }\n\n async getVersions(templateId: string): Promise<TemplateVersionResponse[]> {\n return this.request<TemplateVersionResponse[]>(\n buildUrl(API_ROUTES[\"versions.index\"], {\n ...this.baseParams,\n template: templateId,\n }),\n );\n }\n\n async getVersion(\n templateId: string,\n versionId: string,\n ): Promise<TemplateVersionResponse> {\n return this.request<TemplateVersionResponse>(\n buildUrl(API_ROUTES[\"versions.show\"], {\n ...this.baseParams,\n template: templateId,\n version: versionId,\n }),\n );\n }\n\n async restoreVersion(\n templateId: string,\n versionId: string,\n ): Promise<Template> {\n return this.request<Template>(\n buildUrl(API_ROUTES[\"versions.restore\"], {\n ...this.baseParams,\n template: templateId,\n version: versionId,\n }),\n {\n method: \"POST\",\n },\n );\n }\n\n async exportTemplate(\n templateId: string,\n fontsPayload?: {\n customFonts: CustomFont[];\n defaultFallback: string;\n },\n ): Promise<{ html: string; mjml: string }> {\n const body = fontsPayload\n ? JSON.stringify({\n custom_fonts: fontsPayload.customFonts,\n default_fallback: fontsPayload.defaultFallback,\n })\n : undefined;\n\n return this.request<{ html: string; mjml: string }>(\n buildUrl(API_ROUTES[\"templates.export\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body,\n },\n );\n }\n\n async sendTestEmail(\n templateId: string,\n payload: {\n recipient: string;\n html: string;\n allowed_emails: string[];\n signature: string;\n },\n ): Promise<void> {\n await this.request<void>(\n buildUrl(API_ROUTES[\"templates.sendTestEmail\"], {\n ...this.baseParams,\n template: templateId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(payload),\n },\n );\n }\n\n private commentsUrl(templateId: string, commentId?: string): string {\n if (commentId) {\n return buildUrl(API_ROUTES[\"comments.update\"], {\n ...this.baseParams,\n template: templateId,\n comment: commentId,\n });\n }\n return buildUrl(API_ROUTES[\"comments.index\"], {\n ...this.baseParams,\n template: templateId,\n });\n }\n\n async getComments(templateId: string): Promise<CommentResponse[]> {\n return this.request<CommentResponse[]>(this.commentsUrl(templateId));\n }\n\n async createComment(\n templateId: string,\n data: {\n body: string;\n block_id?: string;\n parent_id?: string;\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(this.commentsUrl(templateId), {\n method: \"POST\",\n body: JSON.stringify(data),\n headers,\n });\n }\n\n async updateComment(\n templateId: string,\n commentId: string,\n data: {\n body: string;\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(\n this.commentsUrl(templateId, commentId),\n {\n method: \"PUT\",\n body: JSON.stringify(data),\n headers,\n },\n );\n }\n\n async deleteComment(\n templateId: string,\n commentId: string,\n data: {\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<void> {\n return this.request<void>(this.commentsUrl(templateId, commentId), {\n method: \"DELETE\",\n body: JSON.stringify(data),\n headers,\n });\n }\n\n async resolveComment(\n templateId: string,\n commentId: string,\n data: {\n user_id: string;\n user_name: string;\n user_signature: string;\n },\n headers?: Record<string, string>,\n ): Promise<CommentResponse> {\n return this.request<CommentResponse>(\n buildUrl(API_ROUTES[\"comments.resolve\"], {\n ...this.baseParams,\n template: templateId,\n comment: commentId,\n }),\n {\n method: \"POST\",\n body: JSON.stringify(data),\n headers,\n },\n );\n }\n\n async fetchConfig(): Promise<PlanConfig> {\n return this.request<PlanConfig>(\n buildUrl(API_ROUTES[\"projects.config\"], this.baseParams),\n );\n }\n\n async listModules(search?: string, category?: string): Promise<SavedBlock[]> {\n const url = buildUrl(API_ROUTES[\"savedModules.index\"], this.baseParams);\n const params = new URLSearchParams();\n if (search) params.set(\"search\", search);\n if (category) params.set(\"category\", category);\n const query = params.size > 0 ? `?${params}` : \"\";\n return this.request<SavedBlock[]>(`${url}${query}`);\n }\n\n async createModule(data: {\n name: string;\n content: Block[];\n category?: string;\n }): Promise<SavedBlock> {\n return this.request<SavedBlock>(\n buildUrl(API_ROUTES[\"savedModules.store\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify(data),\n },\n );\n }\n\n async updateModule(\n id: string,\n data: Partial<{ name: string; content: Block[]; category: string }>,\n ): Promise<SavedBlock> {\n return this.request<SavedBlock>(\n buildUrl(API_ROUTES[\"savedModules.update\"], {\n ...this.baseParams,\n savedModule: id,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify(data),\n },\n );\n }\n\n async deleteModule(id: string): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"savedModules.destroy\"], {\n ...this.baseParams,\n savedModule: id,\n }),\n {\n method: \"DELETE\",\n },\n );\n }\n}\n","import type { WebSocketServerConfig } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type Pusher from \"pusher-js\";\nimport type { Channel, PresenceChannel } from \"pusher-js\";\n\ntype PusherType = Pusher;\n\nexport interface WebSocketConfig {\n host: string;\n port: number;\n appKey: string;\n}\n\nexport function resolveWebSocketConfig(\n serverConfig: WebSocketServerConfig,\n): WebSocketConfig {\n return {\n host: serverConfig.host,\n port: serverConfig.port,\n appKey: serverConfig.app_key,\n };\n}\n\nexport interface PresenceMember {\n id: string;\n name: string;\n type: \"user\" | \"mcp\";\n}\n\nexport interface WebSocketClientOptions {\n authManager: AuthManager;\n config: WebSocketConfig;\n onError?: (error: Error) => void;\n}\n\nexport class WebSocketClient {\n private pusher: PusherType | null = null;\n private readonly authManager: AuthManager;\n private readonly config: WebSocketConfig;\n private readonly onError?: (error: Error) => void;\n\n constructor(options: WebSocketClientOptions) {\n this.authManager = options.authManager;\n this.config = options.config;\n this.onError = options.onError;\n }\n\n async connect(): Promise<void> {\n if (this.pusher) {\n return;\n }\n\n let Pusher: typeof import(\"pusher-js\").default;\n try {\n ({ default: Pusher } = await import(\"pusher-js\"));\n } catch {\n throw new Error(\n \"[Templatical] Cloud features require the optional peer dependency 'pusher-js'. Please install it.\",\n );\n }\n\n const { host, port, appKey } = this.config;\n const authEndpoint = this.authManager.resolveUrl(\n buildUrl(API_ROUTES[\"broadcasting.auth\"], {\n project: this.authManager.projectId,\n tenant: this.authManager.tenantSlug,\n }),\n );\n\n this.pusher = new Pusher(appKey, {\n wsHost: host,\n wsPort: port,\n wssPort: port,\n forceTLS: true,\n disableStats: true,\n enabledTransports: [\"ws\", \"wss\"],\n cluster: \"\",\n channelAuthorization: {\n transport: \"ajax\",\n endpoint: authEndpoint,\n headers: {\n Authorization: `Bearer ${this.authManager.accessTokenValue}`,\n Accept: \"application/json\",\n },\n params: {\n user_id: this.authManager.userConfig?.id ?? \"\",\n user_name: this.authManager.userConfig?.name ?? \"\",\n user_signature: this.authManager.userConfig?.signature ?? \"\",\n },\n },\n });\n\n this.pusher.connection.bind(\"error\", (error: unknown) => {\n this.onError?.(\n error instanceof Error\n ? error\n : new Error(\"WebSocket connection error\"),\n );\n });\n }\n\n subscribePresence(channelName: string): PresenceChannel {\n if (!this.pusher) {\n throw new Error(\"WebSocketClient not connected. Call connect() first.\");\n }\n\n return this.pusher.subscribe(channelName) as PresenceChannel;\n }\n\n unsubscribe(channelName: string): void {\n this.pusher?.unsubscribe(channelName);\n }\n\n getChannel(channelName: string): Channel | undefined {\n return this.pusher?.channel(channelName);\n }\n\n disconnect(): void {\n if (this.pusher) {\n this.pusher.disconnect();\n this.pusher = null;\n }\n }\n\n getSocketId(): string | null {\n return this.pusher?.connection.socket_id ?? null;\n }\n\n get isConnected(): boolean {\n return this.pusher?.connection.state === \"connected\";\n }\n}\n","import type { McpOperationPayload } from \"@templatical/types\";\nimport type {\n Block,\n TemplateContent,\n TemplateSettings,\n} from \"@templatical/types\";\nimport type { UseEditorReturn } from \"../editor\";\n\nexport function handleOperation(\n editor: UseEditorReturn,\n payload: McpOperationPayload,\n): void {\n const { operation, data } = payload;\n\n switch (operation) {\n case \"add_block\":\n editor.addBlock(\n data.block as Block,\n data.section_id as string | undefined,\n data.column_index as number | undefined,\n data.index as number | undefined,\n );\n break;\n\n case \"update_block\":\n editor.updateBlock(\n data.block_id as string,\n data.updates as Partial<Block>,\n );\n break;\n\n case \"delete_block\":\n editor.removeBlock(data.block_id as string);\n break;\n\n case \"move_block\":\n editor.moveBlock(\n data.block_id as string,\n data.index as number,\n data.section_id as string | undefined,\n data.column_index as number | undefined,\n );\n break;\n\n case \"update_settings\":\n editor.updateSettings(data.updates as Partial<TemplateSettings>);\n break;\n\n case \"set_content\":\n editor.setContent(data.content as TemplateContent);\n break;\n\n case \"update_block_style\":\n editor.updateBlock(\n data.block_id as string,\n {\n styles: data.styles,\n } as Partial<Block>,\n );\n break;\n }\n}\n","import type { AiChatMessage } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { MergeTag, TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseAiChatOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n onApply?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseAiChatReturn {\n messages: ReturnType<typeof ref<AiChatMessage[]>>;\n isGenerating: ReturnType<typeof ref<boolean>>;\n isLoadingHistory: ReturnType<typeof ref<boolean>>;\n isLastChangeReverted: ReturnType<typeof ref<boolean>>;\n lastApplyMessageId: ReturnType<typeof ref<string | null>>;\n error: ReturnType<typeof ref<string | null>>;\n failedPrompt: ReturnType<typeof ref<string | null>>;\n suggestions: ReturnType<typeof ref<string[]>>;\n isLoadingSuggestions: ReturnType<typeof ref<boolean>>;\n sendPrompt: (\n prompt: string,\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<TemplateContent | null>;\n toggleLastRevert: () => void;\n loadConversation: () => Promise<void>;\n loadSuggestions: (\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<void>;\n clearChat: () => void;\n}\n\nlet messageIdCounter = 0;\n\nfunction generateMessageId(): string {\n return `msg_${Date.now()}_${++messageIdCounter}`;\n}\n\nexport function useAiChat(options: UseAiChatOptions): UseAiChatReturn {\n const { authManager, getTemplateId, onApply, onError } = options;\n\n const messages = ref<AiChatMessage[]>([]);\n const isGenerating = ref(false);\n const isLoadingHistory = ref(false);\n const error = ref<string | null>(null);\n const failedPrompt = ref<string | null>(null);\n const conversationId = ref<string | null>(null);\n const lastApplyMessageId = ref<string | null>(null);\n const lastPreviousContent = ref<TemplateContent | null>(null);\n const lastAppliedContent = ref<TemplateContent | null>(null);\n const isLastChangeReverted = ref(false);\n const suggestions = ref<string[]>([]);\n const isLoadingSuggestions = ref(false);\n\n function updateMessage(msgId: string, updates: Partial<AiChatMessage>): void {\n const idx = messages.value.findIndex((m) => m.id === msgId);\n if (idx === -1) {\n return;\n }\n\n const updated = { ...messages.value[idx], ...updates };\n messages.value = [\n ...messages.value.slice(0, idx),\n updated,\n ...messages.value.slice(idx + 1),\n ];\n }\n\n async function loadConversation(): Promise<void> {\n const templateId = getTemplateId();\n if (!templateId) {\n return;\n }\n\n isLoadingHistory.value = true;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.conversationMessages\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n return;\n }\n\n const data = await response.json();\n\n if (data.conversation_id) {\n conversationId.value = data.conversation_id;\n }\n\n if (Array.isArray(data.data) && data.data.length > 0) {\n messages.value = data.data.map(\n (msg: {\n id: string;\n role: \"user\" | \"assistant\";\n content: string;\n created_at: string;\n }) => ({\n id: msg.id,\n role: msg.role,\n content: msg.content,\n timestamp: new Date(msg.created_at).getTime(),\n }),\n );\n }\n } catch {\n // Silently fail - chat will start fresh\n } finally {\n isLoadingHistory.value = false;\n }\n }\n\n async function loadSuggestions(\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<void> {\n const templateId = getTemplateId();\n if (!templateId) {\n return;\n }\n\n isLoadingSuggestions.value = true;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.suggestions\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n current_content: currentContent,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n return;\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"done\" && Array.isArray(event.suggestions)) {\n suggestions.value = event.suggestions.slice(0, 3);\n }\n }\n }\n } catch {\n // Silently fail - suggestions are non-critical\n } finally {\n isLoadingSuggestions.value = false;\n }\n }\n\n async function sendPrompt(\n prompt: string,\n currentContent: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<TemplateContent | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new Error(\"Template must be saved before using AI generation\");\n }\n\n isGenerating.value = true;\n error.value = null;\n failedPrompt.value = null;\n suggestions.value = [];\n\n const userMsgId = generateMessageId();\n messages.value = [\n ...messages.value,\n {\n id: userMsgId,\n role: \"user\",\n content: prompt,\n timestamp: Date.now(),\n },\n ];\n\n const assistantMsgId = generateMessageId();\n messages.value = [\n ...messages.value,\n {\n id: assistantMsgId,\n role: \"assistant\",\n content: \"\",\n timestamp: Date.now(),\n },\n ];\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.generate\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n prompt,\n current_content: currentContent,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n conversation_id: conversationId.value,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null);\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n throw new Error(errorData?.message || \"Failed to generate template\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: TemplateContent | null = null;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n const jsonStr = line.slice(6);\n\n let event;\n try {\n event = JSON.parse(jsonStr);\n } catch {\n continue;\n }\n\n if (event.type === \"text\") {\n updateMessage(assistantMsgId, {\n content:\n (messages.value.find((m) => m.id === assistantMsgId)\n ?.content ?? \"\") + event.text,\n });\n } else if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to generate template\");\n } else if (event.type === \"done\") {\n if (event.conversation_id) {\n conversationId.value = event.conversation_id;\n }\n\n updateMessage(assistantMsgId, {\n content: event.text,\n });\n\n result = event.content ?? null;\n\n if (result) {\n lastPreviousContent.value = currentContent;\n lastAppliedContent.value = result;\n lastApplyMessageId.value = assistantMsgId;\n isLastChangeReverted.value = false;\n onApply?.(result);\n } else {\n error.value = \"ai_apply_failed\";\n }\n }\n }\n }\n } finally {\n // Release the underlying network resource even on early exit.\n // Without this, throwing out of the loop (e.g. on an `error` SSE\n // event) leaves the socket and read buffer attached until GC.\n reader.cancel().catch(() => {});\n }\n\n return result;\n } catch (err) {\n const wrappedError =\n err instanceof Error\n ? err\n : new Error(\"Failed to generate template\", { cause: err });\n error.value = wrappedError.message;\n failedPrompt.value = prompt;\n onError?.(wrappedError);\n\n messages.value = messages.value.filter(\n (m) => m.id !== userMsgId && m.id !== assistantMsgId,\n );\n\n return null;\n } finally {\n isGenerating.value = false;\n }\n }\n\n function toggleLastRevert(): void {\n if (isLastChangeReverted.value) {\n if (lastAppliedContent.value) {\n onApply?.(lastAppliedContent.value);\n }\n isLastChangeReverted.value = false;\n } else {\n if (lastPreviousContent.value) {\n onApply?.(lastPreviousContent.value);\n }\n isLastChangeReverted.value = true;\n }\n }\n\n function clearChat(): void {\n messages.value = [];\n conversationId.value = null;\n error.value = null;\n lastApplyMessageId.value = null;\n lastPreviousContent.value = null;\n lastAppliedContent.value = null;\n isLastChangeReverted.value = false;\n }\n\n return {\n messages,\n isGenerating,\n isLoadingHistory,\n isLastChangeReverted,\n lastApplyMessageId,\n error,\n failedPrompt,\n suggestions,\n isLoadingSuggestions,\n sendPrompt,\n toggleLastRevert,\n loadConversation,\n loadSuggestions,\n clearChat,\n };\n}\n","import type { AiConfig } from \"@templatical/types\";\nimport { computed, type ComputedRef } from \"vue\";\n\nexport interface UseAiConfigReturn {\n isFeatureEnabled: (feature: keyof AiConfig) => boolean;\n hasAnyMenuFeature: ComputedRef<boolean>;\n}\n\nexport function useAiConfig(config?: AiConfig | false): UseAiConfigReturn {\n function isFeatureEnabled(feature: keyof AiConfig): boolean {\n if (config === false) {\n return false;\n }\n\n return config?.[feature] !== false;\n }\n\n const hasAnyMenuFeature = computed(\n () =>\n isFeatureEnabled(\"chat\") ||\n isFeatureEnabled(\"scoring\") ||\n isFeatureEnabled(\"designToTemplate\"),\n );\n\n return {\n isFeatureEnabled,\n hasAnyMenuFeature,\n };\n}\n","import type {\n ScoringCategory,\n ScoringFinding,\n ScoringResult,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { MergeTag, TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseTemplateScoringOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n}\n\nexport interface UseTemplateScoringReturn {\n isScoring: ReturnType<typeof ref<boolean>>;\n scoringResult: ReturnType<typeof ref<ScoringResult | null>>;\n error: ReturnType<typeof ref<string | null>>;\n fixingFindingId: ReturnType<typeof ref<string | null>>;\n fixStreamingText: ReturnType<typeof ref<string>>;\n fixError: ReturnType<typeof ref<string | null>>;\n score: (\n content: TemplateContent,\n mergeTags: MergeTag[],\n ) => Promise<ScoringResult | null>;\n fixFinding: (\n blockContent: string,\n finding: ScoringFinding,\n mergeTags: MergeTag[],\n ) => Promise<string | null>;\n removeFinding: (category: ScoringCategory, findingId: string) => void;\n reset: () => void;\n}\n\nexport function useTemplateScoring(\n options: UseTemplateScoringOptions,\n): UseTemplateScoringReturn {\n const { authManager, getTemplateId } = options;\n\n const isScoring = ref(false);\n const scoringResult = ref<ScoringResult | null>(null);\n const error = ref<string | null>(null);\n const fixingFindingId = ref<string | null>(null);\n const fixStreamingText = ref(\"\");\n const fixError = ref<string | null>(null);\n\n async function score(\n content: TemplateContent,\n mergeTags: MergeTag[],\n ): Promise<ScoringResult | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n return null;\n }\n\n isScoring.value = true;\n error.value = null;\n scoringResult.value = null;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.score\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n current_content: content,\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n const errorData = await response.json().catch(() => null);\n throw new Error(errorData?.message || \"Failed to score template\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: ScoringResult | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to score template\");\n }\n\n if (event.type === \"done\") {\n result = event.result ?? null;\n }\n }\n }\n\n if (result) {\n for (const [category, categoryData] of Object.entries(\n result.categories,\n )) {\n for (const finding of categoryData.findings) {\n finding.category = category as ScoringCategory;\n }\n }\n }\n\n scoringResult.value = result;\n return result;\n } catch (err) {\n error.value =\n err instanceof Error ? err.message : \"Failed to score template\";\n return null;\n } finally {\n isScoring.value = false;\n }\n }\n\n async function fixFinding(\n blockContent: string,\n finding: ScoringFinding,\n mergeTags: MergeTag[],\n ): Promise<string | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n return null;\n }\n\n fixingFindingId.value = finding.id;\n fixStreamingText.value = \"\";\n fixError.value = null;\n\n try {\n const url = buildUrl(API_ROUTES[\"ai.fixFinding\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n content: blockContent,\n finding: {\n id: finding.id,\n message: finding.message,\n suggestion: finding.suggestion,\n category: finding.category,\n },\n merge_tags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n }),\n });\n\n if (!response.ok) {\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n const errorData = await response.json().catch(() => null);\n throw new Error(errorData?.message || \"Failed to fix finding\");\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: string | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n let event;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n if (event.type === \"text\") {\n fixStreamingText.value += event.text;\n } else if (event.type === \"error\") {\n throw new Error(event.message || \"Failed to fix finding\");\n } else if (event.type === \"done\") {\n result = event.content ?? null;\n }\n }\n }\n\n return result;\n } catch (err) {\n fixError.value =\n err instanceof Error ? err.message : \"Failed to fix finding\";\n return null;\n } finally {\n fixingFindingId.value = null;\n }\n }\n\n function removeFinding(category: ScoringCategory, findingId: string): void {\n if (!scoringResult.value) {\n return;\n }\n\n const cat = scoringResult.value.categories[category];\n if (!cat) {\n return;\n }\n\n cat.findings = cat.findings.filter((f) => f.id !== findingId);\n }\n\n function reset(): void {\n isScoring.value = false;\n scoringResult.value = null;\n error.value = null;\n fixingFindingId.value = null;\n fixStreamingText.value = \"\";\n fixError.value = null;\n }\n\n return {\n isScoring,\n scoringResult,\n error,\n fixingFindingId,\n fixStreamingText,\n fixError,\n score,\n fixFinding,\n removeFinding,\n reset,\n };\n}\n","import type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport { ref } from \"vue\";\n\nexport interface UseDesignReferenceOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n onApply?: (content: TemplateContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseDesignReferenceReturn {\n isGenerating: ReturnType<typeof ref<boolean>>;\n error: ReturnType<typeof ref<string | null>>;\n generate: (input: DesignReferenceInput) => Promise<TemplateContent | null>;\n reset: () => void;\n}\n\nexport interface DesignReferenceInput {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n}\n\nexport function useDesignReference(\n options: UseDesignReferenceOptions,\n): UseDesignReferenceReturn {\n const { authManager, getTemplateId, onApply, onError } = options;\n\n const isGenerating = ref(false);\n const error = ref<string | null>(null);\n\n async function generate(\n input: DesignReferenceInput,\n ): Promise<TemplateContent | null> {\n const templateId = getTemplateId();\n if (!templateId) {\n throw new Error(\"Template must be saved before using design reference\");\n }\n\n isGenerating.value = true;\n error.value = null;\n\n try {\n const formData = new FormData();\n\n if (input.prompt) {\n formData.append(\"prompt\", input.prompt);\n }\n\n if (input.imageUpload) {\n formData.append(\"image_upload\", input.imageUpload);\n }\n\n if (input.pdfUpload) {\n formData.append(\"pdf_upload\", input.pdfUpload);\n }\n\n const url = buildUrl(API_ROUTES[\"ai.generateFromDesign\"], {\n project: authManager.projectId,\n tenant: authManager.tenantSlug,\n template: templateId,\n });\n\n const response = await authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: {\n Accept: \"text/event-stream\",\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => null);\n if (response.status === 403) {\n throw new Error(\"ai_generation_not_available\");\n }\n throw new Error(\n errorData?.message || \"Failed to generate template from design\",\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error(\"Failed to read stream\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let result: TemplateContent | null = null;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) {\n continue;\n }\n\n const jsonStr = line.slice(6);\n\n let event;\n try {\n event = JSON.parse(jsonStr);\n } catch {\n continue;\n }\n\n if (event.type === \"error\") {\n throw new Error(\n event.message || \"Failed to generate template from design\",\n );\n }\n\n if (event.type === \"done\") {\n result = event.content ?? null;\n\n if (result) {\n onApply?.(result);\n }\n }\n }\n }\n\n return result;\n } catch (err) {\n const wrappedError =\n err instanceof Error\n ? err\n : new Error(\"Failed to generate template from design\", {\n cause: err,\n });\n error.value = wrappedError.message;\n onError?.(wrappedError);\n\n return null;\n } finally {\n isGenerating.value = false;\n }\n }\n\n function reset(): void {\n isGenerating.value = false;\n error.value = null;\n }\n\n return {\n isGenerating,\n error,\n generate,\n reset,\n };\n}\n","import type {\n Comment,\n CommentChange,\n CommentInput,\n CommentPatch,\n CommentResponse,\n CommentsProvider,\n UserConfig,\n} from \"@templatical/types\";\nimport { watch, type Ref } from \"vue\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * The two methods this adapter needs from a Pusher channel, named structurally.\n *\n * Deliberately not `PresenceChannel`: `pusher-js` is an **optional** peer of this\n * package, so a type imported from it would make `@templatical/editor` — which\n * holds the channel ref for us — fail to typecheck without it installed. A\n * `PresenceChannel` satisfies this shape, so nothing at the call site changes.\n */\nexport interface RealtimeChannel {\n bind(event: string, handler: (payload: any) => void): unknown;\n unbind(event: string, handler?: (payload: any) => void): unknown;\n}\n\n/** The Pusher event Cloud's backend fans comment writes out on. */\nconst BROADCAST_EVENT = \"comment-broadcast\";\n\ninterface CommentBroadcastPayload {\n action:\n | \"comment_created\"\n | \"comment_updated\"\n | \"comment_deleted\"\n | \"comment_resolved\"\n | \"comment_unresolved\";\n comment: CommentResponse;\n}\n\n/**\n * Cloud's wire shape → the contract shape. The whole job of this adapter, plus\n * auth: snake_case to camelCase, and the two flat `author_*` / `resolved_by_*`\n * pairs into {@link CommentAuthor} objects.\n */\nfunction toComment(record: CommentResponse): Comment {\n const comment: Comment = {\n id: record.id,\n body: record.body,\n author: { id: record.author_identifier, name: record.author_name },\n createdAt: record.created_at,\n blockId: record.block_id,\n parentId: record.parent_id,\n resolvedAt: record.resolved_at,\n };\n\n // Cloud stamps `updated_at` on creation, so forwarding it unconditionally would\n // mark every comment \"(edited)\". The contract says the key is present *only*\n // when the body has changed since.\n if (record.updated_at !== record.created_at) {\n comment.updatedAt = record.updated_at;\n }\n if (record.resolved_by_identifier) {\n comment.resolvedBy = {\n id: record.resolved_by_identifier,\n name: record.resolved_by_name ?? \"\",\n };\n }\n if (record.replies?.length) {\n comment.replies = record.replies.map(toComment);\n }\n return comment;\n}\n\nexport interface CreateCloudCommentsProviderOptions {\n authManager: AuthManager;\n /**\n * Cloud's presence channel for the open template, or `null` before it joins.\n *\n * A ref rather than a value because the channel arrives after the template\n * loads and is replaced when another one is opened — {@link subscribe} watches\n * it and rebinds, which is what keeps realtime optional from the editor's point\n * of view: the contract only ever sees `subscribe`.\n */\n channel: Ref<RealtimeChannel | null>;\n /**\n * The socket id to stamp on writes, so Cloud's backend can skip echoing them\n * back to their author. Returns `null` before the socket connects.\n */\n getSocketId?: () => string | null;\n}\n\n/**\n * Cloud-backed {@link CommentsProvider} — the Templatical Cloud adapter for the\n * same review contract consumers implement themselves.\n *\n * All four mutations are enabled: comment storage and its realtime fan-out are\n * what the `commenting` plan feature pays for, so there is no Cloud tier that can\n * read a thread but not reply to it. A consumer who wants a read-only review\n * supplies their own provider with the mutations set to `false`.\n *\n * Two things about the write payloads are Cloud's alone and stay on this side of\n * the seam:\n *\n * - **The author is signed.** `user_id` / `user_name` / `user_signature` come from\n * the JWT, not from the editor's `user` config, so a browser cannot attribute a\n * comment to someone else. The editor's `user` key still gates the feature and\n * drives \"you wrote this\" in the UI; the two agree because `initCloud()` fills\n * `user` from the same JWT.\n * - **`X-Socket-ID`.** Cloud excludes the originating socket from the broadcast,\n * which is why a local write and its echo can't double-post. Every implementation\n * is nonetheless echo-safe (`upsert` replaces by id), so a backend without that\n * header is fine too.\n */\nexport function createCloudCommentsProvider(\n options: CreateCloudCommentsProviderOptions,\n): CommentsProvider {\n const { authManager, channel, getSocketId } = options;\n const api = new ApiClient(authManager);\n\n function userPayload(): {\n user_id: string;\n user_name: string;\n user_signature: string;\n } {\n const user: UserConfig | null = authManager.userConfig;\n if (!user) {\n // Unreachable through the editor — `initCloud()` derives its `user` config\n // from this same field, so the feature is unavailable without it and no\n // action renders. A headless caller can still get here.\n throw new Error(\n \"[Templatical] Cloud comments need a signed user in the auth token. The project's token endpoint must include a `user` claim.\",\n );\n }\n return {\n user_id: user.id,\n user_name: user.name,\n user_signature: user.signature,\n };\n }\n\n function socketHeaders(): Record<string, string> | undefined {\n const socketId = getSocketId?.();\n return socketId ? { \"X-Socket-ID\": socketId } : undefined;\n }\n\n return {\n async list(templateId: string): Promise<Comment[]> {\n const records = await api.getComments(templateId);\n return records.map(toComment);\n },\n\n async create(templateId: string, input: CommentInput): Promise<Comment> {\n return toComment(\n await api.createComment(\n templateId,\n {\n body: input.body,\n block_id: input.blockId,\n parent_id: input.parentId,\n ...userPayload(),\n },\n socketHeaders(),\n ),\n );\n },\n\n async update(\n templateId: string,\n commentId: string,\n patch: CommentPatch,\n ): Promise<Comment> {\n if (patch.body === undefined) {\n // `body` is the only editable field, so an empty patch has nothing to\n // send. Refuse rather than PUT an undefined body over a real one.\n throw new Error(\n \"[Templatical] Cloud comments: update() needs a `body` in the patch.\",\n );\n }\n return toComment(\n await api.updateComment(\n templateId,\n commentId,\n { body: patch.body, ...userPayload() },\n socketHeaders(),\n ),\n );\n },\n\n async delete(templateId: string, commentId: string): Promise<void> {\n await api.deleteComment(\n templateId,\n commentId,\n userPayload(),\n socketHeaders(),\n );\n },\n\n async setResolved(\n templateId: string,\n commentId: string,\n _resolved: boolean,\n ): Promise<Comment> {\n // Cloud's endpoint toggles server-side, so the requested state is not sent.\n // The response is authoritative either way, and `useComments` fires its\n // event off `resolvedAt` in that response rather than off what was asked —\n // so a disagreement surfaces as the truth rather than as a wrong event.\n return toComment(\n await api.resolveComment(\n templateId,\n commentId,\n userPayload(),\n socketHeaders(),\n ),\n );\n },\n\n subscribe(\n _templateId: string,\n onChange: (change: CommentChange) => void,\n ): () => void {\n function handle(payload: CommentBroadcastPayload): void {\n const comment = toComment(payload.comment);\n switch (payload.action) {\n case \"comment_created\":\n onChange({ type: \"created\", comment });\n break;\n case \"comment_deleted\":\n onChange({\n type: \"deleted\",\n commentId: comment.id,\n parentId: comment.parentId,\n });\n break;\n // A resolve and an unresolve are both a changed comment; `resolvedAt`\n // in the payload is what says which, and `useComments` reads it.\n case \"comment_updated\":\n case \"comment_resolved\":\n case \"comment_unresolved\":\n onChange({ type: \"updated\", comment });\n break;\n }\n }\n\n // The channel is joined when the template loads, i.e. after this provider is\n // built, and is replaced when another template is opened — so the binding\n // follows the ref rather than being made once. `_templateId` is unused\n // because Cloud's channel *is* per-template: a channel change already means\n // a template change.\n const stopWatching = watch(\n channel,\n (next, previous) => {\n previous?.unbind(BROADCAST_EVENT, handle);\n next?.bind(BROADCAST_EVENT, handle);\n },\n { immediate: true },\n );\n\n return () => {\n stopWatching();\n // The watcher only unbinds on a *transition*; without this the handler\n // stays attached to the current channel and keeps mutating a torn-down\n // list.\n channel.value?.unbind(BROADCAST_EVENT, handle);\n };\n },\n };\n}\n","import type { UseEditorReturn } from \"../editor\";\nimport type { AuthManager } from \"./auth\";\nimport type { Collaborator, McpOperationPayload } from \"@templatical/types\";\nimport { handleOperation } from \"./mcp-operation-handler\";\nimport type { PresenceMember } from \"./websocket-client\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { computed, onScopeDispose, ref, watch, type Ref } from \"vue\";\n\nconst COLLAB_EVENTS = [\n \"pusher:member_added\",\n \"pusher:member_removed\",\n \"client-block_locked\",\n \"client-block_unlocked\",\n \"client-operation\",\n \"mcp-operation\",\n] as const;\n\nfunction unbindCollabEvents(channel: PresenceChannel): void {\n for (const event of COLLAB_EVENTS) {\n channel.unbind(event);\n }\n}\n\nconst COLLABORATOR_COLORS = [\n \"#3b82f6\",\n \"#ef4444\",\n \"#10b981\",\n \"#f59e0b\",\n \"#8b5cf6\",\n \"#ec4899\",\n \"#06b6d4\",\n \"#f97316\",\n \"#6366f1\",\n \"#14b8a6\",\n];\n\nexport interface UseCollaborationOptions {\n authManager: AuthManager;\n editor: UseEditorReturn;\n channel: Ref<PresenceChannel | null>;\n onError?: (error: Error) => void;\n onCollaboratorJoined?: (collaborator: Collaborator) => void;\n onCollaboratorLeft?: (collaborator: Collaborator) => void;\n onBlockLocked?: (event: {\n blockId: string;\n collaborator: Collaborator;\n }) => void;\n onBlockUnlocked?: (event: {\n blockId: string;\n collaborator: Collaborator;\n }) => void;\n}\n\nexport interface UseCollaborationReturn {\n collaborators: Ref<Collaborator[]>;\n lockedBlocks: Ref<Map<string, Collaborator>>;\n}\n\nexport function useCollaboration(\n options: UseCollaborationOptions,\n): UseCollaborationReturn & {\n _broadcastOperation: (payload: McpOperationPayload) => void;\n _isProcessingRemoteOperation: () => boolean;\n} {\n const { authManager, editor, channel } = options;\n\n const collaborators = ref<Collaborator[]>([]);\n const lockedBlocks = ref<Map<string, Collaborator>>(new Map());\n\n let colorIndex = 0;\n let isProcessingRemoteOperation = false;\n\n const myUserId = computed(() => authManager.userConfig?.id ?? \"\");\n\n function assignColor(): string {\n const color = COLLABORATOR_COLORS[colorIndex % COLLABORATOR_COLORS.length];\n colorIndex++;\n return color;\n }\n\n function addCollaborator(member: PresenceMember): Collaborator | undefined {\n if (member.id === myUserId.value) {\n return undefined;\n }\n\n if (collaborators.value.some((c) => c.id === member.id)) {\n return undefined;\n }\n\n const collaborator: Collaborator = {\n id: member.id,\n name: member.name,\n color: assignColor(),\n selectedBlockId: null,\n };\n\n collaborators.value = [...collaborators.value, collaborator];\n\n return collaborator;\n }\n\n function removeCollaborator(memberId: string): void {\n const newLockedBlocks = new Map(lockedBlocks.value);\n for (const [blockId, collaborator] of newLockedBlocks) {\n if (collaborator.id === memberId) {\n newLockedBlocks.delete(blockId);\n }\n }\n lockedBlocks.value = newLockedBlocks;\n\n collaborators.value = collaborators.value.filter((c) => c.id !== memberId);\n }\n\n function handleBlockLocked(data: { blockId: string; userId: string }): void {\n const collaborator = collaborators.value.find((c) => c.id === data.userId);\n if (!collaborator) {\n return;\n }\n\n collaborators.value = collaborators.value.map((c) =>\n c.id === data.userId ? { ...c, selectedBlockId: data.blockId } : c,\n );\n\n const newLockedBlocks = new Map(lockedBlocks.value);\n for (const [blockId, holder] of newLockedBlocks) {\n if (holder.id === data.userId) {\n newLockedBlocks.delete(blockId);\n }\n }\n\n newLockedBlocks.set(data.blockId, {\n ...collaborator,\n selectedBlockId: data.blockId,\n });\n lockedBlocks.value = newLockedBlocks;\n\n if (editor.state.selectedBlockId === data.blockId) {\n editor.selectBlock(null);\n }\n }\n\n function handleBlockUnlocked(data: { blockId: string }): void {\n const newLockedBlocks = new Map(lockedBlocks.value);\n const holder = newLockedBlocks.get(data.blockId);\n newLockedBlocks.delete(data.blockId);\n lockedBlocks.value = newLockedBlocks;\n\n if (holder) {\n collaborators.value = collaborators.value.map((c) =>\n c.id === holder.id ? { ...c, selectedBlockId: null } : c,\n );\n }\n }\n\n function handleRemoteOperation(payload: McpOperationPayload): void {\n isProcessingRemoteOperation = true;\n try {\n handleOperation(editor, payload);\n } finally {\n isProcessingRemoteOperation = false;\n }\n }\n\n function broadcastOperation(payload: McpOperationPayload): void {\n if (!channel.value || isProcessingRemoteOperation) {\n return;\n }\n\n channel.value.trigger(\"client-operation\", payload);\n }\n\n function broadcastBlockLocked(blockId: string): void {\n if (!channel.value) {\n return;\n }\n\n channel.value.trigger(\"client-block_locked\", {\n blockId,\n userId: myUserId.value,\n });\n }\n\n function broadcastBlockUnlocked(blockId: string): void {\n if (!channel.value) {\n return;\n }\n\n channel.value.trigger(\"client-block_unlocked\", { blockId });\n }\n\n watch(\n () => editor.state.selectedBlockId,\n (newBlockId, oldBlockId) => {\n if (isProcessingRemoteOperation) {\n return;\n }\n\n if (oldBlockId) {\n broadcastBlockUnlocked(oldBlockId);\n }\n\n if (newBlockId) {\n broadcastBlockLocked(newBlockId);\n }\n },\n );\n\n watch(channel, (newChannel, oldChannel) => {\n if (oldChannel) {\n unbindCollabEvents(oldChannel);\n }\n\n if (!newChannel) {\n collaborators.value = [];\n lockedBlocks.value = new Map();\n colorIndex = 0;\n return;\n }\n\n const members = (\n newChannel as PresenceChannel & {\n members: {\n each: (\n callback: (member: { id: string; info: PresenceMember }) => void,\n ) => void;\n };\n }\n ).members;\n if (members) {\n members.each((member: { id: string; info: PresenceMember }) => {\n addCollaborator(member.info);\n });\n }\n\n newChannel.bind(\n \"pusher:member_added\",\n (member: { id: string; info: PresenceMember }) => {\n const collaborator = addCollaborator(member.info);\n if (collaborator) {\n options.onCollaboratorJoined?.(collaborator);\n }\n },\n );\n\n newChannel.bind(\n \"pusher:member_removed\",\n (member: { id: string; info: PresenceMember }) => {\n const collaborator = collaborators.value.find(\n (c) => c.id === member.id,\n );\n removeCollaborator(member.id);\n if (collaborator) {\n options.onCollaboratorLeft?.(collaborator);\n }\n },\n );\n\n newChannel.bind(\n \"client-block_locked\",\n (data: { blockId: string; userId: string }) => {\n handleBlockLocked(data);\n const collaborator = collaborators.value.find(\n (c) => c.id === data.userId,\n );\n if (collaborator) {\n options.onBlockLocked?.({\n blockId: data.blockId,\n collaborator,\n });\n }\n },\n );\n\n newChannel.bind(\"client-block_unlocked\", (data: { blockId: string }) => {\n const holder = lockedBlocks.value.get(data.blockId);\n handleBlockUnlocked(data);\n if (holder) {\n options.onBlockUnlocked?.({\n blockId: data.blockId,\n collaborator: holder,\n });\n }\n });\n\n newChannel.bind(\"client-operation\", (payload: McpOperationPayload) => {\n handleRemoteOperation(payload);\n });\n\n newChannel.bind(\"mcp-operation\", (payload: McpOperationPayload) => {\n handleRemoteOperation(payload);\n });\n });\n\n // The watch above only unbinds when the channel ref *changes*. On scope\n // dispose (component unmount) the watch is auto-stopped but the currently\n // bound listeners stay attached forever — handlers fire on a dead component\n // and accumulate on remount. Tear them down explicitly.\n onScopeDispose(() => {\n if (channel.value) {\n unbindCollabEvents(channel.value);\n }\n });\n\n return {\n collaborators,\n lockedBlocks,\n _broadcastOperation: broadcastOperation,\n _isProcessingRemoteOperation: () => isProcessingRemoteOperation,\n };\n}\n","import type { McpOperationPayload } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"../editor\";\n\ninterface BroadcastTarget {\n _broadcastOperation: (payload: McpOperationPayload) => void;\n}\n\n/**\n * Wraps editor mutation methods to broadcast operations to collaboration\n * peers after each mutation executes. Mutates the editor object in place.\n *\n * Must be applied **before** the history interceptor so the call chain is:\n * history.record() → broadcast + original mutation.\n */\nexport function useCollaborationBroadcast(\n // The six members it actually replaces, rather than a whole editor. It is\n // handed the *cloud* editor while this annotation names the OSS one, so any\n // member either gains that the other lacks would break this call site for\n // reasons that have nothing to do with broadcasting.\n editor: Pick<\n UseEditorReturn,\n | \"addBlock\"\n | \"updateBlock\"\n | \"removeBlock\"\n | \"moveBlock\"\n | \"updateSettings\"\n | \"setContent\"\n >,\n collaboration: BroadcastTarget,\n): void {\n const originalAddBlock = editor.addBlock;\n const originalUpdateBlock = editor.updateBlock;\n const originalRemoveBlock = editor.removeBlock;\n const originalMoveBlock = editor.moveBlock;\n const originalUpdateSettings = editor.updateSettings;\n const originalSetContent = editor.setContent;\n\n editor.addBlock = (block, targetSectionId?, columnIndex?, index?) => {\n originalAddBlock(block, targetSectionId, columnIndex, index);\n collaboration._broadcastOperation({\n operation: \"add_block\",\n data: {\n block,\n section_id: targetSectionId,\n column_index: columnIndex,\n index,\n },\n timestamp: Date.now(),\n });\n };\n\n editor.updateBlock = (blockId, updates) => {\n originalUpdateBlock(blockId, updates);\n collaboration._broadcastOperation({\n operation: \"update_block\",\n data: { block_id: blockId, updates },\n timestamp: Date.now(),\n });\n };\n\n editor.removeBlock = (blockId) => {\n originalRemoveBlock(blockId);\n collaboration._broadcastOperation({\n operation: \"delete_block\",\n data: { block_id: blockId },\n timestamp: Date.now(),\n });\n };\n\n editor.moveBlock = (blockId, newIndex, targetSectionId?, columnIndex?) => {\n originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);\n collaboration._broadcastOperation({\n operation: \"move_block\",\n data: {\n block_id: blockId,\n index: newIndex,\n section_id: targetSectionId,\n column_index: columnIndex,\n },\n timestamp: Date.now(),\n });\n };\n\n editor.updateSettings = (updates) => {\n originalUpdateSettings(updates);\n collaboration._broadcastOperation({\n operation: \"update_settings\",\n data: { updates },\n timestamp: Date.now(),\n });\n };\n\n editor.setContent = (content, markDirty?) => {\n originalSetContent(content, markDirty);\n collaboration._broadcastOperation({\n operation: \"set_content\",\n data: { content },\n timestamp: Date.now(),\n });\n };\n}\n","import type { AuthManager } from \"./auth\";\nimport type { WebSocketConfig } from \"./websocket-client\";\nimport { WebSocketClient } from \"./websocket-client\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { ref, type Ref } from \"vue\";\n\nexport interface UseWebSocketOptions {\n authManager: AuthManager;\n onError?: (error: Error) => void;\n}\n\nexport interface UseWebSocketReturn {\n channel: Ref<PresenceChannel | null>;\n isConnected: Ref<boolean>;\n connect: (templateId: string, config: WebSocketConfig) => Promise<void>;\n disconnect: () => void;\n getSocketId: () => string | null;\n}\n\nexport function useWebSocket(options: UseWebSocketOptions): UseWebSocketReturn {\n const { authManager, onError } = options;\n\n const channel = ref<PresenceChannel | null>(\n null,\n ) as Ref<PresenceChannel | null>;\n const isConnected = ref(false);\n\n let wsClient: WebSocketClient | null = null;\n let channelName: string | null = null;\n\n async function connect(\n templateId: string,\n config: WebSocketConfig,\n ): Promise<void> {\n if (wsClient) {\n return;\n }\n\n wsClient = new WebSocketClient({\n authManager,\n config,\n onError,\n });\n\n await wsClient.connect();\n\n channelName = `presence-template.${templateId}`;\n const presenceChannel = wsClient.subscribePresence(channelName);\n\n presenceChannel.bind(\"pusher:subscription_succeeded\", () => {\n isConnected.value = true;\n channel.value = presenceChannel;\n });\n\n presenceChannel.bind(\"pusher:subscription_error\", (error: unknown) => {\n isConnected.value = false;\n channel.value = null;\n onError?.(\n error instanceof Error\n ? error\n : new Error(\"Failed to subscribe to template channel\"),\n );\n });\n }\n\n function disconnect(): void {\n if (channelName && wsClient) {\n wsClient.unsubscribe(channelName);\n }\n\n wsClient?.disconnect();\n wsClient = null;\n channelName = null;\n channel.value = null;\n isConnected.value = false;\n }\n\n function getSocketId(): string | null {\n return wsClient?.getSocketId() ?? null;\n }\n\n return {\n channel,\n isConnected,\n connect,\n disconnect,\n getSocketId,\n };\n}\n","import type {\n SavedBlock,\n SavedBlockInput,\n SavedBlockPatch,\n SavedBlocksListParams,\n SavedBlocksProvider,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * Cloud-backed {@link SavedBlocksProvider} — the Templatical Cloud adapter for\n * the same contract consumers implement themselves.\n *\n * Auth (JWT via {@link AuthManager}) and project/tenant scoping live entirely\n * on this side of the seam: the shared `useSavedBlocks` composable and the\n * editor UI never see them. Plan gating is applied by the caller before this\n * provider is constructed.\n *\n * Wraps the `ApiClient` saved-module endpoints. The REST contract keeps the\n * `savedModules.*` route names even though the type is `SavedBlock`, so the\n * names deliberately differ from the contract's.\n *\n * Rows pass straight through, unmapped: the Cloud endpoints speak the same\n * `SavedBlock` shape the contract defines, so this adapter only supplies auth\n * and scoping. That means the payload's field names track the contract — the\n * backend serialises `category`, `canUpdate`, `canDelete`, `createdAt` and\n * `updatedAt`. Until it does, those fields simply arrive absent, which degrades\n * to \"no timestamp label, every action allowed\" rather than breaking.\n */\nexport function createCloudSavedBlocksProvider(\n authManager: AuthManager,\n): SavedBlocksProvider {\n const api = new ApiClient(authManager);\n\n return {\n list(params?: SavedBlocksListParams): Promise<SavedBlock[]> {\n return api.listModules(params?.search, params?.category);\n },\n // All three enabled: Cloud gates saved blocks by plan entitlement above\n // this seam, and row-level permissions arrive as `canUpdate`/`canDelete`\n // on the entries themselves.\n create(input: SavedBlockInput): Promise<SavedBlock> {\n return api.createModule(input);\n },\n update(id: string, patch: SavedBlockPatch): Promise<SavedBlock> {\n return api.updateModule(id, patch);\n },\n delete(id: string): Promise<void> {\n return api.deleteModule(id);\n },\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplatePatch,\n TemplatesProvider,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * How often a save may also record an automatic version. Cloud's storage, Cloud's\n * number — an editor-side default would be guessing on someone else's behalf.\n */\nconst AUTO_VERSION_INTERVAL_MS = 60_000;\n\n/**\n * Cloud-backed {@link TemplatesProvider} — the Templatical Cloud adapter for the\n * same save/load contract consumers implement themselves.\n *\n * Auth (JWT via {@link AuthManager}) and project/tenant scoping live entirely on\n * this side of the seam; the editor's header, Cmd+S, autosave and\n * unsaved-changes guard never see them.\n *\n * All three methods are enabled: Cloud's template storage is what the plan pays\n * for, so there is no tier in which a Cloud session can load but not save. A\n * consumer who wants read-only supplies their own provider with `save: false`.\n *\n * **This is also where automatic versions are recorded.** The editor never\n * creates one on its own — whoever implements `save` decides whether a save also\n * records a version, which keeps throttling and retention with the side that\n * pays for the storage. Cloud throttles to one automatic version per\n * {@link AUTO_VERSION_INTERVAL_MS}, so an autosave firing every few seconds does\n * not turn history into a keystroke log. Keep this on the provider side: an\n * editor-side timer would put Cloud's retention policy in the editor.\n *\n * Note `initCloud()` **rejects** a consumer-supplied templates provider, unlike\n * `savedBlocks` and `testEmail`. Those are inert — nothing keys off them. The\n * template id is the join key for collaboration, version history, comments, AI\n * rewrite, scoring and the server-side export, so an id Cloud never issued would\n * degrade all six silently.\n */\nexport function createCloudTemplatesProvider(\n authManager: AuthManager,\n): TemplatesProvider {\n const api = new ApiClient(authManager);\n\n // `0` rather than `Date.now()`: the first save of a session always records a\n // version, so a short session that saves once still has history.\n let lastAutoVersionAt = 0;\n\n /**\n * Fire-and-forget: a version is a convenience on top of a save that already\n * succeeded, so a failing history endpoint must not turn a successful save\n * into a failed one. The clock advances before the request, so a slow endpoint\n * can't let a burst of saves queue several at once.\n *\n * Logged rather than swallowed: history quietly not filling up is exactly the\n * kind of failure nobody notices until they need a version that was never\n * recorded.\n */\n function maybeRecordVersion(\n templateId: string,\n content: TemplateContent,\n ): void {\n const now = Date.now();\n if (now - lastAutoVersionAt < AUTO_VERSION_INTERVAL_MS) return;\n lastAutoVersionAt = now;\n void api.createVersion(templateId, content).catch((error: unknown) => {\n console.warn(\n \"[Templatical] Automatic version not recorded — the save itself succeeded:\",\n error,\n );\n });\n }\n\n return {\n load(id: string): Promise<Template> {\n return api.getTemplate(id);\n },\n create(input: {\n name?: string;\n content: TemplateContent;\n }): Promise<Template> {\n return api.createTemplate(input.content, input.name);\n },\n async save(id: string, patch: TemplatePatch): Promise<Template> {\n // Forwarded verbatim: the endpoint speaks the same patch shape, so this\n // adapter only supplies auth and scoping. `name` is inert until the backend\n // stores the column, then live with no SDK change.\n const template = await api.updateTemplate(id, patch);\n // Only content changes are worth a version — a rename patch carries no\n // content, and versioning one would record a duplicate of the last state.\n if (patch.content) maybeRecordVersion(id, patch.content);\n return template;\n },\n };\n}\n","import type { CustomFont, ExportResult, FontsConfig } from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/** The fonts half of an export request, as Cloud's endpoint expects it. */\nexport interface ExportFontsPayload {\n customFonts: CustomFont[];\n defaultFallback: string;\n}\n\nexport interface UseExportOptions {\n authManager: AuthManager;\n}\n\nexport interface UseExportReturn {\n exportHtml: (\n templateId: string,\n fonts: ExportFontsPayload,\n ) => Promise<ExportResult>;\n getMjmlSource: (\n templateId: string,\n fonts: ExportFontsPayload,\n ) => Promise<string>;\n}\n\n/**\n * Flatten a {@link FontsConfig} into the export payload.\n *\n * Unconditional: gating fonts by entitlement meters no resource Cloud buys, and\n * would only make the paid tier render fewer fonts than the free editor.\n */\nexport function resolveExportFonts(\n fonts: FontsConfig | undefined,\n): ExportFontsPayload {\n return {\n customFonts: fonts?.customFonts ?? [],\n defaultFallback: fonts?.defaultFallback ?? \"Arial, sans-serif\",\n };\n}\n\n/**\n * Cloud's server-side export endpoint, as a plain API wrapper.\n *\n * Both calls render from the **stored** template, so callers save first when the\n * canvas may have moved on — see `createCloudRenderProvider`, which is where that\n * policy lives.\n */\nexport function useExport(options: UseExportOptions): UseExportReturn {\n const api = new ApiClient(options.authManager);\n\n async function exportHtml(\n templateId: string,\n fonts: ExportFontsPayload,\n ): Promise<ExportResult> {\n const result = await api.exportTemplate(templateId, fonts);\n\n return {\n html: result.html,\n mjml: result.mjml,\n };\n }\n\n async function getMjmlSource(\n templateId: string,\n fonts: ExportFontsPayload,\n ): Promise<string> {\n const result = await api.exportTemplate(templateId, fonts);\n return result.mjml;\n }\n\n return {\n exportHtml,\n getMjmlSource,\n };\n}\n","import type {\n RenderPayload,\n RenderProvider,\n Template,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { useExport, type ExportFontsPayload } from \"./export\";\n\nexport interface CreateCloudRenderProviderOptions {\n authManager: AuthManager;\n /** The loaded template's id, or `null` before one exists. */\n getTemplateId: () => string | null;\n /** Persist the canvas so the endpoint renders what the user is looking at. */\n save: () => Promise<Template>;\n}\n\n/**\n * Templatical Cloud's renderer, shaped as a {@link RenderProvider} so it plugs\n * into the same editor seam a consumer's own backend would.\n *\n * **Why Cloud renders server-side at all**, rather than running the bundled\n * renderer like an OSS consumer: its output is a deliberate *superset* that a\n * browser cannot produce. A countdown block resolves to a URL serving a live,\n * on-demand animated GIF; a video block gets a composited play button. Both are\n * injected into the published renderer through `blockRenderers` in Cloud's Node\n * sidecar, so the delta is two functions and parity on the other twelve block\n * types holds by construction.\n *\n * Two consequences of the endpoint rendering the **stored** template:\n *\n * - **Every render saves first.** The same trade the test-email adapter makes, for\n * the same reason: exporting a stale version of what is on screen is worse than\n * a write the caller did not ask for. It also means `toMjml()` needs a template\n * to exist — a Cloud session that never created one gets a clear rejection\n * rather than an export of nothing.\n * - **`payload.content` is ignored**, along with the custom-block `renderedHtml`\n * the editor pre-rendered into it. Cloud's `save()` persists that same content\n * immediately before, so the server reads it from storage rather than trusting an\n * echo. `payload.fonts` *is* read, because it is the only place the editor's\n * effective font set is expressed. Don't \"fix\" the content branch.\n *\n * `compileMjml` is deliberately absent: `toMjml` and `toHtml` are both whole-\n * pipeline calls here, so there is no MJML the editor would hand back for\n * compiling.\n */\nexport function createCloudRenderProvider(\n options: CreateCloudRenderProviderOptions,\n): RenderProvider {\n const exporter = useExport({ authManager: options.authManager });\n\n // Every plan renders the fonts the canvas is using: an entitlement on editor\n // capability that OSS gives away free would make the paid tier render *less*\n // than the free one.\n function exportFonts(payload: RenderPayload): ExportFontsPayload {\n return {\n customFonts: payload.fonts?.customFonts ?? [],\n defaultFallback: payload.fonts?.defaultFallback ?? \"Arial, sans-serif\",\n };\n }\n\n async function saveThenResolveId(): Promise<string> {\n if (options.getTemplateId() === null) {\n throw new Error(\n \"[Templatical] Cloud renders from the saved template, so one must exist first. Call create() or load() before toMjml() / toHtml().\",\n );\n }\n\n // Save before exporting: the endpoint reads storage, so skipping this would\n // render whatever was persisted last rather than what is on the canvas.\n const template = await options.save();\n return template.id;\n }\n\n return {\n async toMjml(payload: RenderPayload): Promise<string> {\n const fonts = exportFonts(payload);\n const templateId = await saveThenResolveId();\n return exporter.getMjmlSource(templateId, fonts);\n },\n\n async toHtml(payload: RenderPayload): Promise<string> {\n const fonts = exportFonts(payload);\n const templateId = await saveThenResolveId();\n const { html } = await exporter.exportHtml(templateId, fonts);\n return html;\n },\n };\n}\n","import type {\n Template,\n TemplateContent,\n TemplateVersion,\n TemplateVersionResponse,\n VersionHistoryProvider,\n VersionHistoryListResult,\n} from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\n\n/**\n * Cloud's wire shape → the contract shape. The only real work this adapter does:\n * snake_case to camelCase, and `is_autosave` to `isAutomatic`.\n *\n * `content` is carried through deliberately — Cloud's index endpoint already\n * returns it, so every listed version arrives hydrated and the editor never\n * calls `get` while scrubbing. See {@link TemplateVersion.content}: that is the\n * hint working as intended, not a shortcut. `get` is implemented anyway, because\n * it is the contract's operation and Cloud is one implementation of it like any\n * other.\n */\nfunction toVersion(record: TemplateVersionResponse): TemplateVersion {\n return {\n id: record.id,\n createdAt: record.created_at,\n isAutomatic: record.is_autosave,\n content: record.content,\n };\n}\n\n/**\n * Cloud-backed {@link VersionHistoryProvider} — the Templatical Cloud adapter for\n * the same contract consumers implement themselves.\n *\n * Both mutations are enabled: version storage is what the plan pays for, so\n * there is no Cloud tier that can list history but not restore it. A consumer\n * who wants read-only history supplies their own provider with `restore: false`.\n *\n * **Automatic versions are not created here.** They are recorded by\n * `createCloudTemplatesProvider`'s `save`, because the contract puts that\n * decision on whoever implements `save` — the side that knows the storage cost.\n * `create` here is for a version a person asked for.\n */\nexport function createCloudVersionHistoryProvider(\n authManager: AuthManager,\n): VersionHistoryProvider {\n const api = new ApiClient(authManager);\n\n return {\n async list(templateId: string): Promise<VersionHistoryListResult> {\n const records = await api.getVersions(templateId);\n // No `nextCursor`: Cloud returns the template's whole history in one\n // response — with each entry's content — so there is never a further page.\n return { versions: records.map(toVersion) };\n },\n async get(templateId: string, versionId: string): Promise<TemplateContent> {\n const record = await api.getVersion(templateId, versionId);\n return record.content;\n },\n async create(\n templateId: string,\n content: TemplateContent,\n meta?: { label?: string },\n ): Promise<TemplateVersion> {\n return toVersion(\n await api.createVersion(templateId, content, meta?.label),\n );\n },\n restore(templateId: string, versionId: string): Promise<Template> {\n // Cloud keeps an atomic, audited server endpoint. It is append-only like\n // every other implementation of this method: restoring records a new entry\n // rather than rewriting the one being restored.\n return api.restoreVersion(templateId, versionId);\n },\n };\n}\n","import type { TestEmailConfig } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport type { ComputedRef, Ref } from \"vue\";\nimport { computed, ref, watch } from \"vue\";\n\n/**\n * Cloud's test-email *configuration* — whether the project has the feature, who\n * may be sent to, and the signature that lets the backend verify that list.\n *\n * Sending itself lives in `createCloudTestEmailProvider`, which plugs into the\n * shared `useTestEmailFeature` seam in `@templatical/editor` alongside a\n * consumer's own sender. Keeping the send body in exactly one place is the point:\n * both editors then drive identical UI, state and error handling, so the sending\n * spinner, the inline error and the success confirmation can't drift apart.\n */\nexport interface UseTestEmailOptions {\n authManager: AuthManager;\n /** Gates reading the config off the auth manager until the JWT has resolved. */\n isAuthReady?: Ref<boolean>;\n}\n\nexport interface UseTestEmailReturn {\n /** Whether the project's token carries a test-email config at all. */\n isEnabled: ComputedRef<boolean>;\n /**\n * Addresses the project permits. Empty until auth resolves — read it\n * reactively, never snapshot it, or it stays empty for the session.\n */\n allowedEmails: ComputedRef<string[]>;\n /** The signature accompanying {@link allowedEmails}, or `null` when disabled. */\n getSignature: () => string | null;\n}\n\nexport function useTestEmail(options: UseTestEmailOptions): UseTestEmailReturn {\n const { authManager, isAuthReady } = options;\n\n const testEmailConfig = ref<TestEmailConfig | null>(null);\n\n if (isAuthReady) {\n watch(\n isAuthReady,\n (ready) => {\n if (ready) {\n testEmailConfig.value = authManager.testEmailConfig;\n }\n },\n { immediate: true },\n );\n }\n\n const isEnabled = computed<boolean>(() => testEmailConfig.value !== null);\n\n const allowedEmails = computed<string[]>(\n () => testEmailConfig.value?.allowedEmails ?? [],\n );\n\n function getSignature(): string | null {\n return testEmailConfig.value?.signature ?? null;\n }\n\n return { isEnabled, allowedEmails, getSignature };\n}\n","import type {\n ExportResult,\n Template,\n TestEmailProvider,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { ApiClient } from \"./api\";\nimport type { ComputedRef } from \"vue\";\n\nexport interface CreateCloudTestEmailProviderOptions {\n authManager: AuthManager;\n getTemplateId: () => string | null;\n save: () => Promise<Template>;\n exportHtml: (templateId: string) => Promise<ExportResult>;\n /**\n * The project's allowed recipients, from the signed JWT.\n *\n * Taken as a `ComputedRef` rather than an array because it only becomes\n * populated once auth resolves — see the getter note on the returned provider.\n */\n allowedEmails: ComputedRef<string[]>;\n /** The signature that lets the backend verify {@link allowedEmails}. */\n getSignature: () => string | null;\n onBeforeTestEmail?: (html: string) => string | Promise<string>;\n}\n\n/**\n * Templatical Cloud's test-email sender, shaped as a {@link TestEmailProvider}\n * so it plugs into the same editor seam a consumer's own sender would.\n *\n * Two things about it are deliberately unlike a BYO provider:\n *\n * - **It ignores `payload.content` and `payload.allowedRecipients`.** Cloud saves\n * the template, has its backend render the HTML, and derives the allowlist plus\n * its signature from the auth manager. Trusting the echoed payload would swap a\n * server-signed list for an unsigned browser value. The contract is \"send a test\n * of the current template\" — how an adapter obtains the bytes is its business,\n * so this is honest rather than a smell. Don't \"fix\" it.\n * - **It never sets `includeMjml`.** Cloud renders server-side, so the editor\n * should not spend a render pass producing MJML nobody reads.\n */\nexport function createCloudTestEmailProvider(\n options: CreateCloudTestEmailProviderOptions,\n): TestEmailProvider {\n const api = new ApiClient(options.authManager);\n\n return {\n /**\n * A getter, not a snapshot: the allowlist arrives with the JWT, so it starts\n * empty and fills once auth is ready. The editor reads this property inside a\n * computed, which is what makes the trigger appear at that point — reading it\n * once at construction would pin it to `[]` and, because an empty allowlist\n * means \"nobody may be sent to\", hide the button forever.\n */\n get allowedRecipients(): string[] {\n return options.allowedEmails.value;\n },\n\n async send({ recipient }): Promise<void> {\n const signature = options.getSignature();\n if (signature === null) {\n throw new Error(\"Test email is not enabled for this project\");\n }\n\n const templateId = options.getTemplateId();\n if (templateId === null) {\n throw new Error(\"Template must be saved before sending a test email\");\n }\n\n // Save first: the backend renders from the stored template, so sending\n // without this would mail a stale version of what's on screen.\n await options.save();\n\n let { html } = await options.exportHtml(templateId);\n if (options.onBeforeTestEmail) {\n html = await options.onBeforeTestEmail(html);\n }\n\n await api.sendTestEmail(templateId, {\n recipient,\n html,\n allowed_emails: options.allowedEmails.value,\n signature,\n });\n },\n };\n}\n","import type { PlanConfig, PlanFeatures } from \"@templatical/types\";\nimport { ApiClient } from \"./api\";\nimport type { AuthManager } from \"./auth\";\nimport { computed, ref, type ComputedRef, type Ref } from \"vue\";\n\nexport interface UsePlanConfigOptions {\n authManager: AuthManager;\n onError?: (error: Error) => void;\n}\n\nexport interface UsePlanConfigReturn {\n config: Ref<PlanConfig | null>;\n isLoading: Ref<boolean>;\n hasFeature: (feature: keyof PlanFeatures) => boolean;\n features: ComputedRef<PlanFeatures | null>;\n fetchConfig: () => Promise<void>;\n}\n\nexport function usePlanConfig(\n options: UsePlanConfigOptions,\n): UsePlanConfigReturn {\n const { authManager, onError } = options;\n\n const config = ref<PlanConfig | null>(null);\n const isLoading = ref(false);\n\n const apiClient = new ApiClient(authManager);\n\n const features = computed(() => config.value?.features ?? null);\n\n function hasFeature(feature: keyof PlanFeatures): boolean {\n return config.value?.features[feature] ?? false;\n }\n\n async function fetchConfig(): Promise<void> {\n if (isLoading.value) {\n return;\n }\n\n isLoading.value = true;\n\n try {\n config.value = await apiClient.fetchConfig();\n } catch (error) {\n onError?.(\n error instanceof Error\n ? error\n : new Error(\"Failed to fetch config\", { cause: error }),\n );\n } finally {\n isLoading.value = false;\n }\n }\n\n return {\n config,\n isLoading,\n hasFeature,\n features,\n fetchConfig,\n };\n}\n","import type { HealthCheckResult } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES } from \"./url-builder\";\n\ninterface HealthCheckOptions {\n authManager?: AuthManager;\n baseUrl?: string;\n}\n\nconst WS_HANDSHAKE_TIMEOUT = 5000;\n\nfunction resolveHealthUrl(options: HealthCheckOptions): string {\n if (options.authManager) {\n return options.authManager.resolveUrl(API_ROUTES.health);\n }\n\n const base = (options.baseUrl ?? \"https://templatical.com\").replace(\n /\\/$/,\n \"\",\n );\n return `${base}${API_ROUTES.health}`;\n}\n\nasync function checkApiAndAuth(\n url: string,\n authManager?: AuthManager,\n): Promise<{\n api: { ok: boolean; latency: number };\n auth: { ok: boolean; error?: string };\n wsConfig?: { host: string; port: number; app_key: string };\n}> {\n const start = performance.now();\n\n try {\n const response = authManager\n ? await authManager.authenticatedFetch(API_ROUTES.health, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n })\n : await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n const latency = Math.round(performance.now() - start);\n\n if (response.status === 401) {\n return {\n api: { ok: true, latency },\n auth: { ok: false, error: \"HTTP 401\" },\n };\n }\n\n if (!response.ok) {\n return {\n api: { ok: false, latency },\n auth: {\n ok: !authManager,\n error: authManager ? `HTTP ${response.status}` : undefined,\n },\n };\n }\n\n const data = await response.json();\n\n return {\n api: { ok: data.status === \"ok\", latency },\n auth: { ok: true },\n wsConfig: data.websocket,\n };\n } catch (error) {\n const latency = Math.round(performance.now() - start);\n\n return {\n api: { ok: false, latency },\n auth: {\n ok: !authManager,\n error: authManager\n ? error instanceof Error\n ? error.message\n : \"Authentication check failed\"\n : undefined,\n },\n };\n }\n}\n\nasync function checkWebSocket(wsConfig?: {\n host: string;\n port: number;\n app_key: string;\n}): Promise<{ ok: boolean; error?: string }> {\n if (!wsConfig?.host || !wsConfig?.app_key) {\n return { ok: false, error: \"WebSocket configuration not available\" };\n }\n\n if (typeof WebSocket === \"undefined\") {\n return {\n ok: false,\n error: \"WebSocket not supported in this environment\",\n };\n }\n\n const protocol = wsConfig.port === 443 ? \"wss\" : \"ws\";\n const url = `${protocol}://${wsConfig.host}:${wsConfig.port}/app/${wsConfig.app_key}?protocol=7&client=js&version=8.4.0-rc2&flash=false`;\n\n return new Promise((resolve) => {\n let ws: WebSocket | null = null;\n\n const timeout = setTimeout(() => {\n ws?.close();\n resolve({ ok: false, error: \"WebSocket connection timed out\" });\n }, WS_HANDSHAKE_TIMEOUT);\n\n try {\n ws = new WebSocket(url);\n } catch (error) {\n // Some browsers throw synchronously from `new WebSocket(...)`\n // (e.g. SecurityError on mixed content, InvalidAccessError on\n // malformed URL). Without this catch the queued timeout would\n // later access `ws` in TDZ and the outer promise would hang.\n clearTimeout(timeout);\n resolve({\n ok: false,\n error:\n error instanceof Error\n ? error.message\n : \"WebSocket connection failed\",\n });\n return;\n }\n\n ws.onopen = () => {\n clearTimeout(timeout);\n ws?.close();\n resolve({ ok: true });\n };\n\n ws.onerror = () => {\n clearTimeout(timeout);\n resolve({ ok: false, error: \"WebSocket connection failed\" });\n };\n });\n}\n\nexport async function performHealthCheck(\n options: HealthCheckOptions = {},\n): Promise<HealthCheckResult> {\n const healthUrl = resolveHealthUrl(options);\n\n const result = await checkApiAndAuth(healthUrl, options.authManager);\n const wsResult = await checkWebSocket(result.wsConfig);\n\n return {\n api: result.api,\n websocket: wsResult,\n auth: result.auth,\n overall: result.api.ok && result.auth.ok,\n };\n}\n","import type { UseEditorReturn } from \"../editor\";\nimport type { McpOperationPayload } from \"@templatical/types\";\nimport { handleOperation } from \"./mcp-operation-handler\";\nimport type { PresenceChannel } from \"pusher-js\";\nimport { watch, type Ref } from \"vue\";\n\nexport interface UseMcpListenerOptions {\n editor: UseEditorReturn;\n channel: Ref<PresenceChannel | null>;\n onOperation?: (payload: McpOperationPayload) => void;\n}\n\nexport function useMcpListener(options: UseMcpListenerOptions): void {\n const { editor, channel, onOperation } = options;\n\n watch(channel, (newChannel, oldChannel) => {\n if (oldChannel) {\n oldChannel.unbind(\"mcp-operation\");\n }\n\n if (newChannel) {\n newChannel.bind(\"mcp-operation\", (payload: McpOperationPayload) => {\n handleOperation(editor, payload);\n onOperation?.(payload);\n });\n }\n });\n}\n"],"mappings":"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"}
|