@templatical/editor 0.41.0 → 0.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiChatSidebar-a8TPBAl2.js → AiChatSidebar-DOCvzGBe.js} +8 -8
- package/dist/{AiFeatureMenu-CroDMqRG.js → AiFeatureMenu-CfijAx4G.js} +4 -4
- package/dist/{BlockIssueBadge-D8KsGWY6.js → BlockIssueBadge-BJ3A6mqh.js} +2 -2
- package/dist/{BlockPreviewCanvas-Z9SZIgfA.js → BlockPreviewCanvas-DggfBaoJ.js} +33 -33
- package/dist/{CloudHeaderExtras-CVqHeqc6.js → CloudHeaderExtras-dFbdqBr5.js} +2 -2
- package/dist/{CloudPanels-CmEpQUJB.js → CloudPanels-DmMxhzzZ.js} +4 -4
- package/dist/{CollaboratorBar-AIQC9fEF.js → CollaboratorBar-XXk1tOC5.js} +2 -2
- package/dist/{CommentsPanel-9k1q-79y.js → CommentsPanel-CGEhbjx0.js} +1 -1
- package/dist/{CommentsSidebar-Bc7nORmD.js → CommentsSidebar-CUpst9ou.js} +10 -10
- package/dist/{CountdownToolbar-DkC-qSgq.js → CountdownToolbar-B1a_UIuB.js} +3 -3
- package/dist/{DesignReferenceSidebar-OoiYJAKS.js → DesignReferenceSidebar-DkAmsBgM.js} +6 -6
- package/dist/{IssuesPanel-Dnns3O9z.js → IssuesPanel-lTpsCV4a.js} +5 -5
- package/dist/{LogicTagInsertButton-BimKobaG.js → LogicTagInsertButton-B5YymR4U.js} +243 -219
- package/dist/{MediaEditModal-C-InB-H2.js → MediaEditModal-B1X8qtJb.js} +1 -1
- package/dist/{MediaPanels-CTosUpeo.js → MediaPanels-DO30L1KS.js} +1 -1
- package/dist/{MergeTagInput-DqpVzhy7.js → MergeTagInput-B0M_-X0K.js} +1 -1
- package/dist/{MergeTagModeToggle-BkIiAgpQ.js → MergeTagModeToggle-B-8W1tK7.js} +4 -4
- package/dist/{ParagraphEditor-DaZ03d1U.js → ParagraphEditor-BhhpXgoO.js} +35 -35
- package/dist/{RestoreVersionDialog-DHb1mPoF.js → RestoreVersionDialog-DE8aqmdi.js} +3 -3
- package/dist/{RichTextEditorContent-AEtfKvLz.js → RichTextEditorContent-AaLAArA0.js} +5 -5
- package/dist/{SaveBlockDialog-hGwLDWfE.js → SaveBlockDialog-BJ-hEwLv.js} +8 -8
- package/dist/{SavedBlocksBrowserModal-C0lYVBFi.js → SavedBlocksBrowserModal-nLI8aAQ5.js} +9 -9
- package/dist/{SavedBlocksPanels-CaTbeVDv.js → SavedBlocksPanels-RnzUMUJI.js} +1 -1
- package/dist/{SavedBlocksPickBar-DR2afid2.js → SavedBlocksPickBar-D3QA3Tgy.js} +2 -2
- package/dist/{TemplateScoringPanel-Cs0TGtRF.js → TemplateScoringPanel-C6ellAcs.js} +11 -11
- package/dist/{TemplateSettings-BD1WNP4C.js → TemplateSettings-C_0qK-zs.js} +6 -6
- package/dist/{TestEmailModal-DqoosG1u.js → TestEmailModal-CnyxLhWJ.js} +7 -7
- package/dist/{TestEmailPanel-DpozqyHF.js → TestEmailPanel-BRisIL4G.js} +1 -1
- package/dist/{TitleEditor-BCBdZLt_.js → TitleEditor-lPITPzw6.js} +12 -12
- package/dist/{ToggleSwitch-CJfQ8u57.js → ToggleSwitch-CtZ6adlX.js} +1 -1
- package/dist/{Toolbar-C7SjilzH.js → Toolbar-B6dG5_IP.js} +847 -554
- package/dist/{TplModal-CVO-2KrU.js → TplModal-BiAf7tWe.js} +2 -2
- package/dist/{VersionHistoryMenu-b0A8mS2_.js → VersionHistoryMenu-DdspCRSA.js} +5 -5
- package/dist/{VersionHistoryPanels-BzF7dmwa.js → VersionHistoryPanels-C589Jm_B.js} +1 -1
- package/dist/{VersionPreviewBanner-xB9r83r0.js → VersionPreviewBanner-XUuLe36h.js} +1 -1
- package/dist/{WrapperBlock-BDjOn3fY.js → WrapperBlock-CVQ4t1tY.js} +4 -4
- package/dist/{blockTypeIcons-B0Bq1WT1.js → blockTypeIcons-Sg8SY1NQ.js} +2 -2
- package/dist/bundle-stats.json +6 -6
- package/dist/{cdn/chunks/ca-Ckgw9yd0.js → ca--zGWn9AU.js} +14 -2
- package/dist/cdn/chunks/{AiChatSidebar-mKImUXi6.js → AiChatSidebar-DiG-EQ-E.js} +8 -8
- package/dist/cdn/chunks/AiChatSidebar-DiG-EQ-E.js.map +1 -0
- package/dist/cdn/chunks/{AiFeatureMenu-Cx3qdStQ.js → AiFeatureMenu-BXSPgOzA.js} +5 -5
- package/dist/cdn/chunks/AiFeatureMenu-BXSPgOzA.js.map +1 -0
- package/dist/cdn/chunks/{BlockIssueBadge-DS1pUf5g.js → BlockIssueBadge-ChQ0mPf5.js} +2 -2
- package/dist/cdn/chunks/BlockIssueBadge-ChQ0mPf5.js.map +1 -0
- package/dist/cdn/chunks/{BlockPreviewCanvas-D31TNib3.js → BlockPreviewCanvas-CMmv1FTA.js} +28 -28
- package/dist/cdn/chunks/BlockPreviewCanvas-CMmv1FTA.js.map +1 -0
- package/dist/cdn/chunks/{CloudHeaderExtras-0zdlKiq7.js → CloudHeaderExtras-yvFz2_e-.js} +3 -3
- package/dist/cdn/chunks/CloudHeaderExtras-yvFz2_e-.js.map +1 -0
- package/dist/cdn/chunks/{CloudPanels-DPh71yOO.js → CloudPanels-DpKck9Na.js} +4 -4
- package/dist/cdn/chunks/CloudPanels-DpKck9Na.js.map +1 -0
- package/dist/cdn/chunks/{CollaboratorBar-lCZU8qkg.js → CollaboratorBar-Clm1ZVXQ.js} +3 -3
- package/dist/cdn/chunks/CollaboratorBar-Clm1ZVXQ.js.map +1 -0
- package/dist/cdn/chunks/{CommentsPanel-BtAGZfF6.js → CommentsPanel-CjhxGMjL.js} +2 -2
- package/dist/cdn/chunks/CommentsPanel-CjhxGMjL.js.map +1 -0
- package/dist/cdn/chunks/{CommentsSidebar-Ws07MXYj.js → CommentsSidebar-D9bqMnlt.js} +16 -16
- package/dist/cdn/chunks/CommentsSidebar-D9bqMnlt.js.map +1 -0
- package/dist/cdn/chunks/CountdownBlock-sVDjKkRw.js.map +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-Bu93WiS8.js → CountdownToolbar-CIRx5w1U.js} +4 -4
- package/dist/cdn/chunks/CountdownToolbar-CIRx5w1U.js.map +1 -0
- package/dist/cdn/chunks/{DesignReferenceSidebar-BlSdh5B7.js → DesignReferenceSidebar-C1-SbE_R.js} +9 -9
- package/dist/cdn/chunks/DesignReferenceSidebar-C1-SbE_R.js.map +1 -0
- package/dist/cdn/chunks/{IssuesPanel-CKkv9z9g.js → IssuesPanel-qKY6UDd-.js} +8 -8
- package/dist/cdn/chunks/IssuesPanel-qKY6UDd-.js.map +1 -0
- package/dist/cdn/chunks/LoadingTrack-_7DH_5aw.js.map +1 -1
- package/dist/cdn/chunks/{LogicTagInsertButton-CHc1yZ9v.js → LogicTagInsertButton-CItDS_U1.js} +250 -226
- package/dist/cdn/chunks/LogicTagInsertButton-CItDS_U1.js.map +1 -0
- package/dist/cdn/chunks/{MediaEditModal-Cw9MhjVz.js → MediaEditModal-CzUtXkpk.js} +2 -2
- package/dist/cdn/chunks/MediaEditModal-CzUtXkpk.js.map +1 -0
- package/dist/cdn/chunks/{MediaPanels-FD_aX3H5.js → MediaPanels-DOLsh7WC.js} +2 -2
- package/dist/cdn/chunks/MediaPanels-DOLsh7WC.js.map +1 -0
- package/dist/cdn/chunks/{MergeTagInput--qTYfCWk.js → MergeTagInput-B33u3nxj.js} +2 -2
- package/dist/cdn/chunks/MergeTagInput-B33u3nxj.js.map +1 -0
- package/dist/cdn/chunks/{MergeTagModeToggle-CdWIGo3Q.js → MergeTagModeToggle-Bz1CH3NT.js} +10 -10
- package/dist/cdn/chunks/MergeTagModeToggle-Bz1CH3NT.js.map +1 -0
- package/dist/cdn/chunks/{ParagraphEditor-w2qEZTkM.js → ParagraphEditor-J74SIn-m.js} +51 -51
- package/dist/cdn/chunks/ParagraphEditor-J74SIn-m.js.map +1 -0
- package/dist/cdn/chunks/{RestoreVersionDialog-cnWVN3nX.js → RestoreVersionDialog-CFE7jqDB.js} +3 -3
- package/dist/cdn/chunks/RestoreVersionDialog-CFE7jqDB.js.map +1 -0
- package/dist/cdn/chunks/{RichTextEditorContent-Fzuu9pLP.js → RichTextEditorContent-dOg1xFqv.js} +5 -5
- package/dist/cdn/chunks/RichTextEditorContent-dOg1xFqv.js.map +1 -0
- package/dist/cdn/chunks/{SaveBlockDialog-jxQtKnVs.js → SaveBlockDialog-DZOmDCC8.js} +38 -38
- package/dist/cdn/chunks/SaveBlockDialog-DZOmDCC8.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-DpXWf4Pi.js → SavedBlocksBrowserModal-B43H5zKA.js} +10 -10
- package/dist/cdn/chunks/SavedBlocksBrowserModal-B43H5zKA.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksPanels-B-ASmf1P.js → SavedBlocksPanels-uTSb2xO_.js} +2 -2
- package/dist/cdn/chunks/SavedBlocksPanels-uTSb2xO_.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksPickBar-CEFIQ8Nd.js → SavedBlocksPickBar-BUvmGqHK.js} +3 -3
- package/dist/cdn/chunks/SavedBlocksPickBar-BUvmGqHK.js.map +1 -0
- package/dist/cdn/chunks/{TemplateScoringPanel-d_Ppowsq.js → TemplateScoringPanel-ebsrVge3.js} +19 -19
- package/dist/cdn/chunks/TemplateScoringPanel-ebsrVge3.js.map +1 -0
- package/dist/cdn/chunks/{TemplateSettings-Dr5OJ0Gi.js → TemplateSettings-BaEVdUTM.js} +9 -9
- package/dist/cdn/chunks/TemplateSettings-BaEVdUTM.js.map +1 -0
- package/dist/cdn/chunks/{TestEmailModal-CrjzVw8e.js → TestEmailModal-ide7UP_H.js} +7 -7
- package/dist/cdn/chunks/TestEmailModal-ide7UP_H.js.map +1 -0
- package/dist/cdn/chunks/{TestEmailPanel-k9vdulTu.js → TestEmailPanel-Cqdsnufx.js} +2 -2
- package/dist/cdn/chunks/TestEmailPanel-Cqdsnufx.js.map +1 -0
- package/dist/cdn/chunks/{TitleEditor-laILWfKh.js → TitleEditor-CoFzxLPA.js} +16 -16
- package/dist/cdn/chunks/TitleEditor-CoFzxLPA.js.map +1 -0
- package/dist/cdn/chunks/{ToggleSwitch-0dGCYnRY.js → ToggleSwitch-D-8dCEbI.js} +2 -2
- package/dist/cdn/chunks/ToggleSwitch-D-8dCEbI.js.map +1 -0
- package/dist/cdn/chunks/{Toolbar-BsiHW9gY.js → Toolbar-BDzhrmGV.js} +814 -566
- package/dist/cdn/chunks/Toolbar-BDzhrmGV.js.map +1 -0
- package/dist/cdn/chunks/{TplModal-D8DoPbFa.js → TplModal-mLMmolQp.js} +3 -3
- package/dist/cdn/chunks/TplModal-mLMmolQp.js.map +1 -0
- package/dist/cdn/chunks/{VersionHistoryMenu-B3-hNu9c.js → VersionHistoryMenu-C_bBKOYU.js} +7 -7
- package/dist/cdn/chunks/VersionHistoryMenu-C_bBKOYU.js.map +1 -0
- package/dist/cdn/chunks/{VersionHistoryPanels-zw1AtzGN.js → VersionHistoryPanels-B9LwOKxm.js} +2 -2
- package/dist/cdn/chunks/VersionHistoryPanels-B9LwOKxm.js.map +1 -0
- package/dist/cdn/chunks/{VersionPreviewBanner-PzVQSBJE.js → VersionPreviewBanner-CKyrcgxJ.js} +2 -2
- package/dist/cdn/chunks/VersionPreviewBanner-CKyrcgxJ.js.map +1 -0
- package/dist/cdn/chunks/{WrapperBlock-CpRvfAV0.js → WrapperBlock-DI8vSvTO.js} +4 -4
- package/dist/cdn/chunks/WrapperBlock-DI8vSvTO.js.map +1 -0
- package/dist/cdn/chunks/blockTypeIcons-CdfCfqHE.js +21 -0
- package/dist/cdn/chunks/blockTypeIcons-CdfCfqHE.js.map +1 -0
- package/dist/{ca-Ckgw9yd0.js → cdn/chunks/ca--zGWn9AU.js} +16 -0
- package/dist/cdn/chunks/ca--zGWn9AU.js.map +1 -0
- package/dist/cdn/chunks/ca-BTf1Zbqb.js.map +1 -1
- package/dist/cdn/chunks/{cloud-CIT4yPp4.js → cloud-De-eh1u6.js} +2 -2
- package/dist/cdn/chunks/cloud-De-eh1u6.js.map +1 -0
- package/dist/cdn/chunks/{createCloudRuntime-COUxbPvd.js → createCloudRuntime-DsaUaWvr.js} +3 -3
- package/dist/cdn/chunks/createCloudRuntime-DsaUaWvr.js.map +1 -0
- package/dist/cdn/chunks/{de-D4dweQw8.js → de-BRNLKcDl.js} +15 -1
- package/dist/cdn/chunks/de-BRNLKcDl.js.map +1 -0
- package/dist/cdn/chunks/de-mMY5yQJu.js.map +1 -1
- package/dist/cdn/chunks/de-xzrh9PA3.js.map +1 -1
- package/dist/cdn/chunks/dist-BRZhN8qJ.js.map +1 -1
- package/dist/cdn/chunks/dist-CXB2_kBa.js.map +1 -1
- package/dist/cdn/chunks/draggable-IB4x6w2O.js.map +1 -1
- package/dist/cdn/chunks/{editor-modal-CKu8HhiB.js → editor-modal-CZQArGNj.js} +88 -88
- package/dist/cdn/chunks/editor-modal-CZQArGNj.js.map +1 -0
- package/dist/cdn/chunks/emojiData-6m0DBh7O.js.map +1 -1
- package/dist/cdn/chunks/en-9unxBjV8.js +2 -0
- package/dist/cdn/chunks/en-ChVeGQBY.js.map +1 -1
- package/dist/{en-VE7R7NyL.js → cdn/chunks/en-JJjqHMjy.js} +16 -0
- package/dist/cdn/chunks/en-JJjqHMjy.js.map +1 -0
- package/dist/cdn/chunks/{es-B9OhP8H3.js → es-BIh8BxV9.js} +15 -1
- package/dist/cdn/chunks/es-BIh8BxV9.js.map +1 -0
- package/dist/cdn/chunks/es-BT4LSpE2.js.map +1 -1
- package/dist/cdn/chunks/{extensions-CafoGF65.js → extensions-Dy251lPd.js} +5 -5
- package/dist/cdn/chunks/extensions-Dy251lPd.js.map +1 -0
- package/dist/cdn/chunks/formatAbsoluteDateTime-DMmop3C-.js.map +1 -1
- package/dist/cdn/chunks/formatRelativeTime-CtUU-QZ8.js.map +1 -1
- package/dist/{fr-wrXhIi00.js → cdn/chunks/fr-C5VUZogP.js} +16 -0
- package/dist/cdn/chunks/fr-C5VUZogP.js.map +1 -0
- package/dist/cdn/chunks/fr-CI7OJoql.js.map +1 -1
- package/dist/cdn/chunks/htmlparser-ClUZ6waR.js.map +1 -1
- package/dist/cdn/chunks/{icons-BKkJ-5xF.js → icons-CQnjugjy.js} +86 -45
- package/dist/cdn/chunks/icons-CQnjugjy.js.map +1 -0
- package/dist/cdn/chunks/ja-DhlWUfke.js.map +1 -1
- package/dist/cdn/chunks/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +15 -1
- package/dist/cdn/chunks/ja-x5JYxaCW.js.map +1 -0
- package/dist/cdn/chunks/keys-DCWeHkSF.js.map +1 -1
- package/dist/cdn/chunks/liquid.browser-BypTT2oc.js.map +1 -1
- package/dist/cdn/chunks/logger-ByvdoAwq.js.map +1 -1
- package/dist/cdn/chunks/nl-D8y5OWGB.js.map +1 -1
- package/dist/cdn/chunks/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +15 -1
- package/dist/cdn/chunks/nl-UXxFyXV6.js.map +1 -0
- package/dist/cdn/chunks/{preRenderCustomBlocks-Dpj-_cOj.js → preRenderCustomBlocks-h4ZFBKbD.js} +2 -2
- package/dist/cdn/chunks/preRenderCustomBlocks-h4ZFBKbD.js.map +1 -0
- package/dist/cdn/chunks/pt-BR-CoCwRUi0.js.map +1 -1
- package/dist/{pt-BR-Bh1J8ey6.js → cdn/chunks/pt-BR-DHm3SP6W.js} +16 -0
- package/dist/cdn/chunks/pt-BR-DHm3SP6W.js.map +1 -0
- package/dist/cdn/chunks/pt-BR-DMCbUBvJ.js.map +1 -1
- package/dist/cdn/chunks/pusher-BbIHJKf3.js.map +1 -1
- package/dist/cdn/chunks/readableTextColor-f8Kykfnh.js.map +1 -1
- package/dist/cdn/chunks/{socialIcons-BvYNMn1G.js → socialIcons-ZOCwCCyn.js} +3 -3
- package/dist/cdn/chunks/socialIcons-ZOCwCCyn.js.map +1 -0
- package/dist/cdn/chunks/{src-DveVYoWS.js → src-BR3I3gOW.js} +265 -224
- package/dist/cdn/chunks/src-BR3I3gOW.js.map +1 -0
- package/dist/cdn/chunks/{src-Cnc5E71L.js → src-fZSLjhjy.js} +12 -12
- package/dist/cdn/chunks/src-fZSLjhjy.js.map +1 -0
- package/dist/cdn/chunks/{src-L6HEdwFn.js → src-nhIF5-mf.js} +179 -166
- package/dist/cdn/chunks/src-nhIF5-mf.js.map +1 -0
- package/dist/cdn/chunks/styleConstants-C_YAPdQS.js.map +1 -1
- package/dist/cdn/chunks/templateSettingsFields-YCFVBVYh.js.map +1 -1
- package/dist/cdn/chunks/timeouts-DHPZQ0G3.js.map +1 -1
- package/dist/cdn/chunks/{tiptap-DhLuKyDc.js → tiptap-D1u8JIUW.js} +1916 -1917
- package/dist/cdn/chunks/tiptap-D1u8JIUW.js.map +1 -0
- package/dist/cdn/chunks/useAliveFlag-DAlQLbM2.js.map +1 -1
- package/dist/cdn/chunks/useCloudI18n-CrjWbpbu.js.map +1 -1
- package/dist/cdn/chunks/{useEditorCore-DZmtKwJh.js → useEditorCore-D4kzTMtQ.js} +458 -446
- package/dist/cdn/chunks/useEditorCore-D4kzTMtQ.js.map +1 -0
- package/dist/cdn/chunks/useEditorRoot-CshefZLP.js.map +1 -1
- package/dist/cdn/chunks/useI18n-CyO_Nmca.js.map +1 -1
- package/dist/cdn/chunks/{useLogicTag-DF7-l66G.js → useLogicTag-J_i_hvZg.js} +2 -2
- package/dist/cdn/chunks/useLogicTag-J_i_hvZg.js.map +1 -0
- package/dist/cdn/chunks/{useMergeTag-DbIHw4NG.js → useMergeTag-C5MyHYxJ.js} +2 -2
- package/dist/cdn/chunks/useMergeTag-C5MyHYxJ.js.map +1 -0
- package/dist/cdn/chunks/{usePopoverPosition-BJRvAb-h.js → usePopoverPosition-DGXjXKjM.js} +25 -12
- package/dist/cdn/chunks/usePopoverPosition-DGXjXKjM.js.map +1 -0
- package/dist/cdn/chunks/useScrollToBlock-DgHZJvID.js.map +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +493 -464
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-eqb2JhqU.js → check-BMx_TLAS.js} +1 -1
- package/dist/{chevron-down-Dkr-7Ywl.js → chevron-down-KZf5mh9_.js} +1 -1
- package/dist/{chevron-right-Bge4XF_B.js → chevron-right-BLZ60Vxw.js} +1 -1
- package/dist/{chevron-up-CQAyl0kC.js → chevron-up-B9ynB8eU.js} +1 -1
- package/dist/{circle-alert-CYgTHKWr.js → circle-alert-pjvFFDrF.js} +1 -1
- package/dist/{clock-BgSJZJP5.js → clock-CiiTBfF3.js} +1 -1
- package/dist/{cloud-DsqqUfzh.js → cloud-AO_X8T9g.js} +1 -1
- package/dist/{copy-mw6PVnOJ.js → copy-BF0_pQjH.js} +1 -1
- package/dist/{createCloudRuntime-iLflkYzP.js → createCloudRuntime-D641nhri.js} +2 -2
- package/dist/{createLucideIcon-BxY5yz5u.js → createLucideIcon-C2YOVVWx.js} +8 -8
- package/dist/{de-D4dweQw8.js → de-BRNLKcDl.js} +14 -0
- package/dist/{dist-CQSVro7b.js → dist-B0dkvy7z.js} +1 -1
- package/dist/{dist-BJM3eFyT.js → dist-BPWuzECC.js} +1 -1
- package/dist/{dist-EDkj3Ub1.js → dist-BUHovM4k.js} +1 -1
- package/dist/{dist-BmsLcnrO.js → dist-BwoEME8p.js} +1 -1
- package/dist/{dist-DerFNpDY.js → dist-C2Ge44fD.js} +760 -761
- package/dist/{dist-BdHF7nyq.js → dist-CIxmOOSG.js} +5 -5
- package/dist/{dist-wyXxj3R1.js → dist-CJ7RZARo.js} +1 -1
- package/dist/{dist-Bl4rxpX9.js → dist-CxmLZQso.js} +1 -1
- package/dist/{dist-DyrEOKEJ.js → dist-DRmafAUz.js} +1 -1
- package/dist/{dist-Djmzp6wi.js → dist-DdOAotfz.js} +1 -1
- package/dist/{dist-BxVnMgXZ.js → dist-W_ERo6c_.js} +1 -1
- package/dist/{dist-DbUZvK04.js → dist-iLUVPysa.js} +263 -222
- package/dist/{dist-DQbOebGW.js → dist-p-LkiWIQ.js} +1 -1
- package/dist/{editor-modal-qzmEVcIk.js → editor-modal-99FB40dl.js} +16 -16
- package/dist/en-BvOjRLiw.js +2 -0
- package/dist/{cdn/chunks/en-VE7R7NyL.js → en-JJjqHMjy.js} +14 -2
- package/dist/{es-B9OhP8H3.js → es-BIh8BxV9.js} +14 -0
- package/dist/{extensions-DDhjuVKy.js → extensions-DqQXxrvP.js} +13 -13
- package/dist/{eye-DphkRtAD.js → eye-RdXYgrQZ.js} +1 -1
- package/dist/{cdn/chunks/fr-wrXhIi00.js → fr-C5VUZogP.js} +14 -2
- package/dist/{image-up-Qi2BXD7T.js → image-up-BwdoReQt.js} +1 -1
- package/dist/{info-VSyAs02q.js → info-zFMSGrL1.js} +1 -1
- package/dist/{ja-fKnVWXHC.js → ja-x5JYxaCW.js} +14 -0
- package/dist/{link-CNuozGqD.js → link-D7orqFBT.js} +1 -1
- package/dist/{list-BA0I5Wmu.js → list-BGt4Pgxc.js} +1 -1
- package/dist/{loader-circle-z5eeqasv.js → loader-circle-BVBgkRGz.js} +1 -1
- package/dist/{message-circle-BsQ9hEup.js → message-circle-D8YLsarv.js} +1 -1
- package/dist/{nl-DeFatKJe.js → nl-UXxFyXV6.js} +14 -0
- package/dist/{package-OkXfuRoO.js → package-CuFvk0L5.js} +1 -1
- package/dist/{pencil-DFVFA7Dj.js → pencil-DRYbmZW7.js} +1 -1
- package/dist/{plus-D8bkVjZ7.js → plus-Dv0b3kh0.js} +1 -1
- package/dist/{preRenderCustomBlocks-BBSqQgke.js → preRenderCustomBlocks-CTstI3Q-.js} +4 -4
- package/dist/{cdn/chunks/pt-BR-Bh1J8ey6.js → pt-BR-DHm3SP6W.js} +14 -2
- package/dist/{refresh-cw-DPB7YFuz.js → refresh-cw-gYTIS5bJ.js} +1 -1
- package/dist/{search-C-dCgqIa.js → search-D84_Ki_a.js} +1 -1
- package/dist/{send-Cm2fy2zm.js → send-Cn-8_H_U.js} +1 -1
- package/dist/{shield-check-COgBYFDF.js → shield-check-BOoVWJtD.js} +1 -1
- package/dist/{smartphone-v0u_MOEV.js → smartphone-4Ej3PBw5.js} +1 -1
- package/dist/{socialIcons-O7PZCW2P.js → socialIcons-CzRJskEk.js} +47 -47
- package/dist/{sparkles-Ds0RF8Iu.js → sparkles-D3vnNe7O.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/templatical-editor.js +432 -403
- package/dist/{text-align-start-DiavaIm6.js → text-align-start-Ci2h_Xly.js} +1 -1
- package/dist/{trash-DnOkfu2V.js → trash-wmi4UKvv.js} +1 -1
- package/dist/{triangle-alert-DGLAyXnh.js → triangle-alert-DJ0r2wmQ.js} +1 -1
- package/dist/{upload-BBDme2cy.js → upload-XMde-TXf.js} +1 -1
- package/dist/{useEditorCore-dcdHB_7H.js → useEditorCore-Dhit8Ygg.js} +894 -882
- package/dist/{useLogicTag-D9pvgxQR.js → useLogicTag-BaH8gey0.js} +1 -1
- package/dist/{useMergeTag-BaVFPdlq.js → useMergeTag-Cyad1yBE.js} +1 -1
- package/dist/{usePopoverPosition-DH7z3Ghm.js → usePopoverPosition-BYmd-lhR.js} +24 -11
- package/dist/{useScrollToBlock-Dj4v3rxr.js → useScrollToBlock-B4F7KCrf.js} +1 -1
- package/dist/{x-3BHV3Q6K.js → x-BWrtpVl-.js} +1 -1
- package/package.json +10 -10
- package/dist/cdn/chunks/AiChatSidebar-mKImUXi6.js.map +0 -1
- package/dist/cdn/chunks/AiFeatureMenu-Cx3qdStQ.js.map +0 -1
- package/dist/cdn/chunks/BlockIssueBadge-DS1pUf5g.js.map +0 -1
- package/dist/cdn/chunks/BlockPreviewCanvas-D31TNib3.js.map +0 -1
- package/dist/cdn/chunks/CloudHeaderExtras-0zdlKiq7.js.map +0 -1
- package/dist/cdn/chunks/CloudPanels-DPh71yOO.js.map +0 -1
- package/dist/cdn/chunks/CollaboratorBar-lCZU8qkg.js.map +0 -1
- package/dist/cdn/chunks/CommentsPanel-BtAGZfF6.js.map +0 -1
- package/dist/cdn/chunks/CommentsSidebar-Ws07MXYj.js.map +0 -1
- package/dist/cdn/chunks/CountdownToolbar-Bu93WiS8.js.map +0 -1
- package/dist/cdn/chunks/DesignReferenceSidebar-BlSdh5B7.js.map +0 -1
- package/dist/cdn/chunks/IssuesPanel-CKkv9z9g.js.map +0 -1
- package/dist/cdn/chunks/LogicTagInsertButton-CHc1yZ9v.js.map +0 -1
- package/dist/cdn/chunks/MediaEditModal-Cw9MhjVz.js.map +0 -1
- package/dist/cdn/chunks/MediaPanels-FD_aX3H5.js.map +0 -1
- package/dist/cdn/chunks/MergeTagInput--qTYfCWk.js.map +0 -1
- package/dist/cdn/chunks/MergeTagModeToggle-CdWIGo3Q.js.map +0 -1
- package/dist/cdn/chunks/ParagraphEditor-w2qEZTkM.js.map +0 -1
- package/dist/cdn/chunks/RestoreVersionDialog-cnWVN3nX.js.map +0 -1
- package/dist/cdn/chunks/RichTextEditorContent-Fzuu9pLP.js.map +0 -1
- package/dist/cdn/chunks/SaveBlockDialog-jxQtKnVs.js.map +0 -1
- package/dist/cdn/chunks/SavedBlocksBrowserModal-DpXWf4Pi.js.map +0 -1
- package/dist/cdn/chunks/SavedBlocksPanels-B-ASmf1P.js.map +0 -1
- package/dist/cdn/chunks/SavedBlocksPickBar-CEFIQ8Nd.js.map +0 -1
- package/dist/cdn/chunks/TemplateScoringPanel-d_Ppowsq.js.map +0 -1
- package/dist/cdn/chunks/TemplateSettings-Dr5OJ0Gi.js.map +0 -1
- package/dist/cdn/chunks/TestEmailModal-CrjzVw8e.js.map +0 -1
- package/dist/cdn/chunks/TestEmailPanel-k9vdulTu.js.map +0 -1
- package/dist/cdn/chunks/TitleEditor-laILWfKh.js.map +0 -1
- package/dist/cdn/chunks/ToggleSwitch-0dGCYnRY.js.map +0 -1
- package/dist/cdn/chunks/Toolbar-BsiHW9gY.js.map +0 -1
- package/dist/cdn/chunks/TplModal-D8DoPbFa.js.map +0 -1
- package/dist/cdn/chunks/VersionHistoryMenu-B3-hNu9c.js.map +0 -1
- package/dist/cdn/chunks/VersionHistoryPanels-zw1AtzGN.js.map +0 -1
- package/dist/cdn/chunks/VersionPreviewBanner-PzVQSBJE.js.map +0 -1
- package/dist/cdn/chunks/WrapperBlock-CpRvfAV0.js.map +0 -1
- package/dist/cdn/chunks/blockTypeIcons-raBjStuv.js +0 -21
- package/dist/cdn/chunks/blockTypeIcons-raBjStuv.js.map +0 -1
- package/dist/cdn/chunks/ca-Ckgw9yd0.js.map +0 -1
- package/dist/cdn/chunks/cloud-CIT4yPp4.js.map +0 -1
- package/dist/cdn/chunks/createCloudRuntime-COUxbPvd.js.map +0 -1
- package/dist/cdn/chunks/de-D4dweQw8.js.map +0 -1
- package/dist/cdn/chunks/editor-modal-CKu8HhiB.js.map +0 -1
- package/dist/cdn/chunks/en-DqzAdO8F.js +0 -2
- package/dist/cdn/chunks/en-VE7R7NyL.js.map +0 -1
- package/dist/cdn/chunks/es-B9OhP8H3.js.map +0 -1
- package/dist/cdn/chunks/extensions-CafoGF65.js.map +0 -1
- package/dist/cdn/chunks/fr-wrXhIi00.js.map +0 -1
- package/dist/cdn/chunks/icons-BKkJ-5xF.js.map +0 -1
- package/dist/cdn/chunks/ja-fKnVWXHC.js.map +0 -1
- package/dist/cdn/chunks/nl-DeFatKJe.js.map +0 -1
- package/dist/cdn/chunks/preRenderCustomBlocks-Dpj-_cOj.js.map +0 -1
- package/dist/cdn/chunks/pt-BR-Bh1J8ey6.js.map +0 -1
- package/dist/cdn/chunks/socialIcons-BvYNMn1G.js.map +0 -1
- package/dist/cdn/chunks/src-Cnc5E71L.js.map +0 -1
- package/dist/cdn/chunks/src-DveVYoWS.js.map +0 -1
- package/dist/cdn/chunks/src-L6HEdwFn.js.map +0 -1
- package/dist/cdn/chunks/tiptap-DhLuKyDc.js.map +0 -1
- package/dist/cdn/chunks/useEditorCore-DZmtKwJh.js.map +0 -1
- package/dist/cdn/chunks/useLogicTag-DF7-l66G.js.map +0 -1
- package/dist/cdn/chunks/useMergeTag-DbIHw4NG.js.map +0 -1
- package/dist/cdn/chunks/usePopoverPosition-BJRvAb-h.js.map +0 -1
- package/dist/en-1tgLzB8q.js +0 -2
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-CIT4yPp4.js","names":[],"sources":["../../../../core/src/cloud/auth.ts","../../../../core/src/cloud/url-builder.ts","../../../../core/src/cloud/api.ts","../../../../core/src/cloud/media-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/media-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.expiresAt || !data.projectId || !data.tenant) {\n throw new Error(\n \"Invalid token response: missing token, expiresAt, projectId, or tenant\",\n );\n }\n\n this.accessToken = data.token;\n this.expiresAt = new Date(data.expiresAt * 1000);\n this._projectId = data.projectId;\n this._tenantSlug = data.tenant;\n\n if (data.testEmail?.allowedEmails && data.testEmail?.signature) {\n this._testEmailConfig = {\n allowedEmails: data.testEmail.allowedEmails,\n signature: data.testEmail.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 allowedEmails: 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 blockId?: string;\n parentId?: string;\n userId: string;\n userName: string;\n userSignature: 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 userId: string;\n userName: string;\n userSignature: 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 userId: string;\n userName: string;\n userSignature: 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 userId: string;\n userName: string;\n userSignature: 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 { ApiError, ApiResponse } from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport { API_ROUTES, buildUrl } from \"./url-builder\";\n\n/**\n * Cloud's HTTP row for one media file. Same field names as\n * {@link MediaAsset}. `thumbnailUrl` is optional; omit it and the grid\n * uses `url`.\n */\nexport interface CloudMediaItem {\n id: string;\n filename: string;\n mimeType: string;\n size: number;\n url: string;\n thumbnailUrl?: string | null;\n folderId: string | null;\n width: number | null;\n height: number | null;\n alt: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Cloud's wire shape for one folder. The index endpoint may return a tree\n * (`children`) or a flat list (`parentId`); the adapter flattens either.\n */\nexport interface CloudMediaFolder {\n id: string;\n name: string;\n parentId?: string | null;\n children?: CloudMediaFolder[];\n projectId?: string;\n createdAt?: string;\n}\n\nexport interface CloudMediaBrowseParams {\n folderId?: string | null;\n search?: string;\n category?: string;\n sort?: string;\n cursor?: string;\n}\n\nexport interface CloudMediaBrowseResponse {\n data: CloudMediaItem[];\n meta: {\n path: string;\n perPage: number;\n nextCursor: string | null;\n prevCursor: string | null;\n };\n}\n\nexport interface CloudMediaUsageInfo {\n templateCount: number;\n templateNames: string[];\n}\n\nexport interface CloudMediaUsageResponse {\n data: Record<string, CloudMediaUsageInfo>;\n}\n\n/**\n * Cloud's HTTP client for media. Auth and project/tenant scoping come from\n * {@link AuthManager}. The BYO contract lives on\n * `createCloudMediaProvider`, which maps this client's rows.\n */\nexport class MediaApiClient {\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 throw new Error(error.message, { cause: error });\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 async browseMedia(\n params: CloudMediaBrowseParams,\n ): Promise<CloudMediaBrowseResponse> {\n const query = new URLSearchParams();\n if (params.folderId) query.set(\"folderId\", params.folderId);\n if (params.search) query.set(\"search\", params.search);\n if (params.category) query.set(\"category\", params.category);\n if (params.sort) query.set(\"sort\", params.sort);\n if (params.cursor) query.set(\"cursor\", params.cursor);\n\n const queryString = query.toString();\n const url = `${buildUrl(API_ROUTES[\"media.browse\"], this.baseParams)}${queryString ? `?${queryString}` : \"\"}`;\n const response = await this.authManager.authenticatedFetch(url, {\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n throw new Error(error.message, { cause: error });\n }\n\n return response.json();\n }\n\n async uploadMedia(\n file: File,\n folderId?: string | null,\n ): Promise<CloudMediaItem> {\n const formData = new FormData();\n formData.append(\"file\", file);\n if (folderId) formData.append(\"folderId\", folderId);\n\n const url = buildUrl(API_ROUTES[\"media.upload\"], this.baseParams);\n const response = await this.authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: { Accept: \"application/json\" },\n body: formData,\n });\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n throw new Error(error.message, { cause: error });\n }\n\n const json: ApiResponse<CloudMediaItem> = await response.json();\n return json.data;\n }\n\n async updateMedia(\n mediaId: string,\n filename?: string,\n alt?: string,\n ): Promise<CloudMediaItem> {\n const body: { filename?: string; alt?: string } = {};\n if (filename !== undefined) {\n body.filename = filename;\n }\n if (alt !== undefined) {\n body.alt = alt;\n }\n return this.request<CloudMediaItem>(\n buildUrl(API_ROUTES[\"media.update\"], {\n ...this.baseParams,\n media: mediaId,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify(body),\n },\n );\n }\n\n async deleteMedia(ids: string[]): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"media.delete\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify({ ids }),\n },\n );\n }\n\n async moveMedia(\n ids: string[],\n folderId: string | null,\n ): Promise<CloudMediaItem[]> {\n return this.request<CloudMediaItem[]>(\n buildUrl(API_ROUTES[\"media.move\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify({ ids, folderId }),\n },\n );\n }\n\n async getMediaFolders(): Promise<CloudMediaFolder[]> {\n return this.request<CloudMediaFolder[]>(\n buildUrl(API_ROUTES[\"folders.index\"], this.baseParams),\n );\n }\n\n async createMediaFolder(\n name: string,\n parentId?: string | null,\n ): Promise<CloudMediaFolder> {\n return this.request<CloudMediaFolder>(\n buildUrl(API_ROUTES[\"folders.store\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify({\n name,\n parentId: parentId ?? null,\n }),\n },\n );\n }\n\n async renameMediaFolder(\n folderId: string,\n name: string,\n ): Promise<CloudMediaFolder> {\n return this.request<CloudMediaFolder>(\n buildUrl(API_ROUTES[\"folders.update\"], {\n ...this.baseParams,\n mediaFolder: folderId,\n }),\n {\n method: \"PUT\",\n body: JSON.stringify({ name }),\n },\n );\n }\n\n async deleteMediaFolder(folderId: string): Promise<void> {\n return this.request<void>(\n buildUrl(API_ROUTES[\"folders.destroy\"], {\n ...this.baseParams,\n mediaFolder: folderId,\n }),\n {\n method: \"DELETE\",\n },\n );\n }\n\n async checkMediaUsage(ids: string[]): Promise<CloudMediaUsageResponse> {\n const response = await this.authManager.authenticatedFetch(\n buildUrl(API_ROUTES[\"media.checkUsage\"], this.baseParams),\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ ids }),\n },\n );\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n throw new Error(error.message, { cause: error });\n }\n\n return response.json();\n }\n\n async getFrequentlyUsed(): Promise<CloudMediaItem[]> {\n return this.request<CloudMediaItem[]>(\n buildUrl(API_ROUTES[\"media.frequentlyUsed\"], this.baseParams),\n );\n }\n\n async importFromUrl(\n url: string,\n folderId?: string | null,\n ): Promise<CloudMediaItem> {\n return this.request<CloudMediaItem>(\n buildUrl(API_ROUTES[\"media.importFromUrl\"], this.baseParams),\n {\n method: \"POST\",\n body: JSON.stringify({\n url,\n folderId: folderId ?? null,\n }),\n },\n );\n }\n\n async replaceMedia(mediaId: string, file: File): Promise<CloudMediaItem> {\n const formData = new FormData();\n formData.append(\"file\", file);\n\n const url = buildUrl(API_ROUTES[\"media.replace\"], {\n ...this.baseParams,\n media: mediaId,\n });\n const response = await this.authManager.authenticatedFetch(url, {\n method: \"POST\",\n headers: { Accept: \"application/json\" },\n body: formData,\n });\n\n if (!response.ok) {\n const error: ApiError = await response.json().catch(() => ({\n message: `HTTP error ${response.status}`,\n }));\n throw new Error(error.message, { cause: error });\n }\n\n const json: ApiResponse<CloudMediaItem> = await response.json();\n return json.data;\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.appKey,\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 userId: this.authManager.userConfig?.id ?? \"\",\n userName: this.authManager.userConfig?.name ?? \"\",\n userSignature: 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 { TemplateOperationPayload } 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: TemplateOperationPayload,\n): void {\n const { operation, data } = payload;\n\n switch (operation) {\n case \"addBlock\":\n editor.addBlock(\n data.block as Block,\n data.sectionId as string | undefined,\n data.columnIndex as number | undefined,\n data.index as number | undefined,\n );\n break;\n\n case \"updateBlock\":\n editor.updateBlock(\n data.blockId as string,\n data.updates as Partial<Block>,\n );\n break;\n\n case \"deleteBlock\":\n editor.removeBlock(data.blockId as string);\n break;\n\n case \"moveBlock\":\n editor.moveBlock(\n data.blockId as string,\n data.index as number,\n data.sectionId as string | undefined,\n data.columnIndex as number | undefined,\n );\n break;\n\n case \"updateSettings\":\n editor.updateSettings(data.updates as Partial<TemplateSettings>);\n break;\n\n case \"setContent\":\n editor.setContent(data.content as TemplateContent);\n break;\n\n case \"updateBlockStyle\":\n editor.updateBlock(\n data.blockId 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.conversationId) {\n conversationId.value = data.conversationId;\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 createdAt: string;\n }) => ({\n id: msg.id,\n role: msg.role,\n content: msg.content,\n timestamp: new Date(msg.createdAt).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 currentContent: currentContent,\n mergeTags: 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 currentContent: currentContent,\n mergeTags: mergeTags.map((p) => ({\n label: p.label,\n value: p.value,\n })),\n conversationId: 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(\"aiGenerationNotAvailable\");\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.conversationId) {\n conversationId.value = event.conversationId;\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 currentContent: content,\n mergeTags: 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(\"aiGenerationNotAvailable\");\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 mergeTags: 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(\"aiGenerationNotAvailable\");\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(\"aiGenerationNotAvailable\");\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 | \"commentCreated\"\n | \"commentUpdated\"\n | \"commentDeleted\"\n | \"commentResolved\"\n | \"commentUnresolved\";\n comment: CommentResponse;\n}\n\n/**\n * Cloud's HTTP row → {@link Comment}. Omits `updatedAt` when it equals\n * `createdAt` (Cloud stamps both on insert) so the panel does not mark\n * every comment \"(edited)\", and omits `resolvedBy` / empty `replies`\n * when they are absent.\n */\nfunction toComment(record: CommentResponse): Comment {\n const comment: Comment = {\n id: record.id,\n body: record.body,\n author: record.author,\n createdAt: record.createdAt,\n blockId: record.blockId,\n parentId: record.parentId,\n resolvedAt: record.resolvedAt,\n };\n\n if (record.updatedAt !== record.createdAt) {\n comment.updatedAt = record.updatedAt;\n }\n if (record.resolvedBy) {\n comment.resolvedBy = record.resolvedBy;\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.** `userId` / `userName` / `userSignature` 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 userId: string;\n userName: string;\n userSignature: 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 userId: user.id,\n userName: user.name,\n userSignature: 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 blockId: input.blockId,\n parentId: 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 \"commentCreated\":\n onChange({ type: \"created\", comment });\n break;\n case \"commentDeleted\":\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 \"commentUpdated\":\n case \"commentResolved\":\n case \"commentUnresolved\":\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 {\n Collaborator,\n TemplateOperationPayload,\n} 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-blockLocked\",\n \"client-blockUnlocked\",\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: TemplateOperationPayload) => 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: TemplateOperationPayload): void {\n isProcessingRemoteOperation = true;\n try {\n handleOperation(editor, payload);\n } finally {\n isProcessingRemoteOperation = false;\n }\n }\n\n function broadcastOperation(payload: TemplateOperationPayload): 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-blockLocked\", {\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-blockUnlocked\", { 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-blockLocked\",\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-blockUnlocked\", (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: TemplateOperationPayload) => {\n handleRemoteOperation(payload);\n });\n\n newChannel.bind(\"mcp-operation\", (payload: TemplateOperationPayload) => {\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 { TemplateOperationPayload } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"../editor\";\n\ninterface BroadcastTarget {\n _broadcastOperation: (payload: TemplateOperationPayload) => 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: \"addBlock\",\n data: {\n block,\n sectionId: targetSectionId,\n columnIndex: 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: \"updateBlock\",\n data: { blockId: blockId, updates },\n timestamp: Date.now(),\n });\n };\n\n editor.removeBlock = (blockId) => {\n originalRemoveBlock(blockId);\n collaboration._broadcastOperation({\n operation: \"deleteBlock\",\n data: { blockId: 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: \"moveBlock\",\n data: {\n blockId: blockId,\n index: newIndex,\n sectionId: targetSectionId,\n columnIndex: columnIndex,\n },\n timestamp: Date.now(),\n });\n };\n\n editor.updateSettings = (updates) => {\n originalUpdateSettings(updates);\n collaboration._broadcastOperation({\n operation: \"updateSettings\",\n data: { updates },\n timestamp: Date.now(),\n });\n };\n\n editor.setContent = (content, markDirty?) => {\n originalSetContent(content, markDirty);\n collaboration._broadcastOperation({\n operation: \"setContent\",\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 MediaAsset,\n MediaAssetPatch,\n MediaCategory,\n MediaCreateInput,\n MediaFolder,\n MediaFolderInput,\n MediaListPage,\n MediaListParams,\n MediaProvider,\n MediaStorageInfo,\n MediaUsageInfo,\n PlanConfig,\n} from \"@templatical/types\";\nimport type { AuthManager } from \"./auth\";\nimport {\n MediaApiClient,\n type CloudMediaFolder,\n type CloudMediaItem,\n} from \"./media-api\";\n\n/**\n * Cloud's HTTP row → {@link MediaAsset}. Omits null optionals so\n * `thumbnailUrl` / `width` / `height` are absent rather than `null` —\n * the grid falls back to `url` when there is no thumbnail. Confirm\n * always inserts `url`.\n */\nfunction toAsset(item: CloudMediaItem): MediaAsset {\n const asset: MediaAsset = {\n id: item.id,\n url: item.url,\n filename: item.filename,\n alt: item.alt,\n mimeType: item.mimeType,\n folderId: item.folderId,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n size: item.size,\n };\n if (item.width != null) {\n asset.width = item.width;\n }\n if (item.height != null) {\n asset.height = item.height;\n }\n if (item.thumbnailUrl != null) {\n asset.thumbnailUrl = item.thumbnailUrl;\n }\n return asset;\n}\n\nfunction toFolder(\n folder: CloudMediaFolder,\n inheritedParentId: string | null = null,\n): MediaFolder {\n return {\n id: folder.id,\n name: folder.name,\n parentId:\n folder.parentId !== undefined ? folder.parentId : inheritedParentId,\n };\n}\n\n/**\n * Flatten a Cloud folder payload into the contract's flat list.\n *\n * The index endpoint may return a tree (`children`) or already-flat\n * `parentId` rows. Depth-first so a parent is always listed before its\n * descendants. Already-flat rows just copy `parentId`.\n */\nfunction flattenFolders(\n nodes: CloudMediaFolder[],\n inheritedParentId: string | null = null,\n): MediaFolder[] {\n const out: MediaFolder[] = [];\n for (const node of nodes) {\n out.push(toFolder(node, inheritedParentId));\n if (node.children?.length) {\n out.push(...flattenFolders(node.children, node.id));\n }\n }\n return out;\n}\n\nfunction mimeTypesFromPlan(\n plan: PlanConfig | null,\n): Partial<Record<MediaCategory, string[]>> | undefined {\n const categories = plan?.media?.categories;\n if (!categories) {\n return undefined;\n }\n const mapped: Partial<Record<MediaCategory, string[]>> = {};\n for (const [key, value] of Object.entries(categories)) {\n mapped[key as MediaCategory] = value.mimeTypes;\n }\n return mapped;\n}\n\n/**\n * Cloud-backed {@link MediaProvider} — the Templatical Cloud adapter for the\n * same DAM contract consumers implement themselves.\n *\n * Every method is a function: Cloud's media storage is what the plan pays\n * for, so there is no Cloud tier that can list but not upload. A consumer\n * who wants a read-only gallery supplies their own provider with the\n * mutations set to `false`.\n *\n * **Ignores `templateId`.** Cloud's library is project-scoped; a CMS that\n * scopes a gallery per template reads that param in its own `list`.\n *\n * **`storage` / `maxFileSize` / `mimeTypes` are live.** They read\n * `getPlanConfig()` at call time, because plan config arrives after\n * construction. Snapshotting at setup would pin `storage()` to `null` and\n * hide the quota ring for the whole session. A spread of this object\n * freezes the getters — `initCloud()` must `Object.assign`, never spread.\n */\nexport function createCloudMediaProvider(\n authManager: AuthManager,\n getPlanConfig?: () => PlanConfig | null,\n): MediaProvider {\n const api = new MediaApiClient(authManager);\n\n return {\n async list(params?: MediaListParams): Promise<MediaListPage> {\n const response = await api.browseMedia({\n search: params?.search,\n cursor: params?.cursor,\n folderId: params?.folderId,\n category: params?.category,\n });\n const page: MediaListPage = {\n items: response.data.map(toAsset),\n };\n if (response.meta.nextCursor) {\n page.nextCursor = response.meta.nextCursor;\n }\n return page;\n },\n\n async create(input: MediaCreateInput): Promise<MediaAsset> {\n return toAsset(await api.uploadMedia(input.file, input.folderId));\n },\n\n async update(id: string, patch: MediaAssetPatch): Promise<MediaAsset> {\n return toAsset(await api.updateMedia(id, patch.filename, patch.alt));\n },\n\n delete(ids: string[]): Promise<void> {\n return api.deleteMedia(ids);\n },\n\n folders: {\n async list(): Promise<MediaFolder[]> {\n return flattenFolders(await api.getMediaFolders());\n },\n async create(input: MediaFolderInput): Promise<MediaFolder> {\n return toFolder(\n await api.createMediaFolder(input.name, input.parentId),\n );\n },\n async update(id: string, patch: { name: string }): Promise<MediaFolder> {\n return toFolder(await api.renameMediaFolder(id, patch.name));\n },\n delete(id: string): Promise<void> {\n return api.deleteMediaFolder(id);\n },\n async move(\n ids: string[],\n folderId: string | null,\n ): Promise<MediaAsset[]> {\n return (await api.moveMedia(ids, folderId)).map(toAsset);\n },\n },\n\n async replace(id: string, file: File): Promise<MediaAsset> {\n return toAsset(await api.replaceMedia(id, file));\n },\n\n async importFromUrl(\n url: string,\n folderId?: string | null,\n ): Promise<MediaAsset> {\n return toAsset(await api.importFromUrl(url, folderId));\n },\n\n async checkUsage(ids: string[]): Promise<Record<string, MediaUsageInfo>> {\n const response = await api.checkMediaUsage(ids);\n const mapped: Record<string, MediaUsageInfo> = {};\n for (const [id, info] of Object.entries(response.data)) {\n mapped[id] = {\n templateCount: info.templateCount,\n templateNames: info.templateNames,\n };\n }\n return mapped;\n },\n\n async frequentlyUsed(): Promise<MediaAsset[]> {\n return (await api.getFrequentlyUsed()).map(toAsset);\n },\n\n async storage(): Promise<MediaStorageInfo | null> {\n return getPlanConfig?.()?.storage ?? null;\n },\n\n /**\n * A getter, not a snapshot: plan config arrives after construction.\n * The editor reads this inside a computed; capturing it at setup\n * would pin the client pre-check to `undefined`.\n */\n get maxFileSize(): number | undefined {\n return getPlanConfig?.()?.media?.maxFileSize;\n },\n\n get mimeTypes(): Partial<Record<MediaCategory, string[]>> | undefined {\n return mimeTypesFromPlan(getPlanConfig?.() ?? null);\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 HTTP row → {@link TemplateVersion}. Copies the contract fields\n * and drops `templateId`.\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.createdAt,\n isAutomatic: record.isAutomatic,\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 allowedEmails: 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; appKey: 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 appKey: string;\n}): Promise<{ ok: boolean; error?: string }> {\n if (!wsConfig?.host || !wsConfig?.appKey) {\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.appKey}?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 { TemplateOperationPayload } 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: TemplateOperationPayload) => 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: TemplateOperationPayload) => {\n handleOperation(editor, payload);\n onOperation?.(payload);\n });\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createCloudRuntime-COUxbPvd.js","names":[],"sources":["../../../src/cloud/composables/useCloudFeatureFlags.ts","../../../src/cloud/composables/useCloudPanelState.ts","../../../src/cloud/composables/useCloudSaveGate.ts","../../../src/cloud/composables/useCollabUndoWarning.ts","../../../src/cloud/collectSavedBlockIds.ts","../../../src/cloud/createCloudRuntime.ts"],"sourcesContent":["import { computed, type ComputedRef } from \"vue\";\nimport type {\n UsePlanConfigReturn,\n UseAiConfigReturn,\n} from \"@templatical/core/cloud\";\n\nexport interface UseCloudFeatureFlagsOptions {\n planConfigInstance: UsePlanConfigReturn;\n aiConfig: UseAiConfigReturn;\n /** The loaded template's id, or `null` before one exists. */\n getTemplateId: () => string | null;\n}\n\nexport interface UseCloudFeatureFlagsReturn {\n canUseAiGeneration: ComputedRef<boolean>;\n canSendTestEmail: ComputedRef<boolean>;\n hasTemplateSaved: ComputedRef<boolean>;\n templateLimit: ComputedRef<number | null>;\n templateCount: ComputedRef<number>;\n}\n\n/**\n * Every entitlement check Cloud makes, in one place.\n *\n * The containment rule this exists for: entitlement checks go through here,\n * never inline in a component and never across a package boundary. With five\n * plan features left there is little excuse to break it.\n *\n * Save status is deliberately not here — it lives on `useTemplatesFeature`, which\n * the one shared header reads, and was never an entitlement. Footer branding is\n * likewise not gated: `config.branding !== false` decides it on any plan.\n */\nexport function useCloudFeatureFlags(\n options: UseCloudFeatureFlagsOptions,\n): UseCloudFeatureFlagsReturn {\n const { planConfigInstance, aiConfig, getTemplateId } = options;\n\n const canUseAiGeneration = computed(\n () =>\n planConfigInstance.hasFeature(\"aiGeneration\") &&\n aiConfig.hasAnyMenuFeature.value,\n );\n const canSendTestEmail = computed(() =>\n planConfigInstance.hasFeature(\"testEmail\"),\n );\n const hasTemplateSaved = computed(() => getTemplateId() !== null);\n const templateLimit = computed(\n () => planConfigInstance.config.value?.limits.maxTemplates ?? null,\n );\n const templateCount = computed(\n () => planConfigInstance.config.value?.templateCount ?? 0,\n );\n\n return {\n canUseAiGeneration,\n canSendTestEmail,\n hasTemplateSaved,\n templateLimit,\n templateCount,\n };\n}\n","import { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport { onClickOutside } from \"@vueuse/core\";\nimport type { AiFeature } from \"../components/AiFeatureMenu.vue\";\n\nexport interface UseCloudPanelStateReturn {\n activePanel: Ref<RightPanel | null>;\n aiChatOpen: ComputedRef<boolean> & { value: boolean };\n scoringPanelOpen: ComputedRef<boolean> & { value: boolean };\n designReferenceOpen: ComputedRef<boolean> & { value: boolean };\n commentsOpen: ComputedRef<boolean> & { value: boolean };\n aiMenuOpen: Ref<boolean>;\n aiMenuRef: Ref<HTMLElement | null>;\n rightPanelOpen: ComputedRef<boolean>;\n activeAiFeature: ComputedRef<AiFeature | null>;\n aiButtonActive: ComputedRef<boolean>;\n toggleAiMenu: () => void;\n handleAiFeatureSelect: (feature: AiFeature) => void;\n}\n\ntype RightPanel = \"ai-chat\" | \"scoring\" | \"design-reference\" | \"comments\";\n\nexport function useCloudPanelState(): UseCloudPanelStateReturn {\n const activePanel = ref<RightPanel | null>(null);\n\n const aiChatOpen = computed({\n get: () => activePanel.value === \"ai-chat\",\n set: (v) => (activePanel.value = v ? \"ai-chat\" : null),\n });\n const scoringPanelOpen = computed({\n get: () => activePanel.value === \"scoring\",\n set: (v) => (activePanel.value = v ? \"scoring\" : null),\n });\n const designReferenceOpen = computed({\n get: () => activePanel.value === \"design-reference\",\n set: (v) => (activePanel.value = v ? \"design-reference\" : null),\n });\n const commentsOpen = computed({\n get: () => activePanel.value === \"comments\",\n set: (v) => (activePanel.value = v ? \"comments\" : null),\n });\n\n const aiMenuOpen = ref(false);\n const aiMenuRef = ref<HTMLElement | null>(null);\n\n const rightPanelOpen = computed(() => activePanel.value !== null);\n\n const activeAiFeature = computed<AiFeature | null>(() => {\n const p = activePanel.value;\n if (p === \"ai-chat\" || p === \"design-reference\" || p === \"scoring\")\n return p;\n return null;\n });\n\n const aiButtonActive = computed(\n () =>\n aiMenuOpen.value ||\n activePanel.value === \"ai-chat\" ||\n activePanel.value === \"design-reference\" ||\n activePanel.value === \"scoring\",\n );\n\n function toggleAiMenu(): void {\n aiMenuOpen.value = !aiMenuOpen.value;\n }\n\n function handleAiFeatureSelect(feature: AiFeature): void {\n aiMenuOpen.value = false;\n activePanel.value = activePanel.value === feature ? null : feature;\n }\n\n onClickOutside(aiMenuRef, () => {\n aiMenuOpen.value = false;\n });\n\n return {\n activePanel,\n aiChatOpen,\n scoringPanelOpen,\n designReferenceOpen,\n commentsOpen,\n aiMenuOpen,\n aiMenuRef,\n rightPanelOpen,\n activeAiFeature,\n aiButtonActive,\n toggleAiMenu,\n handleAiFeatureSelect,\n };\n}\n","import { computed, ref, type ComputedRef, type Ref } from \"vue\";\nimport type { PlanConfig } from \"@templatical/types\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nexport interface UseCloudSaveGateOptions {\n /** Reactive accessibility issues from `useTemplateLint`. */\n issues: Ref<LintIssue[]>;\n /** Reactive plan config from the server. */\n planConfig: Ref<PlanConfig | null>;\n}\n\nexport interface UseCloudSaveGateReturn {\n /** True when the gate would block a save click given current state. */\n shouldBlock: ComputedRef<boolean>;\n /** Errors that would block the save. Empty when `shouldBlock` is false. */\n blockingIssues: ComputedRef<LintIssue[]>;\n /** Modal visibility — toggled by `tryRunSave` and `cancel`. */\n modalOpen: Ref<boolean>;\n /**\n * Wraps a save callback. Returns `true` when the save was invoked\n * (blocking disabled or no errors), `false` when the modal opened\n * instead. Caller can `await` the returned promise to know when the\n * save settled.\n */\n tryRunSave: (run: () => Promise<unknown> | unknown) => Promise<boolean>;\n /**\n * Runs the save only when the gate would not block, and does **nothing** when\n * it would — no modal, no save, nothing pending. Returns whether it ran.\n *\n * This is the autosave path. A modal on a debounce timer would interrupt\n * typing, but simply saving anyway would demote `blockOnError` from a server\n * policy to a manual-save-only speed bump. Skipping leaves the template dirty\n * and the header saying \"unsaved\", which is true, and the reason stays visible\n * in the Issues panel. The next edit re-arms the debounce, so fixing the issue\n * is what lets the save through — no retry timer needed.\n */\n runUnlessBlocked: (run: () => Promise<unknown> | unknown) => Promise<boolean>;\n /** Force-run the pending save, bypassing the gate. */\n confirmAndSave: () => Promise<void>;\n /** Dismiss the modal without saving. */\n cancel: () => void;\n}\n\n/**\n * Cloud-only save-gate. When `planConfig.accessibility.blockOnError === true`\n * and the linter reports any error-severity issues, intercept the save\n * click and surface a confirmation modal listing the blockers. The user\n * can dismiss to fix issues, or click \"Save anyway\" to bypass — the gate\n * does NOT enforce a hard block, it surfaces a deliberate choice.\n *\n * Server policy is the source of truth for `blockOnError`; consumer-side\n * `accessibility` options can't override it.\n */\nexport function useCloudSaveGate(\n opts: UseCloudSaveGateOptions,\n): UseCloudSaveGateReturn {\n const modalOpen = ref(false);\n let pending: (() => Promise<unknown> | unknown) | null = null;\n\n const blockOnError = computed(\n () => opts.planConfig.value?.accessibility?.blockOnError === true,\n );\n\n const blockingIssues = computed(() =>\n blockOnError.value\n ? opts.issues.value.filter((i) => i.severity === \"error\")\n : [],\n );\n\n const shouldBlock = computed(() => blockingIssues.value.length > 0);\n\n async function tryRunSave(\n run: () => Promise<unknown> | unknown,\n ): Promise<boolean> {\n if (!shouldBlock.value) {\n await run();\n return true;\n }\n pending = run;\n modalOpen.value = true;\n return false;\n }\n\n async function runUnlessBlocked(\n run: () => Promise<unknown> | unknown,\n ): Promise<boolean> {\n if (shouldBlock.value) return false;\n await run();\n return true;\n }\n\n async function confirmAndSave(): Promise<void> {\n const run = pending;\n pending = null;\n modalOpen.value = false;\n if (run) {\n await run();\n }\n }\n\n function cancel(): void {\n pending = null;\n modalOpen.value = false;\n }\n\n return {\n shouldBlock,\n blockingIssues,\n modalOpen,\n tryRunSave,\n runUnlessBlocked,\n confirmAndSave,\n cancel,\n };\n}\n","import { type ComputedRef, type Ref, ref } from \"vue\";\nimport { useTimeoutFn } from \"@vueuse/core\";\nimport { COLLAB_UNDO_WARNING_MS } from \"../../constants/timeouts\";\n\nexport interface UseCollabUndoWarningOptions {\n /** Whether collaboration is currently enabled (reactive). */\n isCollaborationEnabled: ComputedRef<boolean>;\n /** Returns the current list of collaborators. */\n getCollaboratorCount: () => number;\n /** Whether the history stack has entries to undo. */\n canUndo: ComputedRef<boolean>;\n}\n\nexport interface UseCollabUndoWarningReturn {\n collabUndoWarningVisible: Ref<boolean>;\n showCollabUndoWarning: () => void;\n}\n\nexport function useCollabUndoWarning(\n options: UseCollabUndoWarningOptions,\n): UseCollabUndoWarningReturn {\n const { isCollaborationEnabled, getCollaboratorCount, canUndo } = options;\n\n const collabUndoWarningFired = ref(false);\n const collabUndoWarningVisible = ref(false);\n\n const { start: startCollabUndoWarningTimeout } = useTimeoutFn(\n () => {\n collabUndoWarningVisible.value = false;\n },\n COLLAB_UNDO_WARNING_MS,\n { immediate: false },\n );\n\n function showCollabUndoWarning(): void {\n if (\n collabUndoWarningFired.value ||\n !isCollaborationEnabled.value ||\n getCollaboratorCount() === 0 ||\n !canUndo.value\n ) {\n return;\n }\n\n collabUndoWarningFired.value = true;\n collabUndoWarningVisible.value = true;\n startCollabUndoWarningTimeout();\n }\n\n return {\n collabUndoWarningVisible,\n showCollabUndoWarning,\n };\n}\n","/**\n * Just enough of a stored template to walk its ids — deliberately structural\n * rather than `Template`, because the caller reads `editor.state`, which is\n * `DeepReadonly` and so not assignable to the mutable contract type.\n *\n * `children` is a union: a section stores columns (`Block[][]`); a layout\n * `wrapper` stores a flat `Block[]`. Both appear on the `Block` union, so a\n * shape that only named the section form is not assignable from\n * `DeepReadonly<Template>`.\n */\ninterface SavedBlockIdNode {\n readonly id: string;\n readonly type: string;\n readonly children?:\n readonly SavedBlockIdNode[] | readonly (readonly SavedBlockIdNode[])[];\n}\n\ninterface SavedBlockIdSource {\n content?: {\n blocks?: readonly SavedBlockIdNode[];\n };\n}\n\n/**\n * Every block id present in the **stored** template, sections' children\n * included.\n *\n * This is the last thing the deleted Cloud editor core carried over the OSS one\n * (`savedBlockIds`), and it never belonged there: its only reader gates a\n * comment filter on whether the block exists server-side, which makes it a\n * *comments* dependency. It travels with that feature accordingly, and reaches\n * `CommentsSidebar` through `capabilities.comments.isBlockSaved`.\n *\n * Derived on demand rather than memoised: `state.template` only changes on a\n * create / load / save round-trip, and the one caller runs on a user filtering\n * the comments panel.\n */\nexport function collectSavedBlockIds(\n template: SavedBlockIdSource | null | undefined,\n): Set<string> {\n const ids = new Set<string>();\n collectFromBlocks(template?.content?.blocks, ids);\n return ids;\n}\n\nfunction collectFromBlocks(\n blocks: readonly SavedBlockIdNode[] | undefined,\n ids: Set<string>,\n): void {\n if (!blocks) return;\n for (const block of blocks) {\n ids.add(block.id);\n if (!block.children) continue;\n if (block.type === \"section\") {\n for (const column of block.children as readonly (readonly SavedBlockIdNode[])[]) {\n collectFromBlocks(column, ids);\n }\n continue;\n }\n if (block.type === \"wrapper\") {\n collectFromBlocks(block.children as readonly SavedBlockIdNode[], ids);\n }\n }\n}\n","import { computed, provide, ref, watch } from \"vue\";\nimport {\n AuthManager,\n isFatalAuthError,\n performHealthCheck,\n useAiConfig,\n useCollaboration,\n useCollaborationBroadcast,\n useExport,\n useMcpListener,\n usePlanConfig,\n resolveExportFonts,\n resolveWebSocketConfig,\n createCloudCommentsProvider,\n createCloudMediaProvider,\n createCloudRenderProvider,\n createCloudSavedBlocksProvider,\n createCloudTemplatesProvider,\n createCloudTestEmailProvider,\n createCloudVersionHistoryProvider,\n useTemplateScoring,\n useTestEmail,\n useWebSocket,\n type RealtimeChannel,\n} from \"@templatical/core/cloud\";\nimport type { UseEditorReturn } from \"@templatical/core\";\nimport type {\n CommentsOptions,\n CommentsProvider,\n EditorUser,\n MediaOptions,\n MediaProvider,\n RenderProvider,\n SavedBlocksOptions,\n SavedBlocksProvider,\n Template,\n TemplateContent,\n TemplatePatch,\n TemplatesOptions,\n TemplatesProvider,\n TestEmailOptions,\n TestEmailProvider,\n VersionHistoryOptions,\n VersionHistoryProvider,\n} from \"@templatical/types\";\n\nimport type { CloudTranslations } from \"../i18n\";\nimport { preRenderCustomBlocks } from \"../utils/preRenderCustomBlocks\";\nimport { logger } from \"../utils/logger\";\nimport type { UseEditorCoreReturn } from \"../composables/useEditorCore\";\nimport {\n AI_CONFIG_KEY,\n AUTH_MANAGER_KEY,\n CLOUD_TRANSLATIONS_KEY,\n SCORING_KEY,\n} from \"../keys\";\n\nimport { useCloudFeatureFlags } from \"./composables/useCloudFeatureFlags\";\nimport { useCloudPanelState } from \"./composables/useCloudPanelState\";\nimport {\n useCloudSaveGate,\n type UseCloudSaveGateReturn,\n} from \"./composables/useCloudSaveGate\";\nimport {\n useCollabUndoWarning,\n type UseCollabUndoWarningReturn,\n} from \"./composables/useCollabUndoWarning\";\nimport { collectSavedBlockIds } from \"./collectSavedBlockIds\";\nimport type {\n CloudAttachContext,\n CloudAttachment,\n CloudCollaborationInstance,\n CloudReady,\n CloudReadyContext,\n CloudRuntime,\n} from \"./runtime\";\nimport type { TemplaticalCloudEditorConfig } from \"./cloudConfig\";\n\nexport interface BootstrapCloudOptions {\n config: TemplaticalCloudEditorConfig;\n cloudTranslations: CloudTranslations;\n}\n\n/**\n * What {@link bootstrapCloud} hands back: a set of ordinary `init()` config\n * values, plus the runtime for the few things that must run inside the editor\n * component's `setup()`.\n */\nexport interface CloudBootstrap {\n runtime: CloudRuntime;\n providers: {\n templates: TemplatesProvider;\n render: RenderProvider;\n versionHistory: VersionHistoryProvider;\n /** Absent when `savedBlocks: false` — the feature is then off entirely. */\n savedBlocks?: SavedBlocksProvider;\n testEmail: TestEmailProvider;\n comments: CommentsProvider;\n /** Absent when `media: false` — Browse is then off unless `onRequestMedia` is set. */\n media?: MediaProvider;\n };\n /**\n * Who is editing, from the JWT — `init({ user })`'s value for a Cloud session.\n *\n * `undefined` when the project's token carries no `user` claim, which makes\n * comments unavailable rather than anonymous. Not a key `initCloud()` accepts:\n * Cloud's comment writes are signed against this same claim, so a\n * consumer-supplied identity would only be able to disagree with it.\n */\n user?: EditorUser;\n}\n\n/**\n * Pick only the event members off a consumer-supplied `templates` value.\n *\n * A whitelist, never a spread. The config type says `TemplatesOptions`, but a\n * JavaScript consumer is unchecked, so the object can carry anything —\n * `load`/`create`/`save` are already shadowed by the literal defining them\n * after the spread, and this drops everything else so the guarantee never\n * depends on that ordering surviving a later edit.\n *\n * Each member is also type-checked, not merely truthiness-checked: a value\n * like `onSaved: \"yes\"` would otherwise reach core and throw at save time,\n * inside a call the consumer never made.\n */\nfunction eventsOf(value: TemplatesOptions | undefined): TemplatesOptions {\n if (!value) return {};\n const { onSaved, onCreated, onLoaded } = value;\n return {\n ...(typeof onSaved === \"function\" ? { onSaved } : {}),\n ...(typeof onCreated === \"function\" ? { onCreated } : {}),\n ...(typeof onLoaded === \"function\" ? { onLoaded } : {}),\n };\n}\n\n/**\n * Fails to compile when `TemplatesOptions` gains a member outside the six\n * named below. Cloud picks that object apart by name in two places —\n * `eventsOf` above and `initCloud`'s merge in `index.ts` — so a seventh\n * member forces a **deliberate decision** about which of the two forwards\n * it. The check cannot make that decision itself: adding the new name to the\n * `Exclude` list below satisfies the compiler on its own and forwards\n * nothing by itself, and it has no way to tell an event (belongs in\n * `eventsOf`) from config (belongs in the `index.ts` merge).\n *\n * The type alone checks nothing: TypeScript never evaluates an alias nothing\n * reads, so a `never` result would sit there silently. The assignment below\n * is what forces the check — it fails to compile the moment a member falls\n * outside the six named here.\n */\ntype _TemplatesOptionsForwarded =\n Exclude<\n keyof TemplatesOptions,\n | \"autoSave\"\n | \"unsavedChangesGuard\"\n | \"nameField\"\n | \"onSaved\"\n | \"onCreated\"\n | \"onLoaded\"\n > extends never\n ? true\n : never;\nconst _templatesOptionsForwarded: _TemplatesOptionsForwarded = true;\n\n/**\n * Pick only the event members off a consumer-supplied `comments` value.\n *\n * A whitelist, never a spread. The config type says `CommentsOptions`, but a\n * JavaScript consumer is unchecked, so the object can carry anything —\n * `list`/`create`/`update`/`delete`/`setResolved` are already shadowed by the\n * spread of Cloud's own adapter that follows this in the provider literal, and\n * this drops everything else so the guarantee never depends on that ordering\n * surviving a later edit.\n *\n * Each member is also type-checked, not merely truthiness-checked: a value\n * like `onCreated: \"yes\"` would otherwise reach core and throw at\n * comment-creation time, inside a call the consumer never made.\n */\nfunction commentEventsOf(\n value: false | CommentsOptions | undefined,\n): CommentsOptions {\n if (!value) return {};\n const { onCreated, onUpdated, onDeleted, onResolved, onUnresolved } = value;\n return {\n ...(typeof onCreated === \"function\" ? { onCreated } : {}),\n ...(typeof onUpdated === \"function\" ? { onUpdated } : {}),\n ...(typeof onDeleted === \"function\" ? { onDeleted } : {}),\n ...(typeof onResolved === \"function\" ? { onResolved } : {}),\n ...(typeof onUnresolved === \"function\" ? { onUnresolved } : {}),\n };\n}\n\n/**\n * Fails to compile when `CommentsOptions` gains a member outside the five\n * named below. Every member of `CommentsOptions` is an event, and\n * `commentEventsOf` above is the only place that forwards them — unlike\n * `TemplatesOptions`, there is no second merge in `index.ts`, since comments\n * carries no config booleans for that merge to combine. A sixth member has\n * exactly one place to be added: this list and `commentEventsOf`'s destructure,\n * together.\n *\n * The type alone checks nothing: TypeScript never evaluates an alias nothing\n * reads, so a `never` result would sit there silently. The assignment below is\n * what forces the check — it fails to compile the moment a member falls\n * outside the five named here.\n */\ntype _CommentsOptionsForwarded =\n Exclude<\n keyof CommentsOptions,\n \"onCreated\" | \"onUpdated\" | \"onDeleted\" | \"onResolved\" | \"onUnresolved\"\n > extends never\n ? true\n : never;\nconst _commentsOptionsForwarded: _CommentsOptionsForwarded = true;\n\n/**\n * Pick only the event members off a consumer-supplied `savedBlocks` value.\n *\n * A whitelist, never a spread. `savedBlocks` accepts a boolean, an\n * events-only `SavedBlocksOptions`, or a full `SavedBlocksProvider` — this is\n * only ever called on the first two, since a full provider replaces Cloud's\n * adapter outright and is used as-is (see `consumerSavedBlocks` below). A\n * non-object value (`true`, `undefined`, `false`) carries no keys to pick, so\n * it returns `{}` rather than throwing on the destructure.\n *\n * Each member is also type-checked, not merely truthiness-checked: a value\n * like `onCreated: \"yes\"` would otherwise reach core and throw at\n * saved-block-creation time, inside a call the consumer never made.\n */\nfunction savedBlocksEventsOf(\n value: TemplaticalCloudEditorConfig[\"savedBlocks\"],\n): SavedBlocksOptions {\n if (typeof value !== \"object\" || value === null) return {};\n const { onCreated, onUpdated, onDeleted } = value;\n return {\n ...(typeof onCreated === \"function\" ? { onCreated } : {}),\n ...(typeof onUpdated === \"function\" ? { onUpdated } : {}),\n ...(typeof onDeleted === \"function\" ? { onDeleted } : {}),\n };\n}\n\n/**\n * Fails to compile when `SavedBlocksOptions` gains a member outside the three\n * named below. `savedBlocksEventsOf` above is the only place that forwards\n * them, so a fourth member has exactly one place to be added: this list and\n * that function's destructure, together.\n *\n * The type alone checks nothing: TypeScript never evaluates an alias nothing\n * reads, so a `never` result would sit there silently. The assignment below\n * is what forces the check — it fails to compile the moment a member falls\n * outside the three named here.\n */\ntype _SavedBlocksOptionsForwarded =\n Exclude<\n keyof SavedBlocksOptions,\n \"onCreated\" | \"onUpdated\" | \"onDeleted\"\n > extends never\n ? true\n : never;\nconst _savedBlocksOptionsForwarded: _SavedBlocksOptionsForwarded = true;\n\n/**\n * Pick `onSent` and `defaultRecipient` off a consumer-supplied `testEmail`\n * value.\n *\n * A whitelist, like `savedBlocksEventsOf` — but note what it does NOT pick:\n * `includeMjml` and `allowedRecipients` stay Cloud's own whenever Cloud's\n * sender is in play, so they are never read here even though\n * `TestEmailOptions` declares them (see `_TestEmailOptionsForwarded` below).\n * `defaultRecipient` has no such conflict — Cloud's provider never defines\n * it, and `useTestEmailFeature` already discards a value outside\n * `allowedRecipients`, so a consumer's choice can only ever pre-fill an\n * address already on Cloud's own signed list, never smuggle in one outside\n * it. `onSent` is a plain notification with no conflict either, so both\n * cross over.\n *\n * The result is assigned ONTO Cloud's own provider with `Object.assign`\n * (never spread together with it) at the call site — that provider's\n * `allowedRecipients` is a live getter over the JWT-derived allowlist, and a\n * spread reads every own property through `[[Get]]`, freezing the getter's\n * current value into a plain property on the new object. `Object.assign`\n * with the cloud provider as the *target* only ever writes the keys this\n * function returns, so the getter is never read and never disturbed.\n */\nfunction testEmailEventsOf(\n value: TemplaticalCloudEditorConfig[\"testEmail\"],\n): Pick<TestEmailOptions, \"onSent\" | \"defaultRecipient\"> {\n if (typeof value !== \"object\" || value === null) return {};\n const { onSent, defaultRecipient } = value;\n return {\n ...(typeof onSent === \"function\" ? { onSent } : {}),\n ...(typeof defaultRecipient === \"string\" ? { defaultRecipient } : {}),\n };\n}\n\n/**\n * Fails to compile when `TestEmailOptions` gains a member outside the four\n * named below. `onSent` and `defaultRecipient` are forwarded by\n * `testEmailEventsOf` above; `includeMjml` and `allowedRecipients` stay\n * Cloud's own whenever Cloud's sender is in play (Cloud renders server-side\n * rather than from a client MJML pass, and the allowlist is the signed one\n * from the project's JWT, not a client-supplied one). A fifth member still\n * forces a deliberate decision the same way a fourth `SavedBlocksOptions`\n * member does: whether it joins the forwarded pair or stays excluded for the\n * same reason as the other two.\n *\n * The type alone checks nothing: TypeScript never evaluates an alias nothing\n * reads, so a `never` result would sit there silently. The assignment below\n * is what forces the check — it fails to compile the moment a member falls\n * outside the four named here.\n */\ntype _TestEmailOptionsForwarded =\n Exclude<\n keyof TestEmailOptions,\n \"includeMjml\" | \"allowedRecipients\" | \"defaultRecipient\" | \"onSent\"\n > extends never\n ? true\n : never;\nconst _testEmailOptionsForwarded: _TestEmailOptionsForwarded = true;\n\n/**\n * Pick only the event members off a consumer-supplied `media` value.\n *\n * A whitelist, never a spread. `media` accepts `false`, an events-only\n * {@link MediaOptions}, or a full {@link MediaProvider} — this is only ever\n * called on the first two, since a full provider replaces Cloud's adapter\n * outright and is used as-is (see `consumerMedia` below). A non-object\n * value (`undefined`, `false`) carries no keys to pick, so it returns `{}`\n * rather than throwing on the destructure.\n *\n * What it does NOT pick: `maxFileSize` and `mimeTypes` stay Cloud's own\n * whenever Cloud's store is in play (Cloud's plan owns those limits), so\n * they are never read here even though `MediaOptions` declares them (see\n * `_MediaOptionsForwarded` below).\n *\n * The result is assigned ONTO Cloud's own provider with `Object.assign`\n * (never spread together with it) at the call site — that provider's\n * `storage` / `maxFileSize` / `mimeTypes` read plan config live, and a\n * spread reads every own property through `[[Get]]`, freezing each\n * getter's current value into a plain property on the new object.\n * `Object.assign` with the cloud provider as the *target* only ever\n * writes the keys this function returns, so those getters are never\n * read and never disturbed.\n */\nfunction mediaEventsOf(\n value: TemplaticalCloudEditorConfig[\"media\"],\n): Pick<MediaOptions, \"onCreated\" | \"onUpdated\" | \"onDeleted\"> {\n if (typeof value !== \"object\" || value === null) return {};\n const { onCreated, onUpdated, onDeleted } = value;\n return {\n ...(typeof onCreated === \"function\" ? { onCreated } : {}),\n ...(typeof onUpdated === \"function\" ? { onUpdated } : {}),\n ...(typeof onDeleted === \"function\" ? { onDeleted } : {}),\n };\n}\n\n/**\n * Fails to compile when `MediaOptions` gains a member outside the five\n * named below. `onCreated` / `onUpdated` / `onDeleted` are forwarded by\n * `mediaEventsOf` above; `maxFileSize` and `mimeTypes` stay Cloud's own\n * whenever Cloud's store is in play (Cloud's plan owns those limits). A\n * sixth member still forces a deliberate decision the same way a fifth\n * `TestEmailOptions` member does: whether it joins the forwarded trio or\n * stays excluded for the same reason as the other two.\n *\n * The type alone checks nothing: TypeScript never evaluates an alias\n * nothing reads, so a `never` result would sit there silently. The\n * assignment below is what forces the check — it fails to compile the\n * moment a member falls outside the five named here.\n */\ntype _MediaOptionsForwarded =\n Exclude<\n keyof MediaOptions,\n \"onCreated\" | \"onUpdated\" | \"onDeleted\" | \"maxFileSize\" | \"mimeTypes\"\n > extends never\n ? true\n : never;\nconst _mediaOptionsForwarded: _MediaOptionsForwarded = true;\n\n/**\n * Pick only the event members off a consumer-supplied `versionHistory`\n * value.\n *\n * A whitelist, never a spread — the config type says `VersionHistoryOptions`,\n * but a JavaScript consumer is unchecked, so the object can carry anything.\n * `versionHistory` never accepts a full provider (see the rejected-keys block\n * below), so unlike `savedBlocksEventsOf` / `testEmailEventsOf` this is\n * always called on the whole config value.\n */\nfunction versionHistoryEventsOf(\n value: VersionHistoryOptions | undefined,\n): VersionHistoryOptions {\n if (!value) return {};\n const { onCreated, onRestored } = value;\n return {\n ...(typeof onCreated === \"function\" ? { onCreated } : {}),\n ...(typeof onRestored === \"function\" ? { onRestored } : {}),\n };\n}\n\n/**\n * Fails to compile when `VersionHistoryOptions` gains a member outside the\n * two named below. `versionHistoryEventsOf` above is the only place that\n * forwards them, so a third member has exactly one place to be added: this\n * list and that function's destructure, together.\n *\n * The type alone checks nothing: TypeScript never evaluates an alias nothing\n * reads, so a `never` result would sit there silently. The assignment below\n * is what forces the check — it fails to compile the moment a member falls\n * outside the two named here.\n */\ntype _VersionHistoryOptionsForwarded =\n Exclude<keyof VersionHistoryOptions, \"onCreated\" | \"onRestored\"> extends never\n ? true\n : never;\nconst _versionHistoryOptionsForwarded: _VersionHistoryOptionsForwarded = true;\n\n/**\n * A comma list with a final \"and\" — \"a\"; \"a and b\"; \"a, b and c\". The\n * ignored-methods warnings below each name however many storage methods a\n * consumer actually passed, and `Array.join(\" and \")` only reads correctly\n * for exactly two.\n */\nfunction joinWithAnd(items: string[]): string {\n if (items.length < 2) return items.join(\"\");\n if (items.length === 2) return items.join(\" and \");\n return `${items.slice(0, -1).join(\", \")} and ${items[items.length - 1]}`;\n}\n\n/**\n * Cloud's bootstrap and adapter wiring, in one place, run **before the editor\n * mounts**.\n *\n * This replaces `useCloudInitialization`, which owned a second `useEditor`, a\n * second `useEditorCore` and a post-mount async dance with its own loading and\n * error states. The dance is gone: auth, the health check and the plan fetch all\n * resolve here, and a failure rejects `initCloud()` rather than mounting an\n * editor that cannot work. What is left is adapter construction.\n *\n * Two things deliberately stay Cloud-only:\n *\n * - **Entitlements.** In an all-BYO world there are no plans, so `planConfig` is\n * Cloud's commercial layer above the provider set rather than a provider\n * itself. It is down to five booleans, each metering a resource Cloud buys.\n * - **Mid-session failure.** `init()` cannot fail after it mounts and should not\n * grow the ability, so the error overlay lives on this side of the seam.\n */\nexport async function bootstrapCloud(\n options: BootstrapCloudOptions,\n): Promise<CloudBootstrap> {\n const { config, cloudTranslations } = options;\n\n // --- Late-bound editor access ---------------------------------------------\n //\n // The providers below are built before there is an editor to read. Each\n // reaches it through these holders, filled by `attach()` / `ready()` — which is\n // what lets them be handed to `init()` as ordinary config keys.\n let editorRef: UseEditorReturn | null = null;\n let coreRef: UseEditorCoreReturn | null = null;\n let websocketRef: ReturnType<typeof useWebSocket> | null = null;\n let collaboration: CloudCollaborationInstance | null = null;\n let collabWarning: UseCollabUndoWarningReturn | null = null;\n let saveGate: UseCloudSaveGateReturn | null = null;\n\n const sessionError = ref<Error | null>(null);\n const lockedBlocks = ref<Map<string, unknown>>(new Map());\n /**\n * Cloud's presence channel, forward-declared for the comments adapter's\n * `subscribe`. `useWebSocket` can only be built in `attach()` (it needs the\n * editor), while the provider is handed to `init()` before that — so the adapter\n * watches this and rebinds when the real channel lands.\n */\n const commentChannel = ref<RealtimeChannel | null>(null);\n\n function requireEditor(): UseEditorReturn {\n if (!editorRef) {\n throw new Error(\"[Templatical] The Cloud editor is not mounted yet.\");\n }\n return editorRef;\n }\n\n function getTemplateId(): string | null {\n return editorRef?.state.template?.id ?? null;\n }\n\n // --- Auth + plan ----------------------------------------------------------\n\n const authManager = new AuthManager({\n ...config.auth,\n // Only a *fatal* refusal raises the overlay. A 4xx means the endpoint\n // rejected the credentials, so every subsequent request fails the same way\n // and the session really is over. A network blip or a 5xx resolves on its\n // own, and blanking a mounted editor over one interrupts someone mid-edit on\n // a template that is probably unsaved — a worse outcome than the blip.\n //\n // The consumer hears about both: `onError` fires either way, and\n // `AuthManager` re-throws regardless, so a caller that cannot proceed still\n // finds out.\n onError: (error: Error) => {\n if (isFatalAuthError(error)) {\n sessionError.value = error;\n }\n config.onError?.(error);\n },\n });\n\n const planConfigInstance = usePlanConfig({\n authManager,\n onError: config.onError,\n });\n\n await authManager.initialize();\n\n const healthResult = await performHealthCheck({ authManager });\n if (!healthResult.api.ok) {\n throw new Error(\"Health check failed: API is not reachable\");\n }\n if (!healthResult.auth.ok) {\n throw new Error(\n `Health check failed: authentication error${healthResult.auth.error ? ` - ${healthResult.auth.error}` : \"\"}`,\n );\n }\n if (!healthResult.websocket.ok) {\n logger.warn(\n \"WebSocket health check failed:\",\n healthResult.websocket.error ?? \"unknown error\",\n \"-- real-time features will be disabled.\",\n );\n }\n\n await planConfigInstance.fetchConfig();\n\n // The bootstrap itself is what the overlay would have covered, and it just\n // succeeded — so anything `onError` recorded on the way here is stale.\n sessionError.value = null;\n\n // --- Rejected consumer keys ----------------------------------------------\n //\n // `render` is not declared on `TemplaticalCloudEditorConfig`, so it only\n // fires for JavaScript consumers — warned rather than silently dropped,\n // because the symptom (a store that stays empty while Cloud's fills up)\n // reads as a broken provider rather than a rejected one. `templates`,\n // `comments` and `versionHistory` ARE declared, but typed narrowly as\n // `TemplatesOptions`, `CommentsOptions` and `VersionHistoryOptions` — a\n // JavaScript consumer can still hand over a full provider, so its storage\n // methods are named and ignored the same way, while any events they carry\n // reach `eventsOf` / `commentEventsOf` / `versionHistoryEventsOf` below\n // regardless of this check.\n const consumerTemplates = (config as { templates?: Record<string, unknown> })\n .templates;\n // Presence, not `typeof === \"function\"` — see the matching comment on\n // `ignoredCommentMethods` below: a stated-but-ignored decision like\n // `save: false` is as much a signal as a stray function override.\n const ignoredTemplateMethods = [\"load\", \"create\", \"save\"].filter(\n (key) => typeof consumerTemplates?.[key] !== \"undefined\",\n );\n if (ignoredTemplateMethods.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredTemplateMethods.map((key) => `templates.${key}`),\n )} — the template id is the join key for collaboration, ` +\n \"version history, comments, AI rewrite, scoring and the server-side \" +\n \"export, so an id Cloud never issued would degrade all six. Your \" +\n \"event handlers were kept. Use init() to bring your own storage.\",\n );\n }\n const consumerVersionHistory = (\n config as { versionHistory?: Record<string, unknown> }\n ).versionHistory;\n // Presence, not `typeof === \"function\"`: a stated-but-ignored decision like\n // `restore: false` is as much a signal as a stray function override, and\n // both must be named here or the consumer never learns it was dropped.\n const ignoredVersionHistoryMethods = [\n \"list\",\n \"get\",\n \"create\",\n \"restore\",\n ].filter((key) => typeof consumerVersionHistory?.[key] !== \"undefined\");\n if (ignoredVersionHistoryMethods.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredVersionHistoryMethods.map((key) => `versionHistory.${key}`),\n )} — a version is keyed to a template id Cloud issued, so Cloud owns ` +\n \"its history. Your event handlers were kept. Use init() to bring \" +\n \"your own storage.\",\n );\n }\n const consumerComments = (config as { comments?: Record<string, unknown> })\n .comments;\n // Presence, not `typeof === \"function\"`: a stated-but-ignored decision like\n // `create: false` is as much a signal as a stray function override, and both\n // must be named here or the consumer never learns it was dropped.\n const ignoredCommentMethods = [\n \"list\",\n \"create\",\n \"update\",\n \"delete\",\n \"setResolved\",\n \"subscribe\",\n ].filter((key) => typeof consumerComments?.[key] !== \"undefined\");\n if (ignoredCommentMethods.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredCommentMethods.map((key) => `comments.${key}`),\n )} — a comment is keyed to a template id Cloud issued, and its ` +\n \"author is signed by the auth token, so Cloud owns the conversation. \" +\n \"Your event handlers were kept. Use init() to bring your own storage.\",\n );\n }\n // Rejected for a different reason than the three above: not the join key, but\n // because Cloud renders on its own for test email, scheduled sends and API\n // exports (`createCloudTestEmailProvider` calls `exportHtml` directly). A\n // consumer provider would have changed `toMjml()` / `toHtml()` and nothing\n // else, so what the user previewed and exported was not what Cloud delivered —\n // and Cloud's output is a deliberate superset (countdown GIFs, composited video\n // thumbnails), so the consumer's was also worse for those blocks.\n if ((config as { render?: unknown }).render) {\n logger.warn(\n \"initCloud does not accept a `render` provider — Cloud renders \" +\n \"server-side for test email, sends and exports, so a supplied renderer \" +\n \"would change only what you preview and export, never what Cloud \" +\n \"delivers. The supplied provider is ignored. For your own MJML, call \" +\n \"renderToMjml(editor.getContent()) from @templatical/renderer.\",\n );\n }\n\n // --- Providers ------------------------------------------------------------\n\n function connectRealtime(template: Template): void {\n const websocketConfig = planConfigInstance.config.value?.websocket;\n if (!websocketRef || !websocketConfig) return;\n websocketRef.connect(template.id, resolveWebSocketConfig(websocketConfig));\n }\n\n const baseTemplates = createCloudTemplatesProvider(authManager);\n const baseCreate = baseTemplates.create;\n const baseSave = baseTemplates.save;\n\n /**\n * Cloud's templates adapter plus the choreography the template id anchors:\n * the websocket joins the template's presence channel. The consumer's\n * `onSaved` / `onCreated` / `onLoaded` ride along via `eventsOf` — core calls\n * them once the editor has settled, so the adapter itself never invokes them.\n *\n * That choreography belongs in the adapter rather than in an editor-side\n * lifecycle composable, because it *is* adapter business — Cloud is what keys\n * realtime off the id it issued. `useCloudLifecycle` existed only to hold it.\n *\n * `save` pre-renders custom blocks, which is why it is wrapped too.\n */\n const templates: TemplatesProvider = {\n ...eventsOf(config.templates),\n load: async (id: string) => {\n const template = await baseTemplates.load(id);\n connectRealtime(template);\n return template;\n },\n create:\n typeof baseCreate === \"function\"\n ? async (input: { name?: string; content: TemplateContent }) => {\n const template = await baseCreate(input);\n connectRealtime(template);\n return template;\n }\n : false,\n save:\n typeof baseSave === \"function\"\n ? async (id: string, patch: TemplatePatch) => {\n if (coreRef && editorRef) {\n await preRenderCustomBlocks(\n editorRef.content.value,\n coreRef.registry,\n );\n }\n return baseSave(id, patch);\n }\n : false,\n };\n\n // Always Cloud's — see the rejected-keys block above. Cloud renders\n // independently for delivery, so a second renderer could only disagree with it.\n const render: RenderProvider = createCloudRenderProvider({\n authManager,\n getTemplateId,\n // Plain `editor.save()`: the templates adapter below already pre-renders\n // custom blocks on every save, which is exactly what the render endpoint\n // needs, since it exports from the stored copy rather than from\n // `payload.content`.\n save: () => requireEditor().save(),\n });\n\n // The consumer's `onCreated` / `onRestored` ride along via\n // `versionHistoryEventsOf` — core calls them once the editor has settled, so\n // the adapter itself never invokes them. `versionHistory` never accepts a\n // full provider (see the rejected-keys block above), so this always merges\n // onto Cloud's own adapter, never onto a consumer's.\n const versionHistory: VersionHistoryProvider = {\n ...versionHistoryEventsOf(config.versionHistory),\n ...createCloudVersionHistoryProvider(authManager),\n };\n\n // Realtime is the adapter's own business: `subscribe` binds the presence channel\n // above, so `useCommentListener` in `@templatical/core` never learns that Pusher\n // exists and a BYO provider with an SSE stream slots into the same seam. The\n // consumer's `onCreated` / `onUpdated` / `onDeleted` / `onResolved` /\n // `onUnresolved` ride along via `commentEventsOf` — core calls them once the\n // editor has settled, so the adapter itself never invokes them.\n const comments: CommentsProvider = {\n ...commentEventsOf(config.comments),\n ...createCloudCommentsProvider({\n authManager,\n channel: commentChannel,\n getSocketId: () => websocketRef?.getSocketId() ?? null,\n }),\n };\n\n // From the JWT, not from config. Cloud signs comment writes against this same\n // claim, so a consumer-supplied identity could only disagree with it — and a\n // token without the claim leaves comments unavailable rather than anonymous.\n const cloudUser: EditorUser | undefined = authManager.userConfig\n ? { id: authManager.userConfig.id, name: authManager.userConfig.name }\n : undefined;\n\n // A consumer may pass their own store instead of a boolean, in which case it\n // replaces Cloud's outright — discriminated on `list`, the one required\n // method every full provider carries, never on `typeof === \"object\"`: an\n // events-only `SavedBlocksOptions` (`{ onCreated }`) is an object too, and\n // reading it as the provider would leave `list` undefined and crash the\n // library on first browse. That path is deliberately NOT plan-gated:\n // `savedModules` licenses Cloud's *storage*, and someone else's backend\n // isn't Cloud's to sell — `isSavedBlocksAvailable` below keys off this same\n // discriminated value for that reason.\n const consumerSavedBlocks =\n typeof (config.savedBlocks as SavedBlocksProvider | undefined)?.list ===\n \"function\"\n ? (config.savedBlocks as SavedBlocksProvider)\n : null;\n // A malformed provider — e.g. `create` / `update` / `delete` with no\n // working `list` — fails the discriminator above and falls through to\n // Cloud's own store, so those methods are silently unused rather than\n // replacing anything. Named here the same way `templates` / `comments` /\n // `versionHistory` name their own ignored methods, so the drop isn't a\n // console-free mystery.\n if (\n consumerSavedBlocks === null &&\n typeof config.savedBlocks === \"object\" &&\n config.savedBlocks !== null\n ) {\n const suppliedSavedBlocks = config.savedBlocks as Record<string, unknown>;\n const ignoredSavedBlocksMethods = [\n \"list\",\n \"create\",\n \"update\",\n \"delete\",\n ].filter((key) => typeof suppliedSavedBlocks[key] !== \"undefined\");\n if (ignoredSavedBlocksMethods.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredSavedBlocksMethods.map((key) => `savedBlocks.${key}`),\n )} — a provider needs a working list to replace Cloud's store, and ` +\n \"this value has none, so it configures Cloud's own store instead. \" +\n \"Your event handlers were kept. Use init() to bring your own storage.\",\n );\n }\n }\n // The consumer's `onCreated` / `onUpdated` / `onDeleted` ride along via\n // `savedBlocksEventsOf` when Cloud's own adapter is used — core calls them\n // once the editor has settled, so the adapter itself never invokes them. A\n // full consumer provider carries its own events already and is used as-is,\n // so this merge only ever runs on Cloud's side of the `??`.\n const savedBlocks: SavedBlocksProvider | undefined =\n config.savedBlocks === false\n ? undefined\n : (consumerSavedBlocks ?? {\n ...savedBlocksEventsOf(config.savedBlocks),\n ...createCloudSavedBlocksProvider(authManager),\n });\n\n // Cloud's test-email *config* — plan enablement plus the signed allowlist, both\n // carried by the JWT `authManager.initialize()` already resolved above. Hence\n // the constant `isAuthReady`: there is no window where it is false.\n const testEmailConfig = useTestEmail({\n authManager,\n isAuthReady: ref(true),\n });\n\n // Cloud's own send path renders through the export endpoint directly: the\n // adapter derives HTML from the *saved* template and applies\n // `onBeforeTestEmail` to it, which is a narrower job than the render contract.\n const exporter = useExport({ authManager });\n\n // Discriminated on `send`, the one required method every full provider\n // carries — never on raw truthiness, which an events-only `TestEmailOptions`\n // (`{ onSent }`) also satisfies and would leave `send` undefined, crashing\n // the dialog on send. `isTestEmailAvailable` below keys off this same\n // discriminated value so an events-only value stays plan-gated rather than\n // reading as \"consumer's own sender\".\n const consumerTestEmail =\n typeof (config.testEmail as TestEmailProvider | undefined)?.send ===\n \"function\"\n ? (config.testEmail as TestEmailProvider)\n : null;\n // `send` reaching here means it failed the discriminator above — e.g.\n // `send: null` — so it is named the same way a malformed `savedBlocks`\n // value's storage methods are. `includeMjml` / `allowedRecipients` are\n // named for a different reason: they are not part of this key's options\n // arm (see `cloudConfig.ts`), but TypeScript does not actually refuse\n // them — both are valid members of the sibling `TestEmailProvider` arm, so\n // the union's excess-property check passes them through regardless of\n // which arm the caller intended. All three log a warning naming them\n // rather than dropping with no console trace.\n if (\n consumerTestEmail === null &&\n typeof config.testEmail === \"object\" &&\n config.testEmail !== null\n ) {\n const suppliedTestEmail = config.testEmail as Record<string, unknown>;\n const ignoredTestEmailMembers = [\n \"send\",\n \"includeMjml\",\n \"allowedRecipients\",\n ].filter((key) => typeof suppliedTestEmail[key] !== \"undefined\");\n if (ignoredTestEmailMembers.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredTestEmailMembers.map((key) => `testEmail.${key}`),\n )} — a provider needs a working send to replace Cloud's sender ` +\n \"outright, and Cloud renders server-side with the signed \" +\n \"allowlist from your project's JWT either way, so none of these \" +\n \"apply here. Your event handlers were kept.\",\n );\n }\n }\n const testEmail: TestEmailProvider =\n consumerTestEmail ??\n // `Object.assign`, not a spread: `createCloudTestEmailProvider`'s\n // `allowedRecipients` is a live getter (see `testEmailEventsOf`'s doc\n // comment for why a spread would freeze it). The cloud provider is the\n // assignment *target* here, so its getter is never read — only the keys\n // `testEmailEventsOf` returns are ever written onto it.\n //\n // Asymmetric with `savedBlocks` / `versionHistory` on purpose, not by\n // drift: those two spread Cloud's provider LAST, so even a whitelist that\n // let something unexpected through could never displace a Cloud method —\n // two lines of defence. Here `testEmailEventsOf`'s return type is the\n // only barrier, because Cloud is the assignment target rather than a\n // second spread source. Copying the sibling shape (`{ ...events,\n // ...createCloudTestEmailProvider(...) }`) would reintroduce the exact\n // getter-freezing bug this construction exists to avoid.\n Object.assign(\n createCloudTestEmailProvider({\n authManager,\n getTemplateId,\n save: () => requireEditor().save(),\n exportHtml: (templateId: string) =>\n exporter.exportHtml(templateId, resolveExportFonts(config.fonts)),\n allowedEmails: testEmailConfig.allowedEmails,\n getSignature: testEmailConfig.getSignature,\n onBeforeTestEmail: config.onBeforeTestEmail,\n }),\n testEmailEventsOf(config.testEmail),\n );\n\n // A consumer may pass their own store instead of omitting the key or\n // passing `false`, in which case it replaces Cloud's outright —\n // discriminated on `list`, the one required method every full provider\n // carries, never on `typeof === \"object\"`: an events-only `MediaOptions`\n // (`{ onCreated }`) is an object too, and reading it as the provider\n // would leave `list` undefined and crash the library on first browse.\n // That path is deliberately NOT plan-gated: there is no media plan\n // feature. The plan licenses Cloud's *storage*, and someone else's\n // backend isn't Cloud's to sell.\n const consumerMedia =\n typeof (config.media as MediaProvider | undefined)?.list === \"function\"\n ? (config.media as MediaProvider)\n : null;\n // A malformed provider — e.g. `create` / `update` / `delete` with no\n // working `list` — fails the discriminator above and falls through to\n // Cloud's own store, so those methods are silently unused rather than\n // replacing anything. Named here the same way `savedBlocks` names its\n // own ignored methods, so the drop isn't a console-free mystery.\n // `maxFileSize` / `mimeTypes` are named for a different reason: they\n // are valid `MediaOptions` members but Cloud's plan owns those limits\n // whenever Cloud's store is in play.\n if (\n consumerMedia === null &&\n typeof config.media === \"object\" &&\n config.media !== null\n ) {\n const suppliedMedia = config.media as Record<string, unknown>;\n const ignoredMediaMethods = [\"list\", \"create\", \"update\", \"delete\"].filter(\n (key) => typeof suppliedMedia[key] !== \"undefined\",\n );\n if (ignoredMediaMethods.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredMediaMethods.map((key) => `media.${key}`),\n )} — a provider needs a working list to replace Cloud's store, and ` +\n \"this value has none, so it configures Cloud's own store instead. \" +\n \"Your event handlers were kept. Use init() to bring your own storage.\",\n );\n }\n const ignoredMediaLimits = [\"maxFileSize\", \"mimeTypes\"].filter(\n (key) => typeof suppliedMedia[key] !== \"undefined\",\n );\n if (ignoredMediaLimits.length > 0) {\n logger.warn(\n `initCloud ignores ${joinWithAnd(\n ignoredMediaLimits.map((key) => `media.${key}`),\n )} — Cloud's plan owns those limits whenever Cloud's store is in ` +\n \"play. Your event handlers were kept.\",\n );\n }\n }\n const media: MediaProvider | undefined =\n config.media === false\n ? undefined\n : (consumerMedia ??\n // `Object.assign`, not a spread: `createCloudMediaProvider`'s\n // `storage` / `maxFileSize` / `mimeTypes` read plan config live\n // (see `mediaEventsOf`'s doc comment for why a spread would freeze\n // the getters). The cloud provider is the assignment *target*\n // here, so its getters are never read — only the keys\n // `mediaEventsOf` returns are ever written onto it.\n Object.assign(\n createCloudMediaProvider(\n authManager,\n () => planConfigInstance.config.value,\n ),\n mediaEventsOf(config.media),\n ));\n\n // --- Setup-time wiring ----------------------------------------------------\n\n const aiConfig = useAiConfig(config.ai);\n const scoringInstance = useTemplateScoring({ authManager, getTemplateId });\n\n const featureFlags = useCloudFeatureFlags({\n planConfigInstance,\n aiConfig,\n getTemplateId,\n });\n\n const isCollaborationEnabled = computed(\n () =>\n !!config.collaboration?.enabled &&\n planConfigInstance.hasFeature(\"collaboration\"),\n );\n\n function attach(context: CloudAttachContext): CloudAttachment {\n const editor = context.editor;\n editorRef = editor;\n\n provide(CLOUD_TRANSLATIONS_KEY, cloudTranslations);\n provide(AUTH_MANAGER_KEY, authManager);\n provide(AI_CONFIG_KEY, aiConfig);\n provide(SCORING_KEY, scoringInstance);\n\n const websocket = useWebSocket({ authManager, onError: config.onError });\n websocketRef = websocket;\n\n // Feeds the comments adapter's `subscribe`, which was handed to `init()` before\n // this socket existed.\n watch(\n websocket.channel,\n (channel) => {\n commentChannel.value = channel;\n },\n { immediate: true },\n );\n\n if (config.mcp?.enabled) {\n useMcpListener({\n editor,\n channel: websocket.channel,\n onOperation: config.mcp.onOperation,\n });\n }\n\n // Order matters: the broadcast wrapper replaces the editor's mutators so they\n // publish to peers, and `useEditorCore`'s history interceptor then wraps them\n // again to push history entries. Reversing it would push history for remote\n // operations and drift the two peers apart.\n if (config.collaboration?.enabled) {\n collaboration = useCollaboration({\n authManager,\n editor,\n channel: websocket.channel,\n onError: config.onError,\n onCollaboratorJoined: config.collaboration.onCollaboratorJoined,\n onCollaboratorLeft: config.collaboration.onCollaboratorLeft,\n onBlockLocked: config.collaboration.onBlockLocked,\n onBlockUnlocked: config.collaboration.onBlockUnlocked,\n }) as CloudCollaborationInstance;\n\n watch(\n () => collaboration!.lockedBlocks.value,\n (next) => {\n lockedBlocks.value = next;\n },\n { immediate: true },\n );\n\n useCollaborationBroadcast(editor, collaboration);\n }\n\n const panelState = useCloudPanelState();\n\n return {\n websocket,\n panelState,\n featureFlags,\n collaboration,\n isCollaborationEnabled,\n };\n }\n\n function ready(context: CloudReadyContext): CloudReady {\n const { core, capabilities } = context;\n coreRef = core;\n\n saveGate = useCloudSaveGate({\n issues: core.templateLint ? core.templateLint.issues : ref([]),\n planConfig: planConfigInstance.config,\n });\n\n collabWarning = useCollabUndoWarning({\n isCollaborationEnabled,\n getCollaboratorCount: () =>\n collaboration?.collaborators.value.length ?? 0,\n canUndo: core.history.canUndo,\n });\n\n // Mutated in place rather than re-provided: `useEditorCore` already provided\n // this exact object, so components injecting it see Cloud's entries on their\n // first render. Same pattern `versionHistory` uses in `Editor.vue`.\n //\n // `capabilities.comments` is *not* set here any more: comments became a shared\n // provider-backed feature, so `Editor.vue` builds the capability from\n // `useCommentsFeature` exactly as it does for saved blocks and test email, and\n // Cloud contributes only the two predicates below.\n capabilities.plan = planConfigInstance;\n capabilities.ai = aiConfig;\n\n // Registered unconditionally. `custom_blocks` and `theme_customization` used\n // to gate these — entitlements on editor capability the free editor grants,\n // and the only two things `applyPlanGates` would ever have had to do.\n if (config.customBlocks?.length) {\n core.registerCustomBlocks(config.customBlocks);\n }\n if (config.theme) {\n core.themeOverrides.value = config.theme;\n }\n\n return {\n saveGate,\n collabWarning,\n sessionError,\n retry: async () => {\n try {\n await authManager.initialize();\n sessionError.value = null;\n } catch (error) {\n sessionError.value =\n error instanceof Error ? error : new Error(\"Authentication failed\");\n }\n },\n };\n }\n\n const runtime: CloudRuntime = {\n lockedBlocks,\n isSavedBlocksAvailable: consumerSavedBlocks\n ? () => true\n : () =>\n config.savedBlocks !== false &&\n planConfigInstance.hasFeature(\"savedModules\"),\n isTestEmailAvailable: consumerTestEmail\n ? // Their sender, their rules — the `testEmail` plan feature licenses\n // Cloud's sending, and \"the template must be saved\" is a constraint of\n // Cloud's server-side render, not of an arbitrary backend.\n () => true\n : // All three matter and none implies another: the plan may grant the\n // feature while the project's token omits a recipient config, and Cloud\n // renders from the *saved* template so there must be one.\n () =>\n testEmailConfig.isEnabled.value &&\n featureFlags.canSendTestEmail.value &&\n featureFlags.hasTemplateSaved.value,\n // Three conditions, none implying another: the consumer may switch the feature\n // off, the plan meters comment storage and its realtime fan-out, and Cloud\n // anchors a comment to the *saved* template. (Whether a `user` exists is the\n // shared feature's own gate, not Cloud's.)\n isCommentsAvailable: () =>\n config.comments !== false &&\n planConfigInstance.hasFeature(\"commenting\") &&\n featureFlags.hasTemplateSaved.value,\n // A comment is anchored server-side, so a block that exists only on the canvas\n // has nothing stored to show. This is the last thing the deleted cloud editor\n // core carried (`savedBlockIds`), and it was always a comments concern rather\n // than an editor one.\n isBlockSaved: (blockId: string) =>\n collectSavedBlockIds(editorRef?.state.template ?? null).has(blockId),\n getSaveGate: () => saveGate,\n isRemoteOperation: () =>\n collaboration?._isProcessingRemoteOperation() ?? false,\n onBeforeUndo: () => collabWarning?.showCollabUndoWarning(),\n attach,\n ready,\n destroy() {\n websocketRef?.disconnect();\n config.onUnmount?.();\n },\n };\n\n return {\n runtime,\n providers: {\n templates,\n render,\n versionHistory,\n ...(savedBlocks ? { savedBlocks } : {}),\n testEmail,\n comments,\n ...(media ? { media } : {}),\n },\n user: cloudUser,\n };\n}\n"],"mappings":"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"}
|